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

POST/v1/authorizations

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.

FieldTypeRequiredMeaning
amountnumberyesTransaction amount
currencystringyesISO code, must match a limit currency on the mandate
recipientstringyesWho gets paid: vendor:acme_saas, or a protocol-level address
categorystringyesSpend category, evaluated against the mandate's allowed list
protocolstringnoPin a rail: x402, mpp, acp, ap2, card. Omit and routing chooses
agentstringorg keyWhich agent this request is for; ignored on an agent key
metadataobjectnoUp 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

FieldMeaning
decisionId of this evaluation; also the jti of the verdict token
requestWhat was asked, echoed back
verdictOne of the eight decision codes
reasonWhich rule decided, in words: amount 720.00 exceeds per_transaction limit 500.00
mandateThe mandate the request was evaluated against; absent on deny_no_mandate
policy_versionThe rule set in force at that moment — what makes the verdict reconstructable
nonceSingle-use marker; a counterparty that sees it twice is looking at a replay
modeenforce, or observe while observe mode runs — in observe, nothing blocked
issued_atWhen the evaluation happened; the iat of the verdict token
expires_atOn allow only: five minutes after issue, the approval stops being presentable for settlement
verdict_tokenThe decision as a signed ES256 JWT — verifiable by anyone against the public key set
supersedesOn 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:

POST/v1/reviews/{id}/approve
POST/v1/reviews/{id}/deny

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

GET/v1/decisions/{id}
GET/v1/decisions?agent=agt_procurement_01&verdict=deny_limit&from=2026-08-01&to=2026-09-01

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.

On this page