ZanoraBetaDocs

Set up

Rules the gateway applies to every purchase before any money moves — deny, require approval, or allow-list — plus a daily budget for the whole workspace.

A policy is a short text document attached to your workspace. The gateway evaluates it on the server, before the payment is taken, for every purchase by every wallet in the workspace. An agent can't skip it, and a model can't talk its way past it.

The language

YAML
deny:
  category == "Adult"
  provider.reputation < 0.3
approval:
  price > 5
allow:
  provider.verified == true
  currency == "USD"

There are three kinds of section, and each line is field comparison value:

SectionA purchase is caught when…Result
deny:any line matchesPOLICY_DENIED: refused
approval:any line matchesAPPROVAL_REQUIRED: held for a person
allow:all lines in one allow policy match, for at least one allow policyif allow policies exist and none matches: POLICY_DENIED

Fields

FieldTypeExample
pricenumber, in dollarsprice > 5
categorystringcategory == "OCR"
currencystringcurrency == "USD"
provider.verifiedbooleanprovider.verified == true
provider.reputationnumber, 0 to 1provider.reputation < 0.3
provider.idstringprovider.id == "prv_3652ec9e…"
capabilityIdstringcapabilityId != "cap_…"
agentIdstringagentId == "experiments"
walletIdstringwalletId == "wal_…"

Rules on provider.* don't apply to sellers outside Zanora. They have no provider record, so a rule like provider.verified == false is skipped for those purchases instead of holding every one. Your workspace's outside-seller settings judge those sellers. Every other field still applies.

Comparisons: == != < <= > >=. Put strings in quotes. # starts a comment. A line that refers to a field the purchase doesn't have never matches.

Evaluation order

  1. Deny rules. The first matching line refuses the purchase.
  2. The workspace daily budget. If this purchase would take today's spending over the budget, it's refused.
  3. Approval rules. The first matching line holds the purchase, unless a person has already approved this exact spend. See Approvals.
  4. Allow rules. If any exist, at least one must match completely.
  5. Otherwise the purchase is allowed.

Adding a policy

In the console: Buyer → Policies → Add a rule, choose the section, and type one rule per line. Buyer → Agents → Create agent can also add two approval rules as it creates the agent, price > <amount> and provider.verified == false. They're workspace rules like any other, so they apply to every agent, not only the new one. A rule already in force in an enabled approval policy isn't added a second time.

With the API:

Terminal
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\ndeny:\n  provider.verified == false"}'

The response lists the parsed policies, each with its own id. A document with several sections becomes several policies.

CallDoes
GET /v1/policieslist your workspace's policies, including disabled ones
POST /v1/policies/:id/disablestop applying one, from the next purchase
POST /v1/policies/:id/enablestart applying it again

Policies can't be edited. To change one, add the corrected version and then disable the old one. That keeps a record of which rules applied to which purchases.

Daily budget

The workspace's dailyBudgetMicros caps total spending across all its wallets per day. When a purchase would exceed it, the purchase is refused with POLICY_DENIED and matchedRule: "workspace.dailyBudget". A payment that's refunded (for example because the seller's call failed) gives its amount back to the day's budget. Only refunds of payments made that day count, so a refund today for yesterday's purchase doesn't raise today's allowance.

Whose day? The workspace's. By default the day runs midnight to midnight UTC. Set a time zone (an IANA name such as Africa/Nairobi or America/New_York) and it runs midnight to midnight there instead, including daylight-saving changes. It never follows where your agents run: a workspace has many agents, and the gateway can't verify an agent's clock or location.

There's a maximum. A workspace can fund its wallets with at most a set amount per rolling 24 hours (the 24-hour funding limit, $100 on the hosted platform), so that's also the most it can spend in a day. A workspace that sets no budget is held to that maximum, and a budget above it is refused when you set it. A budget stored before the maximum existed, or chosen at signup, is held to the maximum without being rewritten. The budget reading reports the maximum as maxDailyBudgetMicros and your own choice, if you made one, as ownDailyBudgetMicros; dailyBudgetMicros is always what purchases are actually held to.

Set the budget at signup, then change it any time. In the console: Settings → Daily budget, where you can also pick the time zone, and Use the maximum clears your own budget. With the API:

Terminal
curl -s -X POST https://api.zanora.dev/v1/workspaces/$WSP/budget -H "x-api-key: $WKEY" \
  -H 'content-type: application/json' -d '{"dailyBudgetMicros":"15000000","timeZone":"Africa/Nairobi"}'
# either field alone works; null clears your budget (back to the maximum) / resets the zone to UTC

curl -s https://api.zanora.dev/v1/workspaces/$WSP/budget -H "x-api-key: $WKEY"
# → { "dailyBudgetMicros":"15000000", "ownDailyBudgetMicros":"15000000", "maxDailyBudgetMicros":"100000000",
#     "timeZone":"Africa/Nairobi", "day":"2026-09-26",
#     "dayStartsAt":"2026-09-25T21:00:00.000Z", "dayEndsAt":"2026-09-26T21:00:00.000Z",
#     "spentTodayMicros":"3000000", "remainingMicros":"12000000" }

Changes apply to the next purchase. A budget of zero is refused: to stop an agent spending, pause it instead. Abbreviations such as EAT or EST are refused too, because they're ambiguous (EST is read as Panama, which has no daylight saving).

Recipes

approval:
  price > 5

The pause after repeated failures isn't a policy

Separately from your rules, if one seller fails three calls in a row for a wallet (a 5xx, a crash, an MCP isError or a timeout, not a 4xx for a bad request), that wallet is paused from buying from that seller (WALLET_PROVIDER_PAUSED). This protects your balance from a broken seller. It lifts on its own, or you can lift it now.