ZanoraBetaDocs

Endpoints

Self-serve signup for buyers and sellers, and the email/password console login. Signup, sign-in and password recovery work without an API key.

Signup

POST/v1/signup

No auth. Claims an address and sends a confirmation email. Creates nothing yet.

FieldTypeRequiredNotes
role"buyer" | "seller"yes
emailstringyes
organizationNamestringyes
websitestringnohelps seller verification
dailyBudgetMicrosstringnobuyers only: the workspace's daily cap in micro-dollars
passwordstringnoenables console sign-in once the email is confirmed
withCodebooleannoalso email a 6-digit code, so the person can confirm through an AI agent instead of a browser. The link still works
refstringnowhere the signup came from: up to 64 letters, digits, _ or -. Recorded for Zanora's own attribution and never returned. The console sends the ref from a ?ref= link
JSON
// 202
{ "signupId": "sgn_…", "status": "pending", "expiresAt": "…", "emailSent": true }

There's one pending signup per email and role. Asking again resends the email for the same signup. The response is identical whether or not the address is already known.

Asking again doesn't change a signup's ref.

Asking again with withCode: true adds a code to a signup that didn't have one. A resend of a signup that has a code always sends a new code.

POST/v1/signup/verify

No auth. Confirms the email and creates the tenant. Send either the link's token, or the signupId with the emailed code.

FieldTypeRequiredNotes
tokenstring, from the email linkone of the two proofs
signupIdstringwith codefrom the POST /v1/signup response
codestring of 6 digitswith signupIdfrom the email. A JSON number is refused
agent{ name, publicKeyPem }no, code onlybuyers only. Creates an agent wallet with this ed25519 public key, and an agent key. name is the wallet's owner id, shown on receipts and approvals
defaultRulesbooleanno, code onlyregisters the console's suggested rules: purchases over $2.00, and any from an unverified seller, need a person's approval
JSON
// 201
{
  "signupId": "sgn_…", "role": "seller", "status": "completed",
  "tenant": { "kind": "provider", "id": "prv_…", "name": "Acme OCR", "walletId": "wal_…" },
  "credential": { "keyId": "akey_…", "token": "zk.akey_….…", "label": "…", "scopes": ["…"] }
}

credential.token is shown once. walletId (sellers only) is the settlement wallet. The token can be used only once and expires after 24 hours. If two requests use it at the same moment, only one tenant is created.

With agent, the response carries the agent's wallet and key instead of credential:

JSON
// 201
{
  "signupId": "sgn_…", "role": "buyer", "status": "completed",
  "tenant": { "kind": "workspace", "id": "wsp_…", "name": "Northwind" },
  "agent": {
    "walletId": "wal_…", "name": "claude-code",
    "credential": { "keyId": "akey_…", "token": "zk.akey_….…", "label": "claude-code agent", "scopes": ["discovery:read", "…"] }
  }
}

The agent key can search, buy, read balances and receipts, rate sellers and read approvals. It can't approve a held purchase or create keys. The workspace's root key is created but never returned to the agent, and no console session is returned either. The person signs in to the console with the password from the signup request. A signup without one is sent a "Set your console password" email as soon as it's confirmed (by link or code). The link works once for 24 hours, and is redeemed like a reset (POST /v1/accounts/password/reset). After that, Forgot password does the same.

The code's limits. It works once, expires 30 minutes after it was sent (never later than the link), and allows 5 attempts. After that, request a new one with POST /v1/signup/resend, which also invalidates the old code. A malformed code or agent key doesn't use up an attempt. A private key sent as publicKeyPem is refused: generate a new keypair, because that key has left the machine.

POST/v1/signup/resend

No auth. Body { role, email }. Always returns 202. Invalidates the previous link, and the previous code if the signup has one.

GET/v1/signup/:id

No auth. The status of a signup by its signupId: pending, completed or expired. Returns no email address and no token.

Console accounts

A console session is an ordinary API key of kind session, limited to your tenant.

POST/v1/accounts/login

No auth. Body { email, password }. Returns { sessionToken, expiresAt?, scopes, account }. Failed attempts count toward a lockout.

POST/v1/accounts/logout

Revokes the session presenting it. It refuses an ordinary API key, so an agent's key can't be revoked from a sign-out button by mistake.

GET/v1/accounts/me

The account behind the current session, plus its standing: { status: "active" }, or { status: "frozen", frozenAt, reason } while Zanora has frozen it. Returns 404 for an ordinary API key.

POST/v1/accounts/password/forgot

No auth. Body { email }. Always returns 202 with the same body.

POST/v1/accounts/password/reset

No auth. Body { token, password }. Revokes every console session and no API keys, so your agents keep running.

POST/v1/accounts/password

Changes the password while signed in. Body { currentPassword, newPassword }.

POST/v1/accounts/close

Closes your account. Console session only: an ordinary API key gets 403, so an agent can never close the account it runs on. Body { confirm, password?, reason? }, where confirm is the email address on the account and password is required if the login has one.

Returns { status: "closed", closedAt, purgeAfter }. Every API key and session is revoked at once, sign-in stops working, and a seller's listings are removed from discovery. Your account details are deleted by purgeAfter. See Closing your account for what happens to money left in your wallets. A frozen account can't be closed this way; email support.