Authorizations
Create a spend authorization, read the decision, and resolve reviews.
The endpoint the rest of the API exists around. An agent asks before it spends; the answer comes back signed.
Create an authorization
Called with an agent key; the agent is taken from the key. An organization key may call it too, naming the agent explicitly — useful for replaying traffic in sandbox.
| Field | Type | Required | Meaning |
|---|---|---|---|
amount | number | yes | Transaction amount |
currency | string | yes | ISO code, must match a limit currency on the mandate |
recipient | string | yes | Who gets paid: vendor:acme_saas, or a protocol-level address |
category | string | yes | Spend category, evaluated against the mandate's allowed list |
protocol | string | no | Pin a rail: x402, mpp, acp, ap2, card. Omit and routing chooses |
agent | string | org key | Which agent this request is for; ignored on an agent key |
metadata | object | no | Up to 20 string keys, yours to fill; returned unread in the decision |
curl -X POST https://api.saifuro.com/v1/authorizations \
-H "Authorization: Bearer ak_live_..." \
-H "Idempotency-Key: 7f9c64d1-order-4412" \
-d '{
"amount": 340.00,
"currency": "USD",
"recipient": "vendor:acme_saas",
"category": "saas"
}'The response is a decision, whatever the verdict — a denial is 201, not an error, and is metered like everything else:
{
"decision": "dec_5b2e",
"request": {
"agent": "agt_procurement_01",
"amount": 340.00,
"recipient": "vendor:acme_saas",
"category": "saas"
},
"verdict": "allow",
"reason": "within mandate mnd_7f3a",
"mandate": "mnd_7f3a",
"policy_version": "pol_2026-08-12.3",
"nonce": "n_9d02c6e4",
"mode": "enforce",
"issued_at": "2026-09-03T08:43:00Z",
"expires_at": "2026-09-03T08:48:00Z",
"verdict_token": "eyJhbGciOiJFUzI1NiIs..."
}The decision object
| Field | Meaning |
|---|---|
decision | Id of this evaluation; also the jti of the verdict token |
request | What was asked, echoed back |
verdict | One of the eight decision codes |
reason | Which rule decided, in words: amount 720.00 exceeds per_transaction limit 500.00 |
mandate | The mandate the request was evaluated against; absent on deny_no_mandate |
policy_version | The rule set in force at that moment — what makes the verdict reconstructable |
nonce | Single-use marker; a counterparty that sees it twice is looking at a replay |
mode | enforce, or observe while observe mode runs — in observe, nothing blocked |
issued_at | When the evaluation happened; the iat of the verdict token |
expires_at | On allow only: five minutes after issue, the approval stops being presentable for settlement |
verdict_token | The decision as a signed ES256 JWT — verifiable by anyone against the public key set |
supersedes | On a review-approval decision only: the original review decision it resolves |
Reviews
A review verdict means the policy wants a person: soft limit crossed, new category, unusual recipient. The decision then carries a review object:
{
"verdict": "review",
"review": {
"review": "rev_3c17",
"waits_for": "role:procurement_lead",
"expires_at": "2026-08-21T14:02:00Z"
}
}The mandate owner acts through your systems, and your backend resolves the review with an organization key:
Approval issues a fresh decision — allow, new nonce, its own five-minute expiry, supersedes pointing at the original. The original decision stays in the log untouched: a correction is a new record, never a rewrite. Denial and expiry close the review in place; the review's status (approved, denied, expired) is itself a record.
A pending review also fires the decision.review_pending webhook, and its resolution fires decision.resolved.
Read decisions
An organization key reads everything; an agent key reads its own. Filters: agent, mandate, verdict, mode, from, to. Paginated as described in the overview.
The full log is also exportable in bulk — see usage and exports.
Latency
The authorization call is synchronous and sits inside the agent's transaction path. We publish no latency figures here: p50 and p99 are measured on your own traffic during observe mode, because a number from someone else's network would say little about yours.

