The official TypeScript SDK for HoodPrivate, the private financial layer of the agent economy: banking on Robinhood Chain for humans and their AI agents, private by default.
Transfer amounts stay encrypted end to end, settlement lands in under a second, and you hold your own keys the entire time. This SDK is a thin, fully typed wrapper over the HoodPrivate REST API. It manages accounts, moves confidential money, provisions agent accounts whose spending limits are enforced by the chain itself, and handles webhook delivery. From zero to your first confidential transfer in minutes.
Beta. The API surface is stable but evolving. Pin a version and read the changelog before upgrading.
npm install @hoodprivate/sdkRequires Node 18 or newer (for the global fetch and Web Crypto APIs). Works in the browser and edge runtimes too, though you should never ship a live API key to a browser.
One client, one call, one settled confidential transfer:
import { HoodPrivate } from "@hoodprivate/sdk";
const hc = new HoodPrivate({ apiKey: process.env.HOODPRIVATE_API_KEY! });
// Send a confidential transfer. The amount is encrypted on-chain; the
// response confirms settlement but never echoes the amount in plaintext.
const transfer = await hc.transfers.create({
to: "@vendor",
amount: "125.00",
asset: "USDG",
memo: "Invoice #4471",
});
console.log(transfer.status, transfer.txHash);Every request is authenticated with a single bearer API key generated from Dashboard → Developer → API Keys. Keys carry an environment prefix, and the client infers the environment from it, so there is no mode flag to misconfigure:
| Prefix | Environment | Behavior |
|---|---|---|
hc_live_ |
Mainnet (chain ID 4663) | Moves real USDG |
hc_test_ |
Testnet (chain ID 46630) | No real funds move |
const hc = new HoodPrivate({ apiKey: "hc_test_..." });
hc.environment; // "test"An API key can move funds. Store it in an environment variable or a secrets manager, never in source control, and rotate it immediately if exposed.
Privacy here is architecture, not policy: HoodPrivate's servers cannot decrypt your balances or transfer amounts even if they wanted to. That has two consequences for this SDK:
- Transfer and balance responses never contain a plaintext amount. Reading one requires client-side decryption with your account key.
accounts.balances()returns decrypted amounts only when you supply adecryptionProofgenerated client-side. Without it, you learn which assets hold a non-zero balance, but not how much.
const { balances } = await hc.accounts.balances({ decryptionProof });Give an agent its own account and a hard mandate. Spending limits are smart-account constraints enforced on-chain, not server-side rules, so an agent can never outspend its policy and a compromised server cannot bypass it:
const agent = await hc.agents.create({
name: "research-bot",
spendPolicy: {
dailyLimitUsdg: 500,
perTransactionLimitUsdg: 50,
allowedRecipients: ["api.market", "*.anthropic.com"],
assets: ["USDG"],
activeHours: "00:00-23:59",
hitlThresholdUsdg: 25, // transactions at or above this are held for approval
},
});
// Review and clear transactions held for human approval.
const { pending } = await hc.agents.listPendingTransactions();
for (const tx of pending) {
await hc.agents.approveTransaction(tx.transactionId);
}The agent runs autonomously. You stay the final authority.
Your systems hear about settlements the moment they happen. Subscribe to events, then verify the signature on every incoming delivery before trusting it. Pass the raw request body exactly as received, not a re-serialized object:
const webhook = await hc.webhooks.create({
url: "https://yourapp.com/hooks/hoodprivate",
events: ["transfer.confirmed", "agent.transaction.pending_approval"],
});
// The secret is shown once. Store it now.
const secret = webhook.secret;
// In your webhook handler:
const valid = await hc.webhooks.verifySignature({
payload: rawBody, // string or Uint8Array, exactly as received
signature: request.headers["x-hoodprivate-signature"],
secret,
});
if (!valid) throw new Error("invalid signature");Failed requests throw a typed error you can narrow on, so failure handling is code, not string matching:
import { HoodPrivateAPIError } from "@hoodprivate/sdk";
try {
await hc.transfers.create({ to: "@vendor", amount: "999999.00" });
} catch (err) {
if (err instanceof HoodPrivateAPIError) {
console.error(err.status, err.code); // 402 "insufficient_balance"
if (err.code === "insufficient_balance") {
// handle it
}
}
}HoodPrivateAPIError: the API returned a non-2xx status. Carriesstatus,code,body, andrequestId.HoodPrivateConnectionError: the request never reached the API (network failure or timeout).HoodPrivateError: the base class for both. Catch this to handle any SDK failure.
Sensible defaults, every one of them overridable:
const hc = new HoodPrivate({
apiKey: process.env.HOODPRIVATE_API_KEY!,
baseUrl: "https://api.hoodprivate.com", // override for a private gateway
timeoutMs: 30_000, // per-request timeout
fetch: customFetch, // bring your own fetch
});| Namespace | Methods |
|---|---|
hc.accounts |
me, balances, listAgents |
hc.transfers |
create, get, list |
hc.agents |
create, getSpendPolicy, updateSpendPolicy, listPendingTransactions, approveTransaction, rejectTransaction |
hc.webhooks |
create, list, delete, replay, deliveries, verifySignature |
Full REST documentation lives at docs.hoodprivate.com.
MIT