
Receipt
A drop-in x402 facilitator that pays the seller only if the response passes the buyer's checks
Demo
About
Receipt is a working x402 facilitator with conditional settlement, live on Hedera testnet. 54 real deals have gone through it: 22 released to the seller, 26 refunded to the buyer automatically, and 6 where the seller never answered and the money became claimable by anyone. All 48 verdicts it has published reproduce exactly when an independent implementation recomputes them from the public log. Zero mismatches. The gap it closes: when an agent pays for an API call over x402, the money moves the moment the payment is verified. If the API returns garbage, the agent has paid for garbage. x402 has no refund primitive and no dispute step anywhere in the stack. Receipt closes it without adding a judge. The buyer writes down what a good answer looks like, signs those terms, and only then pays. The payment sits in escrow on Hedera instead of going to the seller. When the response arrives, seven checks run against it: HTTP status, content type, body size, required JSON paths, JSON schema, freshness of the snapshot, and an optional exact-hash match. All seven pass and the escrow releases. Any one fails and the refund is automatic. If the seller never answers at all, the deadline passes and any stranger can call claimExpired to return the buyer's money. Having no judge is the innovation, not a limitation. Every check is a pure function of (terms, response), and that single property buys three things at once that nothing else in this space has together. The buyer can compute the verdict. The seller can compute it before answering and decline a sale it knows it cannot earn, which no evaluator-based design can offer, because you cannot consult an external evaluator before the work is delivered. And a stranger can recompute it afterwards and check it against what was recorded on chain. The counter-example is worth stating, because it is why we built it this way. Virtuals Protocol's Agent Commerce Protocol shipped escrow with Client, Provider and Evaluator roles, went live on Base, and accumulated over 12 million commerce memos. It now runs at roughly five unique senders a day. The evaluator is what broke it: nobody could answer who judges, who pays the judge, and why the verdict should be trusted. You do not have to trust us either. Every terms document, every raw response body and every verdict is published to a public Hedera Consensus Service topic, hashed with JCS (RFC 8785) plus keccak256. The live ledger is a static page with no backend of ours behind it: it rebuilds all 54 deals in your browser directly from the Hedera mirror node and recomputes any verdict locally, in front of you. If the public log did not carry what that page claims, the page could not exist. One command, pnpm verify --all, replays every verdict ever published. And a second implementation in Python, written from the spec rather than from our code, with zero dependencies and its own keccak and JCS, reaches identical verdicts. We are as strict about what we cannot prove as about what we can. Response latency is our own stopwatch and no third party can recompute it, so it is recorded, displayed, and gates nothing. The verdict splits into reproducible checks, which move the money, and attested ones, which do not. The seller in the demo is a stock x402 resource server selling live token data from The Graph, composing the Token API with a subgraph. Making it settle through Receipt means changing one URL. That is the drop-in claim, and it is a claim a judge can diff. CHECK IT ON CHAIN Escrow contract: 0x3483B3761ebe3C2fC2eB3EfE8215a7CF90634071 (Hedera id 0.0.10485798, verified on Sourcify, full match) Audit topic: 0.0.10495465 Hosted ledger: https://receipt-ledger-zeta.vercel.app Three deals, one of each outcome, all on Hedera testnet. Every hash below opens on hashscan.io/testnet and returns SUCCESS. 1. All seven checks passed, escrow released 0.5 HBAR to the seller deal 0x0aa1e03afb0bf9e2472d60390bf8128e443a53dc671a27c0115fd42f17027623 release() 0x8e4805f761c73814512bc1ce54ffcd506462628733ae74525432caa2849e1aaa 2. Seller answered HTTP 200 with an error blob, requiredPaths failed, buyer refunded automatically deal 0x55e54e954c4c2f32738b878408809d81d05cdc1aaa16835dbb1e468999c55411 refund() 0xdc71a23dab3f6566d0e6a6d6ce8ececdf993b61315b53c98c34e29ea45a2a2a5 3. Genuine Graph data, correct shape, every field valid, but the snapshot was hours old. Only the freshness check caught it, buyer refunded automatically deal 0xe17dcdc8eb7307a0d47a22af290590ac5cdc9035f0a5b896b2742ea12accd7bc refund() 0x43eb94f56c3adda7a92487192df8a2024a3c5dcfec1f77b8ff496d3c6253dbb3 That third one is the case worth opening. Nothing about it looks wrong and a human reviewer would approve it. Each of these three also has its terms, its raw response body and its verdict published on topic 0.0.10495465, which is what lets anyone recompute the verdict with no cooperation from us.
How it's made
Monorepo in TypeScript with pnpm workspaces. The centre of it is packages/core, where the adjudicator is a pure function from (terms, response) to verdict: no clock, no network, no randomness. That purity is the product, because it is what lets a buyer, a seller and a stranger each compute the same answer independently. It was built test first and the repo runs 307 tests, including ones that assert the adjudicator imports nothing impure, so the guarantee cannot rot. HEDERA. The escrow is a Solidity contract on Hedera testnet (chain 296), deployed with Foundry. It holds the HBAR between payment and verdict, verifies the buyer's EIP-712 signature on chain, and exposes release, refund and a permissionless claimExpired that lets any stranger return a buyer's money when a seller goes dark. Hedera Consensus Service is what makes the whole thing auditable at x402 prices. Every terms document, every raw response body and every verdict goes to topic 0.0.10495465, chunked on write and reassembled byte-wise on read. Publishing complete response bodies for every single transaction is only economically possible because HCS is cheap and final in seconds, and at a median x402 ticket well under a dollar nothing else we evaluated came close. The mirror node then does something decisive: it serves permissive CORS headers. That is why our hosted ledger is a static page with no backend of ours behind it. It rebuilds all 54 deals in the visitor's browser directly from Hedera and recomputes any verdict locally. A judge never has to trust our server, because there is no server. Hedera is what made the strongest version of this project possible. One piece of hard-won Hedera knowledge worth passing on: msg.value on the Hedera EVM is denominated in tinybars, not weibars, so the x402 valueScale must be 1. Every EVM tool you arrive with assumes 18 decimals. BLOCKY402. Settlement runs through Blocky402 as the x402 facilitator underneath ours, and Receipt implements the identical /verify, /settle and /supported surface. That is what makes it genuinely drop-in rather than a fork: an existing x402 seller changes one URL and gains conditional settlement. The payment itself is a partially-signed Hedera TransferTransaction carried in the PAYMENT-SIGNATURE header, so the buyer signs and pays no gas. THE GRAPH. The seller sells something real. It returns live token holdings from the Token API composed with the liquidity pools those holdings trade in from a subgraph, both products in one response, fetched together so a failure in either half fails the sale rather than half-answering it. This matters because the acceptance terms are written against the true shape of Graph data, and it is what makes the sharpest scene in the demo possible: a genuine, correct Graph response whose only defect is a stale snapshot. Nothing about it looks wrong and a human reviewer would approve it. Only the freshness check catches it, and the buyer's money comes back automatically. BAZANTIC. Two Recipes are published so an agent can buy verified data through the gateway without knowing that x402, escrow or Hedera exist. The second Recipe composes both sponsors: it buys Graph data through Receipt and proceeds only when the escrow verdict passes, which makes conditional settlement the control flow of the recipe rather than a detail inside it. An MCP server exposing buy_verified_data, verify_deal and get_deal does the same job for agents that speak MCP. TWO IMPLEMENTATIONS, ONE VERDICT. Hashing is JCS (RFC 8785) canonical JSON plus keccak256, chosen so an independent implementation can agree byte for byte. We proved that rather than asserting it: verify-py is Python with zero dependencies, its own Keccak-256, its own JCS canonicaliser, its own JSON Schema subset and its own JSONPath, written from SPEC.md rather than from our TypeScript. It reads the public topic and prints the same verdictHash the escrow recorded. Writing it also sharpened the spec, by surfacing that human-readable detail strings sit inside the hashed document and so must be pinned exactly for two implementations to agree. That is now specified in SPEC.md and asserted in CI, and it is precisely the class of ambiguity only a second implementation can find. The escrow itself is verified on Sourcify with a full match, so the deployed bytecode provably matches the published source. THE HACKY PART. The demo film has no video editor behind it. demo/film.html is a page where the entire film is a pure function of time: seek(ms) computes every pixel of state from the clock and returns a signature of what it painted, so the renderer captures a frame only when that signature changes and turns the rest into a longer duration in an ffmpeg concat list. A scene that holds still for twelve seconds costs one screenshot instead of three hundred and sixty. It drives headless Chrome over the DevTools Protocol using the WebSocket client built into Node, with no dependencies at all. The narration is generated the same way, timed from the same schedule the renderer wrote, so the script and the film cannot drift apart. The same determinism the project claims for its verdicts is what makes the film cheap to render and impossible to fudge, because every figure on screen is pulled from a real captured run rather than typed in by hand.