ZZanoraDocs

Set up

When a policy holds a purchase for a person, how they approve or deny it, how the agent finds out, and how to make sure a model can never approve its own spend.

When an approval: rule matches, the purchase stops before any money moves. The agent gets APPROVAL_REQUIRED with an approvalId, and the gateway records an approval request:

JSON
{
  "id": "apr_7c1e…",
  "workspaceId": "wsp_…",
  "agentId": "invoice-bot",
  "walletId": "wal_91c2…",
  "capabilityId": "cap_7f3a…",
  "amountMinor": "1200",
  "currency": "USD",
  "rule": "price > 5",
  "status": "pending",
  "createdAt": "…"
}

The flow

1

The agent is refused

Nothing is charged. With the SDK, invoke throws ZanoraPolicyRejection with code: "APPROVAL_REQUIRED" and approvalId. With MCP, the tool result says the same in plain language.

2

A person decides

In the console: Buyer → Approvals shows pending requests with the amount, capability, wallet and the rule that held it. Click approve or deny. With the API:

Terminal
curl -s "https://api.zanora.dev/v1/approvals?status=pending" -H "x-api-key: $WKEY"
curl -s -X POST https://api.zanora.dev/v1/approvals/apr_7c1e…/approve -H "x-api-key: $WKEY" \
  -H 'content-type: application/json' -d '{"resolvedBy":"dana@northwind.example"}'
3

The agent retries once

An approval covers one purchase by that wallet of that capability, at up to the approved amount. It's used up by the next matching purchase, and a third call is held again. The agent learns the decision with zanora_approval_status (MCP), or by polling GET /v1/approvals.

Approving inside an MCP client

If the client supports MCP elicitation, @zanora/mcp asks the user directly at the moment of the purchase, and retries once if they approve:

Output
Approve a $12.00 USD purchase?

  Buying     Invoice OCR
  Capability cap_7f3a…
  Wallet     wal_91c2…
  Held by    price > 5

  [ Approve — spend $12.00 USD ]  [ Deny — do not buy this ]
  • The prompt is built from the gateway's approval record, never from the tool call. A model that assembled the purchase can't write the text the person approves.
  • Deny returns APPROVAL_DENIED, and the agent is told not to retry or ask again.
  • Dismissing the prompt leaves the request pending. Silence never counts as approval.
  • Clients without elicitation are never prompted. They get the approval id and stop.

Keeping approval with people

Recording an approval is an authenticated call, so it only succeeds if the agent's key has the approvals:write scope. A workspace root key has it. Two ways to stop a model approving its own spend:

SettingEffect
Give the agent a key without approvals:writethe gateway refuses to record the answer (APPROVAL_NOT_PERMITTED). Enforced by the platform. Recommended.
ZANORA_APPROVAL_ELICITATION=offthe MCP server never shows the prompt. Enforced only by that process's configuration
Terminal
# An agent key that can buy and read, but not approve
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-bot","scopes":["discovery:read","capabilities:read","wallets:read","receipts:read","approvals:read","ratings:write","rail:read"]}'