Agent mandates: proof of authority for agents that pay
Overview
Section titled “Overview”Proof that a real person is behind an agent does not tell a merchant what that person allowed the agent to do. A mandate is that allowance, written down by the person themself:
- the currency the agent may pay in,
- the most it may pay in one payment and in total,
- the merchants or payees it may pay,
- how long the mandate lasts (up to 90 days),
- optionally, an amount above which each payment waits for the person’s yes.
Before it pays, the agent asks Shield360. The payment goes through the same decision function as every other agent action, so the agent’s kill switch, its run context and the person’s own permissions apply first. An allowed payment comes back with a signed proof of authority that the merchant can check offline, or with one call to Shield360.
Mandates are part of the Security module. Open an agent at Govern → Agent access → Agent authorization and choose its Mandates tab.
Who can do what
Section titled “Who can do what”| Action | Who |
|---|---|
| Give a mandate | The signed-in person whose authority it is. Their role must let agents change things for them (agent_delegation.write), and the agent must be enrolled, with its token live and its kill switch off. The mandate is always theirs: it cannot name someone else. |
| Revoke a mandate | The person who gave it, or anyone who can manage agent authorization. |
| Approve or reject a waiting payment | The person who gave the mandate, or anyone who can approve agent elevations. |
| See mandates and payments | Anyone who can view agent authorization. Auditors see them and change nothing. |
Pulling the agent’s kill switch revokes all its mandates. Payments waiting for approval lapse. Clearing the kill switch does not bring the mandates back.
If the person’s role later stops letting agents act for them, the agent’s payments under their mandates are refused (delegator_not_permitted).
How a payment is decided
Section titled “How a payment is decided”The agent calls POST /api/agent-mandates/authorize with its own token:
curl -X POST "$SHIELD360_URL/api/agent-mandates/authorize" \ -H "Authorization: Bearer $SHIELD360_AGENT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "amountMinor": 1999, "currency": "USD", "counterparty": "shop.example.com", "reference": "order-42" }'| Field | Meaning |
|---|---|
amountMinor | A whole number in the currency’s minor units: 1999 is 19.99 USD, 500 is 500 JPY. |
currency | ISO 4217 code. |
counterparty | The merchant’s host name or a payee id. A mandate lists hosts (shop.example.com, or *.example.com for its subdomains) or payee ids. |
reference | Optional. The merchant’s order or invoice reference, up to 128 characters. Kept with the payment. |
mandateId | Optional. Use this mandate only. Otherwise Shield360 picks a live one that fits, preferring one that needs no approval, then the one expiring soonest. |
paymentId | Set it only to make a payment a person has approved (see below). |
The answer is always 200 with one shape:
outcome | What it means |
|---|---|
allow | Pay. proof is the proof of authority and proofExpiresAt says when it stops being accepted (five minutes). The amount is already counted against the mandate. |
needs_approval | The payment is above the mandate’s approval amount. It waits in the agent’s Mandates tab under paymentId. Once a person approves it, ask again with the same amount, currency and counterparty plus paymentId, within 24 hours. |
deny | Do not pay. reason says why. |
reason | Why |
|---|---|
mandate_granted | Allowed by a mandate. |
mandate_none | No live mandate: none was given, or each has expired or been revoked. |
mandate_currency | No live mandate covers this currency. |
mandate_counterparty | No live mandate lists this merchant or payee. |
mandate_per_payment | More than any live mandate allows in one payment. |
mandate_exhausted | The payment would take the mandate past its total. |
mandate_approval | Above the approval amount; waits for a person. |
kill_switch, agent_revoked, run_context, delegator_not_permitted | The agent-wide rules, as for any other action. |
The remaining amount is read, the decision made, the decision logged and the payment counted under the agent’s lock in one transaction. Two payments at once cannot both fit where only one does. If an allowed payment cannot be counted, it is denied (spend_unrecorded), never made off the books. Every answer is in the agent’s decision log under the Payments enforcement point.
The proof of authority
Section titled “The proof of authority”The agent sends the proof to the merchant, for example in a Shield360-Mandate header. It is a compact JWS (RFC 7515) signed with EdDSA (Ed25519). Its header is { "alg": "EdDSA", "typ": "shield360-mandate+jwt", "kid": "…" } and its claims are:
| Claim | Meaning |
|---|---|
iss | Your Shield360 address. |
sub | agent:<id>, the agent. |
aud | The counterparty it may pay. |
jti | The payment’s id. Accept each proof once. |
iat, exp | Issued and expiry times (five minutes apart). |
agent | The agent’s id and name. |
principal | { ref, verified: true, kind: "organisation_member" }. The person whose mandate allowed it, as a one-way reference: stable per person, never their name or address. |
mandate | Its id, currency, per-payment limit and expiry. |
payment | Amount in minor units, currency, counterparty and reference. |
approval | Whether a person approved this payment, and who (a one-way reference). |
decision | The decision log id and the policy version it was decided under. |
Verifying it as a merchant
Section titled “Verifying it as a merchant”Offline. Read kid from the header and fetch the key once: GET /api/agent-mandates/keys?kid=<kid> returns { keys: [<JWK>] } and the PEM. Any JOSE library verifies the proof with it. Check exp, check that aud is your host or payee id, check that the amount matches the cart, and reject a jti you have already accepted.
Online. POST /api/agent-mandates/verify with { "proof": "…", "counterparty": "shop.example.com" }. It checks the signature, expiry and counterparty, then what an offline check cannot see: whether the payment is still authorized and the mandate has not been revoked since. The answer is { valid, reason?, claims, payment, mandate }.
Neither route needs an account. Each answers about one key or one proof and lists nothing. The signing key is the environment’s attestation key, which also signs AI-code attestations and provenance checkpoints. A rotated-out key stays published, so earlier proofs still verify.
To prove to any API or MCP server which agent is calling, not only to merchants it pays, see Agent passports.
What it does not do
Section titled “What it does not do”- It does not move money. The merchant’s own payment flow runs as before; the proof tells it the agent was authorized to decide.
- It does not prove that a person is unique or human. Signals like that answer a different question and can sit alongside a mandate.
- A refund does not give the amount back to the mandate yet. Give a new mandate if the total is used up.