Using the API
API overview
Base URL, authentication, amount format, errors, rate limits and pagination for every Zanora API route.
Base URL
https://api.zanora.devThe API is HTTPS and JSON only. A request that reaches the gateway over plain HTTP is refused with 403 INSECURE_TRANSPORT before authentication, so a key sent over HTTP isn't accepted.
Authentication
Send your API key in the x-api-key header on every request:
curl -s https://api.zanora.dev/v1/whoami -H "x-api-key: zk.akey_….…"These routes need no key: GET /health, GET /v1/fees, the four signup routes, the three account-recovery routes (login, password/forgot, password/reset), and the two routes behind a funding link's pay page (POST /v1/funding-links/lookup and /pay).
| Status | Means |
|---|---|
401 UNAUTHORIZED | missing, mistyped, expired or revoked key |
403 FORBIDDEN | the key lacks the route's scope (the message names it), or the record belongs to another tenant |
Each route below lists the scope it needs. See Accounts and API keys for the full scope table.
Tenancy comes from the key
Routes work out your providerId or workspaceId from your key. Don't send them in request bodies. They're ignored for tenant keys, and a query that names another tenant changes nothing. List routes return your records only.
Amounts
Every amount is a string of integer micro-dollars: "priceMicros": "1000000" is $1.00, "1000" is $0.001. A JSON number is rejected with 400 VALIDATION_FAILED, never rounded, and a field named the old way (priceMinor, in cents) with 400 MONEY_UNIT_CHANGED. Responses use the same format. See Money and amounts.
Request bodies
- Send
content-type: application/jsononly when there's a body. An empty body with that header is rejected. - Routes whose body is optional accept no body at all.
- Unknown enum values are rejected, not ignored.
Errors
{ "error": { "code": "VALIDATION_FAILED", "message": "priceMicros must be a string of integer micro-dollars", "details": {} } }Handle errors by code. An unexpected error returns 500 with "internal error" and an errorId. Quote the errorId when you contact support@zanora.dev. Every code is listed in Errors and refusals.
Rate limits
Requests are limited per key (or per IP without one), 50 per second by default. Over the limit, you get 429 RATE_LIMITED with a retry-after header. Signup and password-recovery routes have a separate, stricter per-IP limit. If you have many workers, give each its own key rather than sharing one.
Requests without an x-api-key are also limited per IP at the edge, to 120 a minute. Keyed requests aren't, so a seller server handling many buyers from one address is fine.
Failed authentication is limited per IP. After 30 requests in a minute with a missing, unknown, revoked or expired key, every authenticated request from that address gets 429 RATE_LIMITED until the minute is up, even one with a working key. If you see that, check that nothing near you is sending a stale key, fix it, and wait out the retry-after.
Pagination
Lists that grow over time use a cursor, not an offset:
curl -s "https://api.zanora.dev/v1/receipts?limit=50" -H "x-api-key: $KEY"
# → { "receipts": [ … ], "nextCursor": "rcp_…" }
curl -s "https://api.zanora.dev/v1/receipts?limit=50&after=rcp_…" -H "x-api-key: $KEY"nextCursor is present only when the page came back full. An unknown cursor is refused rather than starting again from the top.
Route index
| Area | Routes |
|---|---|
| Signup and accounts | POST /v1/signup, /verify, /resend, GET /v1/signup/:id, /v1/accounts/* |
| Keys and identity | GET /v1/whoami, /v1/auth/self, /v1/auth/scopes, /v1/auth/keys… |
| Discovery | POST /v1/discovery/search |
| Capabilities | POST/GET /v1/capabilities, GET/POST /v1/capabilities/:id, …/deprecate |
| Providers | GET /v1/providers, /:id, …/verification-request, …/rate, …/payout-destination(s) |
| Wallets | /v1/wallets…: create, list, balance, ledger, top-up, funding links, deposit address, freeze, key replacement; /v1/funding-links/lookup, /pay |
| Policies and approvals | /v1/policies…, /v1/approvals… |
| Receipts | GET /v1/receipts, /:id, /v1/transactions/:id/receipts |
| Payouts and rails | POST /v1/payouts, GET /v1/rail/providers, /v1/rail/transactions |
| Platform | GET /health, /v1/fees, /v1/platform/public-key, /v1/platform/receipt-keys/:id |
| x402 protocol | POST /v1/x402/challenge, /authorize, /complete, /fail, for middleware authors |