Endpoints
Wallets
Create and list wallets, read balances and ledgers, fund them, freeze or unfreeze them, and replace an agent's signing key.
/v1/walletsScope wallets:write. Creates a wallet in your workspace (workspaceId comes from your key).
| Field | Type | Required | Notes |
|---|---|---|---|
ownerType | "agent" | "workspace" | yes | agent for anything that buys |
ownerId | string | yes | your label for the agent, e.g. invoice-bot |
publicKeyPem | string | agents: yes | the ed25519 public key that payment proofs are checked against |
/v1/walletsScope wallets:read. Your wallets: { wallets }. A provider key returns its settlement wallet.
/v1/wallets/:idScope wallets:read. Wallet details: owner, status, and whether it's frozen.
/v1/wallets/:id/balanceScope wallets:read. { balanceMicros, availableMicros, withdrawableMicros, clearingMicros, heldForDisputesMicros, clearingPeriodMs, currency }. Available is the balance minus holds. Withdrawable is what a payout can take now: the balance minus earnings still in the clearing period and anything held for a card dispute. Everything is calculated from the ledger every time. The balance can be negative after a card dispute.
/v1/wallets/:id/ledgerScope wallets:read. Every ledger entry for the wallet (amounts as strings). Refunds and corrections appear as new entries. Nothing is removed.
/v1/wallets/:id/topupScope wallets:write. Starts a card payment into the wallet (bank debit is in preview). Body { amountMicros, confirmationToken?, asset?, treasuryProviderId? }. Returns { intent: { externalRef, status, amountMicros, hostedUrl?, clientSecret?, declineReason? } }. The wallet is credited when the payment processor confirms.
With confirmationToken (a Stripe ConfirmationToken made in the buyer's browser), the gateway checks the card and confirms the payment itself: status is succeeded, processing, declined, or requires_action (finish 3-D Secure with clientSecret). Without it, you get an unconfirmed intent to finish with clientSecret. A top-up under the international minimum ($50) must send confirmationToken, because a card issued outside the US is refused below it, before anything is charged. See your own card form.
Returns 400 VALIDATION_FAILED below a minimum (details.limit says which), 501 NOT_SUPPORTED if no connected provider takes top-ups, and 429 LIMIT_EXCEEDED over a top-up limit.
/v1/wallets/:id/funding-linksScope funding:request (agent keys have it). Creates a one-time page where a person pays a card top-up into this wallet. Body { amountMicros }. Returns 201 { link: { id, walletId, amountMicros, currency, expiresAt, status: "open", url, token } }. url is the console's pay page with the token after #, so the token never reaches a server log. The link pays once and expires after 30 minutes.
Returns 501 NOT_SUPPORTED if no connected provider takes card top-ups, 400 VALIDATION_FAILED under the card minimum, and 429 LIMIT_EXCEEDED when the wallet already has 5 unpaid links. See a payment link, from your agent.
/v1/funding-links/lookupNo auth. What the pay page shows. Body { token }, so the token stays out of URLs. Returns { link: { id, amountMicros, currency, status, expiresAt, credited }, payee: { workspaceName?, agentName?, accountEmail? }, card?: { vendor, publishableKey, testMode }, minimums? }. status is open, paying, charged or expired; credited turns true once the card processor confirms. accountEmail is partly hidden. An unknown or altered token is 404.
/v1/funding-links/payNo auth. Pays the link with a card the browser tokenised. Body { token, confirmationToken }. The amount and wallet come from the link, never from the body. Returns { link: { id, status }, intent }, with intent as for /topup. A declined card leaves the link open to try another one; a paid link refuses a second payment with 400. Card minimums, caps and the hourly top-up limit apply as they do to /topup. Both public routes are limited to 30 requests a minute per IP.
/v1/wallets/:id/deposit-addressScope wallets:write. Gets or creates the wallet's USDC deposit address. Body is optional: { asset?: "USDC", chain?: "base", treasuryProviderId? }. It's idempotent: asking twice returns the same address. Returns { address: { address, chain, asset, walletId, treasuryProviderId, createdAt } }.
/v1/wallets/:id/deposit-addressesScope wallets:read. The wallet's registered deposit addresses.
/v1/wallets/:id/faucetScope wallets:write. Testnet deployments only, so not available on the hosted platform. Sends test USDC to the wallet's deposit address. Body is optional: { address? }.
/v1/wallets/:id/fundScope wallets:write. Not available on the hosted platform, where it returns 501 NOT_SUPPORTED: real funding comes through a card or a USDC deposit. It exists for Zanora's internal development gateways, where it credits a wallet against a simulated deposit.
/v1/wallets/:id/keyScope wallets:write. Your own agent wallets only. Replaces the wallet's signing key, which is how you recover from a lost or leaked private key. Body { publicKeyPem }: an ed25519 public key PEM. A private key, a non-ed25519 key, or anything else is refused with VALIDATION_FAILED. Returns the wallet, with keyReplacedAt and keyUsableAt.
- The old key stops verifying immediately.
- The new key can't pay until
keyUsableAt(24 hours by default). Purchases before then getWALLET_KEY_COOLING_DOWN(409), and nothing is charged. - The replacement pages the platform's operators (
wallet_key_replaced). - Sending the key the wallet already has is a no-op: no cool-down, no alert.
- SDK:
client.replaceWalletKey(walletId, publicKeyPem).
/v1/wallets/:id/freezeScope wallets:write. Blocks all outgoing payments. No body.
/v1/wallets/:id/unfreezeScope wallets:write. Reverses a freeze. No body.
/v1/wallets/:id/resume-providerScope wallets:write. Lifts the pause on a seller that failed three calls in a row for this wallet (5xx, thrown handler, MCP isError or timeout; 4xx responses don't count). Body { providerId }. Returns { ok, paused }.
/v1/wallets/:id/breaker/:providerIdScope wallets:read. Whether a seller is paused for this wallet: { walletId, providerId, paused }.