Proof and operations

Receipts

Every paid response carries a signed record of what was bought, what was charged and how it settled.

What a receipt binds

FieldCommits to
toolCapability id, version and provider (receipt version 2).
quoteQuote id, quote hash and quoted amount, when the call was quoted.
requestHashThe request: method, URL and body.
paymentRequirementsHash, pricingTermsHashThe payment terms that were accepted.
paymentScheme, network, asset, payer, authorized amount and actual amount.
meteringHashThe usage claim, for metered routes.
responseHash, responseStatusThe response that was delivered.
settlementTransaction, block, settlement state and assurance level at issuance.
stateFlagsWhether the work executed, whether it was charged, whether settlement was final.

Receipts are EIP-712 typed data, canonicalised before hashing, and signed by the merchant’s receipt key.

Verifying

TypeScript · runs in the Tollex test suite
const res = await fetch(`${TOLLEX_URL}/.well-known/tollex-receipt-keys`);
const { keys } = (await res.json()) as { keys: { address: `0x${string}`; status: string }[] };
const check = await verifyReceipt(result.receipt!, {
  trustedSigners: keys.filter((k) => k.status === "active").map((k) => k.address),
});

check.valid; // signature, content hash and bindings all hold
result.receipt!.body.quote?.quoteHash; // the quote this payment was bound to

The keys at /.well-known/tollex-receipt-keys carry an id, the signing address, a validity window and a status (active, retired or revoked), so keys can rotate without breaking older receipts. In production, give the client the keys you trust (pinnedReceiptKeys, or a receiptTrust store with rotation and revocation). Without one, the client trusts a merchant’s published keys on first use and says so: results are labelled trust: "tofu".

Verify without Tollex software

Everything needed is public; the service descriptor at /.well-known/tollex.json repeats it under receipts.

  1. Find the receipt: the PAYMENT-RESPONSE header of a paid response, at extensions["tollex-receipt"].info, or GET /tollex/receipts/{receiptId}. It has body, contentHash, signer, signature and format.
  2. Recompute the content hash: keccak256 of the RFC 8785 (JCS) canonical JSON of body. The body contains only strings, booleans, objects, arrays and safe integers; amounts are decimal strings. It must equal contentHash.
  3. Check the signature: EIP-712 typed data with domain { name: "Tollex Execution Receipt", version: "1" } (no chain id), primary type ExecutionReceipt with fields version (uint256), receiptId (string), contentHash (bytes32) and issuedAt (uint256). The recovered address must equal signer.
  4. Check the signer: it must be a key with status active (or valid at issuedAt) in /.well-known/tollex-receipt-keys, or a key you pinned.
  5. Check the bindings you care about: payment.actualAmount against what you were charged, payment.payer, the settlement transaction on chain, and quote.quoteHash when you paid against a quote.

Receipt versions 1 and 2 are valid; version 2 adds the tool, the quote and state flags. The body’s JSON Schema is in the service descriptor.

What a receipt proves, and what it does not

It proves that the holder of the merchant’s key asserted these commitments at the stated time. The payment fields can be checked against the chain, and the request and response hashes let either side prove which bytes the receipt covers.

It does not prove that the response was correct or useful, that metering was honest (the usage claim becomes attributable and disputable, not trustless), or that settlement is final if the receipt was issued before finality. A later receipt can supersede an earlier one when settlement advances.

Route evidence for external merchants

When Tollex routes a purchase to an x402 merchant it does not run, there is no Tollex execution receipt, because Tollex did not execute anything. The service signs route evidence instead, in the TOLLEX-EXTERNAL-EVIDENCE header, under a separate EIP-712 domain (“Tollex External Route Evidence”, primary type ExternalRouteEvidence with the same four fields as a receipt). It is verified with the same recipe and the same key set.

Its attestation field sorts every claim by source. TOLLEX_OBSERVED covers what the service saw: the terms it compared, the authorization it forwarded and the request and response hashes. ONCHAIN_VERIFIABLE covers the settlement transaction and transfer, which anyone can re-check. MERCHANT_STATED covers hashes of what the merchant said about itself, which Tollex records but does not verify cryptographically; the class MERCHANT_ATTESTED is reserved for a merchant signature Tollex has verified and is never used in this version. Route evidence never turns a merchant’s statement into a Tollex claim, and it does not say the merchant’s output was correct. See External x402 merchants.

Batches

Receipts can be grouped into Merkle batches, so many receipts can be proven against one root. Roots are stored by the operator; they are not anchored on chain automatically.