Endpoints
Policies and approvals
Register, list and switch spending rules on or off, and resolve purchases that a rule held for a person.
Policies
/v1/workspaces/:id/budgetScope policies:write (workspace key; your own workspace only). Sets the daily spending cap, the time zone its day runs in, or both. Body { dailyBudgetMicros?, timeZone? }, with at least one:
dailyBudgetMicros: string micro-dollars such as"15000000"($15.00), ornullto clear your own cap (the maximum then applies, if there is one). Zero is refused, and so is anything above the maximum: the 24-hour funding limit ($100 on the hosted platform), reported asmaxDailyBudgetMicros, since a workspace can't spend more in a day than it can fund.timeZone:"UTC"or an IANA name such as"Africa/Nairobi", ornullfor UTC. Abbreviations are refused.
A refused value applies nothing. Returns the budget view below, and takes effect on the next purchase. SDK: client.setDailyBudget(workspaceId, minor | null), client.setBudgetTimeZone(workspaceId, zone | null).
/v1/workspaces/:id/budgetScope policies:read. The budget as the gateway enforces it right now: { workspaceId, dailyBudgetMicros?, ownDailyBudgetMicros?, maxDailyBudgetMicros?, timeZone, day, dayStartsAt, dayEndsAt, spentTodayMicros, remainingMicros? }. dailyBudgetMicros is the cap purchases are held to: your own (ownDailyBudgetMicros) clamped to the maximum (maxDailyBudgetMicros), or the maximum alone when you set none. day is the local date in timeZone; dayStartsAt/dayEndsAt are UTC instants. spentTodayMicros is what counts toward the cap: payments since dayStartsAt, less refunds of those payments. Show this rather than computing "today" yourself. SDK: client.budget(workspaceId).
/v1/policiesScope policies:write (workspace key). Registers a policy document. workspaceId comes from your key.
| Field | Type | Required |
|---|---|---|
document | string, in the policy language | yes |
curl -s -X POST https://api.zanora.dev/v1/policies -H "x-api-key: $WKEY" \
-H 'content-type: application/json' -d '{"document":"approval:\n price > 5"}'
# → {"policies":[{"id":"pol_…","policyType":"approval","enabled":true,…}]}A document with several sections becomes several policies. A line that can't be parsed rejects the whole document with 400.
/v1/policiesScope policies:read. Your workspace's policies, including disabled ones: { policies }.
/v1/policies/:id/disableScope policies:write. Stops applying a policy from the next purchase. No body. Returns { policy }.
/v1/policies/:id/enableScope policies:write. Starts applying it again. No body.
Approvals
/v1/approvalsScope approvals:read. Your workspace's approval requests: { approvals }. Filter with ?status=pending (or approved, denied, expired, consumed). Leave it off for the full audit trail.
{ "approvals": [{ "id": "apr_…", "agentId": "invoice-bot", "walletId": "wal_…", "capabilityId": "cap_…",
"amountMicros": "12000000", "currency": "USD", "rule": "price > 5", "status": "consumed", "createdAt": "…",
"resolvedAt": "…", "resolvedBy": "console:acc_…", "consumedAt": "…" }] }| Field | Meaning |
|---|---|
status | pending → approved or denied (a person decided) → consumed (the purchase it released went through). approved means granted but not used yet. |
resolvedAt | when the person decided. Using the approval doesn't change it |
resolvedBy | who decided, as a label: console:<account id> from the console, mcp-elicitation:<agent> from a prompt in the agent's MCP client, or whatever resolvedBy you sent |
consumedAt | when the purchase it released went through. Only on consumed |
/v1/approvals/:id/approveScope approvals:write. Approves the request. Body is optional: { resolvedBy? }. The approval covers one later purchase by that wallet of that capability, at up to the approved amount.
/v1/approvals/:id/denyScope approvals:write. Denies it. Body is optional: { resolvedBy? }.
Keep approvals:write away from agents
Give agents a key without approvals:write, so a model can never approve its own held spend. See Approvals.