Most payment systems record what happened. A pre-signing gate records what it saw before anything happened. That difference is the whole value of a receipt: it moves the evidence to the moment of the decision.
But "receipt" is an overloaded word. A screenshot is not a receipt. A log line is not a receipt. A JSON blob with a green checkmark is not a receipt unless it is bound to the exact thing that was signed and can be checked by someone who does not trust the issuer.
What is inside the receipt
A verify402 preflight receipt carries six things that matter:
- The intent hash. A hash of the normalized intent — the chain, asset, amount, payee, resource, calldata, and policy as the gate understood them.
- The request hash. A hash of the original request body, so a caller can prove the receipt belongs to the request it actually sent.
- The block. The pinned block where the chain state was read. A verdict is only meaningful at a block.
- The expiry. Receipts are short-lived by default. A check from ten minutes ago is not a check of the chain now.
- The verdict and reasons. PASS, CAUTION, or BLOCK, plus typed reason codes. Not a paragraph the model has to interpret.
- The signature. An HMAC over the receipt payload, so a verifier can detect tampering without calling the issuer's database.
The response also carries checked and notChecked. Those lists are part of the record, not decoration. They are how a reader knows whether the gate looked at the thing they care about.
What the receipt proves
It proves a narrow, useful set of facts:
- this service saw this normalized intent at this block;
- it returned this verdict, with these reasons and this scope;
- the receipt has not been altered since it was signed;
- it has not expired, if the verifier checks the clock.
That is enough to make the decision auditable. An executor can require a valid receipt before it calls the signer. A treasury can keep the receipt next to the transaction. A counterparty can ask for it and verify it without trusting a dashboard.
What it does not prove
A receipt is not a guarantee that the payment is wise, the counterparty is honest, the contract is safe, or the trade will work. It is a record of a check, not a promise about the future.
It also does not prove that the signer used the receipt. That is an executor problem. The gate can issue a PASS; only the code that holds the key can refuse to sign without one. This is why the reference guard checks both bindings and fails closed, rather than trusting the agent to remember.
Why intent binding is the part people skip
Imagine a gate returns PASS for a 0.01 USDC payment to a known payee. The agent then signs a 100 USDC payment to a different address. The receipt is genuine. The signature is valid. The payment is wrong. If the receipt is not bound to the exact intent, it is theater.
This is why verification has two booleans, not one:
intentBound— the caller's normalized intent matches the hash in the receipt;requestBound— the original request body matches the request hash.
Require both. A receipt-only check is still signature-valid, but it is unbound: it tells you the receipt is authentic, not that it belongs to the transaction in front of you. That distinction is the difference between evidence and a souvenir.
The executor is where the gate becomes real
A gate that returns BLOCK and an agent that signs anyway is not a gate. The enforcement point is the code that calls the signer. The pattern is simple:
- build the intent;
- call the gate;
- require
sign_allowed; - verify the receipt against the intent and request;
- require
intentBoundandrequestBound; - check the expiry;
- only then call the signer.
No bypass flag. No "continue anyway" button in the agent's tool loop. If the receipt is missing, expired, or unbound, the signer does not run.
What to ask a receipt for
If a service shows you a green check before a payment, ask four questions:
- What exact intent is this bound to?
- At what block was the chain state read?
- What did it not check?
- Can I verify it without trusting your dashboard?
If the answer to the last one is no, you have a logo, not a receipt.
Test the binding
Break my receipt.
Run a free intent, change one field, and try to verify the old receipt against the new intent. If it still passes, that is a bug I want to see.
Next: Three payment intents your agent should never sign unchecked. · All notes