ZZanoraDocs

Core concepts

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:

RoleTenantId prefixCreated with
Buyerworkspace: the unit of budget and policy, holding agent walletswsp_…POST /v1/signup with role: "buyer"
Sellerprovider: the unit of listings and earnings, with one settlement walletprv_…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:

Terminal
curl -s https://api.zanora.dev/v1/whoami -H "x-api-key: $ZANORA_API_KEY"
JSON
{
  "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:

Terminal
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/rotate with {"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=true also 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.

ScopeBuyer root keySeller root keyAllows
discovery:readsearch the marketplace
capabilities:readread capabilities; sellers list their own
capabilities:writepublish, edit and deprecate capabilities
providers:readread provider profiles; request verification
workspaces:readread your workspace
wallets:read / wallets:writeread balances and ledgers; create and fund wallets
policies:read / policies:writeread and change spending rules
approvals:read / approvals:writesee held spends; approve or deny them
ratings:writerate a seller you bought from
receipts:readread receipts: purchases for buyers, sales for sellers
payments:writethe x402 routes a seller's middleware calls
payouts:writeset a payout destination; request payouts
rail:readwhat funding and payout rails exist; your rail transactions
keys:read / keys:writelist, 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:

Output
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 password in 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.