Mandates

Create, read, and revoke an agent's authority to spend.

A mandate is authority written down as data — the object core concepts describes, managed over the API with an organization key.

Create a mandate

POST/v1/mandates
FieldTypeRequiredMeaning
principalstringyesWho grants the authority: role:procurement_lead, user:jkim
agentstringyesThe agent it applies to
limits.per_transactionnumberyesCap per spend
limits.per_monthnumbernoRolling monthly cap
limits.currencystringyesCurrency the limits are stated in
categoriesarrayyesAllowed spend categories; anything else returns deny_category
review_abovenumbernoAbove this, the verdict is review and the principal confirms
expiresstringyesDate the mandate stops matching; after it, deny_expired
escrow.required_abovestringnoForce escrow above a threshold, e.g. "1000 USDC" — see escrow
curl -X POST https://api.saifuro.com/v1/mandates \
  -H "Authorization: Bearer sk_live_..." \
  -d '{
    "principal": "role:procurement_lead",
    "agent": "agt_procurement_01",
    "limits": { "per_transaction": 500, "per_month": 5000, "currency": "USD" },
    "categories": ["saas"],
    "review_above": 250,
    "expires": "2026-11-18"
  }'

The response is the mandate as the system holds it — the same shape, plus mandate id and status: "active".

An agent can hold several mandates. A request has to fit inside at least one, and the decision names the mandate it was evaluated against; with no applicable mandate the verdict is deny_no_mandate, because spending without written authority is not possible.

Mandates do not change

There is no update endpoint, deliberately. A mandate that could be edited in place would break the promise that makes the log worth keeping — that a verdict from March can be explained with the rules that applied in March. To change limits, revoke and create a new mandate; both actions are records, and decisions keep pointing at the mandate that was in force when they were made.

Revoke

POST/v1/mandates/{id}/revoke

Takes effect on the next request: the agent's next call under this mandate returns deny_revoked, with no change to the agent's code and no action at any payment provider. The response is the mandate with status: "revoked" and revoked_at set.

Read

GET/v1/mandates/{id}
GET/v1/mandates?agent=agt_procurement_01&status=active

Filters: agent, principal, status (active, revoked, expired). Paginated.

The mandate.revoked and mandate.expiring webhooks cover the lifecycle events worth acting on.

On this page