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
| Field | Commits to |
|---|---|
tool | Capability id, version and provider (receipt version 2). |
quote | Quote id, quote hash and quoted amount, when the call was quoted. |
requestHash | The request: method, URL and body. |
paymentRequirementsHash, pricingTermsHash | The payment terms that were accepted. |
payment | Scheme, network, asset, payer, authorized amount and actual amount. |
meteringHash | The usage claim, for metered routes. |
responseHash, responseStatus | The response that was delivered. |
settlement | Transaction, block, settlement state and assurance level at issuance. |
stateFlags | Whether 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
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 toThe 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.
- Find the receipt: the
PAYMENT-RESPONSEheader of a paid response, atextensions["tollex-receipt"].info, orGET /tollex/receipts/{receiptId}. It hasbody,contentHash,signer,signatureandformat. - 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 equalcontentHash. - Check the signature: EIP-712 typed data with domain
{ name: "Tollex Execution Receipt", version: "1" }(no chain id), primary typeExecutionReceiptwith fieldsversion(uint256),receiptId(string),contentHash(bytes32) andissuedAt(uint256). The recovered address must equalsigner. - Check the signer: it must be a key with status
active(or valid atissuedAt) in/.well-known/tollex-receipt-keys, or a key you pinned. - Check the bindings you care about:
payment.actualAmountagainst what you were charged,payment.payer, the settlement transaction on chain, andquote.quoteHashwhen 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.