Receipt
A receipt is the one artefact Regent produces that is meant to leave Regent. It states what one decision authorised and what settled, under which signed terms and which policy version, and it verifies with a public key alone. An insurer, an auditor, a merchant in a dispute or a regulator checks it without an account, without calling Regent, and without trusting the operator of the agent.
This page is the specification. The verifiers that implement it are public:
regent-receipt-verify (PyPI
regent-receipt-verify, npm @regent-protocol/receipt-verify, Apache-2.0), with test vectors
minted by the real issuer.
Shape
A receipt is a compact JWS (header.payload.signature), signed RS256 by Regent’s issuer.
| Header | Value |
|---|---|
alg | RS256 |
typ | receipt+jwt |
kid | the signing key’s id, looked up in Regent’s JWKS |
The JWKS is published at https://control-api.regentprotocol.org/v1/control/.well-known/jwks.json.
Keys rotate; save the JWKS together with the receipts you keep, and pass the saved copy to the
verifier later. Every JWKS you ever saved stays valid for the receipts minted under it.
Two other tokens share the issuer and the verifier: the allow token (aud = the tool,
scope = tool:action, sixty seconds, the proof an agent carries to the rail) and the budget
token (typ aa-auth+jwt, an AAuth budget envelope). The verifier names the kind it was given;
a receipt can never pass as an access token, because its aud is the fixed string
regent-receipt.
Claims
Always present:
| Claim | Meaning |
|---|---|
iss | regent-control |
sub | the agent’s agent_… identifier |
aud | regent-receipt |
jti | the receipt id, rcp_… |
decision_id | the decision this receipt closes, dec_… |
status | success or failed |
iat | when the receipt was issued |
exp | ten years after iat; the retention horizon, not a validity window for the facts |
Present on money decisions:
| Claim | Meaning |
|---|---|
amount, currency | what settled, in the purchase currency (a number and an ISO 4217 code or a token symbol) |
payee | the counterparty: a merchant name, a store id, a chain address |
payee_source | who named the payee: rail (read from the payment rail itself, never from the agent) or agent |
payee_kind | the kind of counterparty when the rail says it (a connected account, a bank payout, a merchant) |
mandate_id | the mandate the decision was taken under |
mandate_version, mandate_hash | the version of the mandate’s terms in force, and a salted commitment to them (see Commitments) |
tool, action | what the agent was doing: stripe / charge.create, agentcard_buy / POST /buy, custody / transfer |
args_hash | a commitment to the exact request the decision bound (see Commitments) |
settlement_mismatch | true when what settled differs from what was decided (a lower or higher amount, another payee) |
Present when the organization uses them:
| Claim | Meaning |
|---|---|
policy_version, policy_hash, policy_source | the organization’s policy version in force and a salted commitment to it; policy_source is org, starter (Regent’s public starter policy; no reveal needed) or legacy |
risk_model | the behavioural model version that scored the request (guardian-1.2); the score itself is not in the receipt |
approval_method | how a person approved the terms or this escalation: passkey, passkey+authid (passkey plus a biometric step-up), or session in a sandbox |
approval_digest | a commitment to exactly what the person saw and signed |
environment | production or sandbox; absent on receipts older than October 2026 |
assertion_scheme, assertion_kid, key_custody | how the agent itself was authenticated: the signature scheme on its request, the key id, and whether that key is the agent’s own or held in custody for it |
ap2_transaction_id, ap2_payment_reference | the references of the AP2 mandate chain emitted for this decision (AP2 Evidence) |
agent_report_mismatch | true when the agent’s own report of the outcome disagreed with what the rail confirmed |
Present when the money’s outcome came from somewhere other than the agent:
| Claim | Meaning |
|---|---|
settlement_source | who the settlement facts came from: counterparty (the merchant or PSP signed a confirmation), provider (the rail reported the outcome), custodian (the custodian executed the transfer) |
settlement_ref | the rail’s own reference for the settled money: a chain transaction hash, a PSP charge id |
settlement_network | the chain the reference lives on, when it does |
confirmed_by, confirmation_kid, confirmation_hash, confirmation | the counterparty’s signed settlement confirmation, embedded whole, with its issuer, key id and a hash over it |
Claims are added over time; a verifier shows every claim it is given and checks the ones it knows. An absent claim means the fact was not available for that decision, never that it was false.
Commitments
Three claims are commitments: hashes a third party recomputes from material the owner reveals. The ceilings themselves never ride in a token.
args_hash: SHA-256 over the canonical JSON of {"tool", "action", "resource", "args"},
keys sorted, no whitespace (separators (",", ":")), UTF-8, non-ASCII kept as is. Given the
request the agent made, anyone recomputes it and knows the receipt covers that request and
nothing else.
mandate_hash: SHA-256 over a canonical string of the mandate’s terms:
terms-v4|mandate_id=…|version=…|agent_id=…|owner_id=…|currency=…|settlement_chain=…|status=…|per_tx_limit=…|daily_limit=…|monthly_limit=…|per_entity_limit=…|entity_key=…|relational_cap=…|expires_at=…|agent_may_read_limits=…|allowed_resources=…|entity_limits=…|salt=…terms-v2 stops at agent_may_read_limits, terms-v3 adds allowed_resources, terms-v4
adds entity_limits (caps per counterparty). A missing value is written none, booleans
true/false. The owner reveals the snapshot and the salt to whoever needs the terms (an
insurer, an auditor); the verifier recomputes the string and compares. Every change to a
mandate is a new version with a new salt, so a receipt pins the terms that were in force at
that moment, not the terms as they are today.
policy_hash: SHA-256 over
policy-v1|org_id=…|version=…|text_sha256=…|profile_sha256=…|salt=…, where text_sha256 is
the hash of the organization’s policy text and profile_sha256 the hash of its canonical JSON
profile (none when there is none). For policy_source: starter the public starter text alone
opens the commitment.
Verify one
pip install regent-receipt-verify # or: npm i -g @regent-protocol/receipt-verify
curl -s https://control-api.regentprotocol.org/v1/control/.well-known/jwks.json > jwks-2026-10.json
regent-verify @receipt.txt --jwks jwks-2026-10.json \
--request request.json \ # optional: the request, to check args_hash
--mandate mandate-v4-reveal.json \ # optional: the owner's reveal, to open mandate_hash
--policy policy-v3-reveal.json \ # optional: the organization's reveal, to open policy_hash
--counterparty-jwks merchant-jwks.json # optional: to re-verify an embedded confirmationWhat a run proves, in order:
- Signature: the JWS verifies under a key in the JWKS whose
kidmatches the header. Regent signed exactly these claims. - Kind:
typ receipt+jwtandaud regent-receipt; a receipt is not an access token. - Issuer and time:
issisregent-control;iatis in the past;expnot yet reached. - Request binding (with
--request):args_hashrecomputed from the request equals the claim. - Mandate reveal (with
--mandate): the revealed snapshot and salt openmandate_hash, and the snapshot’sversionequalsmandate_version. - Policy reveal (with
--policy): the same forpolicy_hash. - Confirmation (with
--counterparty-jwks): the embedded confirmation verifies under the counterparty’s key, its hash equalsconfirmation_hash, and it names the samedecision_id,status,amountandcurrencyas the receipt. - Settlement reference:
settlement_refandsettlement_networkare shown; with the chain’s own explorer or RPC you look the transaction up yourself.
The library exposes each step (verify_token, check_request_binding, check_mandate_reveal,
check_policy_reveal, check_confirmation) so an integration can run only the ones it needs.
regent-verify bundle runs the same checks over a whole evidence bundle: the retained receipt,
the audit batch proof, Regent’s signed checkpoint and the epoch proof, and with --rpc the
anchor transactions on the network (Audit Trail).
What is never in a receipt
- A card number, CVV, expiry, bank account number or wallet private key. The rail holds the instrument; the receipt holds the rail’s reference to the settled money.
- A shipping address, a person’s legal name, an e-mail address or a phone number. The person
behind the agent is a
responsible_party_id, bound at registration and resolved only by the organization. - Cart contents or line items. The receipt commits to the request (
args_hash); it does not carry it. - The ceilings of the mandate or the text of the policy. Both are commitments; the owner reveals them to whom they choose.
- Biometric data. A biometric step-up leaves
approval_method: passkey+authidand nothing else. - The risk score.
risk_modelnames the model version; the score and the signals stay with the organization.
Retention
A receipt is issued when a decision completes, filed into the organization’s audit trail and
retained for ten years (exp). The audit trail is hashed, batched and anchored, so a receipt
retained there also proves when it existed. An organization can read its receipts back at any
time over the API and hand them, with the saved JWKS, to anyone who asks. A receipt in your
own files verifies the same way; Regent does not need to be reachable, or to exist, for the
check to pass.