Core concepts
Accounts and API keys
Tenants, API keys, scopes and console logins. Everything that authenticates you to Zanora, and how to keep it narrow.
Tenants
Everything you own on Zanora belongs to a tenant. The type depends on your role:
| Role | Tenant | Id prefix | Created with |
|---|---|---|---|
| Buyer | workspace: the unit of budget and policy, holding agent wallets | wsp_… | POST /v1/signup with role: "buyer" |
| Seller | provider: the unit of listings and earnings, with one settlement wallet | prv_… | POST /v1/signup with role: "seller" |
A tenant's id comes from its credential. You almost never send workspaceId or providerId in a request body. If you do, it is ignored for anything except an admin key. This means a leaked request can't be replayed against someone else's tenant.
API keys
An API key looks like zk.akey_3652ec9e….<secret> and goes in the x-api-key header on every call:
curl -s https://api.zanora.dev/v1/whoami -H "x-api-key: $ZANORA_API_KEY"{
"principal": "workspace(wsp_91c2…)",
"kind": "workspace",
"workspaceId": "wsp_91c2…",
"scopes": ["wallets:read", "wallets:write", "policies:write", "…"],
"keyId": "akey_3652ec9e…"
}Make /v1/whoami your first call with any new key. It tells you whether the key works and what it's allowed to do.
Keys are shown once
Zanora stores only sha256(token). A lost key can't be recovered, only replaced: mint a new one from another key, or from the console (Settings → API keys) after signing in with your password.
Your root key and the keys you mint from it
Signup gives you a root key with every scope your role can use. Use it to set up, not to run production. Mint a narrower key for each service or agent:
curl -s -X POST https://api.zanora.dev/v1/auth/keys -H "x-api-key: $ROOT_KEY" \
-H 'content-type: application/json' \
-d '{"label":"invoice-agent","scopes":["discovery:read","wallets:read","receipts:read","ratings:write"],"expiresInSeconds":2592000}'The rules:
- Delegation only narrows. A new key's scopes must be a subset of the scopes on the key that minted it, and it can't outlive that key.
- A key's scopes are fixed when it's minted. If a release adds a new scope, existing keys don't get it. Mint a replacement if you need it.
- Rotation doesn't need downtime.
POST /v1/auth/keys/:id/rotatewith{"graceSeconds":300}keeps the old secret working for five minutes while you redeploy. - Revoking takes effect on the next request.
DELETE /v1/auth/keys/:id?cascade=truealso revokes every key minted from it. Use that when a key has leaked.
Scopes
A scope decides which routes a key can call. Ownership decides which records it can reach. These are separate checks, and you need both. A provider key with wallets:read still can't read a buyer's wallet.
| Scope | Buyer root key | Seller root key | Allows |
|---|---|---|---|
discovery:read | ✓ | ✓ | search the marketplace |
capabilities:read | ✓ | ✓ | read capabilities; sellers list their own |
capabilities:write | ✓ | publish, edit and deprecate capabilities | |
providers:read | ✓ | ✓ | read provider profiles; request verification |
workspaces:read | ✓ | read your workspace | |
wallets:read / wallets:write | ✓ | ✓ | read balances and ledgers; create and fund wallets |
policies:read / policies:write | ✓ | read and change spending rules | |
approvals:read / approvals:write | ✓ | see held spends; approve or deny them | |
ratings:write | ✓ | rate a seller you bought from | |
receipts:read | ✓ | ✓ | read receipts: purchases for buyers, sales for sellers |
payments:write | ✓ | the x402 routes a seller's middleware calls | |
payouts:write | ✓ | set a payout destination; request payouts | |
rail:read | ✓ | ✓ | what funding and payout rails exist; your rail transactions |
keys:read / keys:write | ✓ | ✓ | list, mint, rotate and revoke keys |
GET /v1/auth/scopes returns the full list. When a key is missing a scope, the 403 names the scope it needed:
403 {"error":{"code":"FORBIDDEN","message":"GET /v1/metrics requires the \"metrics:read\" scope (key ci-publish holds: capabilities:write)"}}Give an agent a key without approvals:write
If an agent's key can't record approvals, the model can't approve its own held spend, even if you've set up in-client approval prompts. See Approvals.
Console logins
The console↗ uses an email and password. Signing in creates a session, which is an ordinary API key of kind session, held in an httpOnly cookie. It has exactly your tenant's permissions and no more.
- Resetting a password revokes every console session but no API keys, so your production agents keep working.
- Signing out revokes that session only. You can't use it to revoke an agent's API key by accident.
- If you sign up through the API rather than the console, include
passwordin the signup request if you want to be able to sign in to the console. The password only takes effect once the email is confirmed.