Circle Gateway nanopayments batch settlement, so a buyer gets no per-payment receipt on chain. This page restores one for purchases on Arc: each signed Gateway authorization is checked against Circle Gateway’s record: a batch settlement and the buyer’s balance change or, for a seller that declares a prepaid balance, settled top-ups that cover the call. A Merkle root of what each seller returned is recorded on Arc before anything is published.
Round 3 · 2026-09-28 · ledger 0x7238b5ccaadf3bce753152a6cfc298431e0a83c8 · anchor tx 0xbd6a647c6f1c6fbb…
| Arc x402 listings payable through Circle Gateway (Circle Discovery API) | 482 |
| after the safety rules (GET/POST, ≤ $1, no state-changing endpoint, no unfilled path id) | 335 |
| answering a live 402 with a Gateway option on Arc | 322 |
| with every required input either not needed or given an example by the seller itself | 99 |
| paid (signed authorization sent) in this round | 84 |
| charged by the seller (a transfer exists in Circle Gateway) | 51 |
| delivered (charged, and the response passed all four checks) | 42 |
| could not tell (charged and failed a check, on a call sent with no input) | 9 |
“Paid” counts signed authorizations we sent. “Charged” counts those Circle Gateway recorded as transfers. A seller that refuses a bad request without charging is not penalised. “Could not tell” counts calls the seller charged that then failed a check, where we had sent no input. We send a GET with no input when the seller’s live 402 gives no request values to send and its listing marks no input as required. A buyer who sends the right input may get a different result, so these calls are shown with the HTTP status the seller returned and left out of the rate. A 5xx or no answer stays in the rate. The delivery rate is over every other charged call (“judged”). Calls sent with no input that delivered are still counted, so read the rate together with the “Could not tell” column. payment_not_accepted means the seller answered 402 again to a signed payment and charged nothing. Host names are shown after each host has had 72 hours with its own results.
| Seller | Paid | Charged | Delivered of judged | Charged, not delivered | Could not tell | Not charged |
|---|---|---|---|---|---|---|
| np.orthogonal.com | 33 | 0 | – (0/0) | – | – | payment_not_accepted 33. Re-tested in round 4, below |
| api.aisa.one | 32 | 32 | 100% (23/23) 23/32 of all charged calls | – | 9 (HTTP 200 with 0 bytes: 9) | – |
| edge.goldsky.com | 11 | 11 | 100% (11/11) | – | – | – |
| nano.blockrun.ai | 7 | 7 | 100% (7/7) | – | – | – |
| api.exa.ai | 1 | 1 | 100% (1/1) | – | – | – |
re-test of np.orthogonal.com after the seller reported a fix (2026-09-29). Anchored in tx 0x18d026a268a2ec3a….
| Seller | Paid | Charged | Delivered of judged | Could not tell | Other outcomes |
|---|---|---|---|---|---|
| np.orthogonal.com | 47 | 47 | 100% (33/33) 33/47 of all charged calls | 14 (HTTP 404: 4, HTTP 400: 9, HTTP 422: 1) | – |
GatewayWalletBatched on Arc): nonce 0xb973c86a716d0253aef8a33f504f4c13097722ceca40e428d517994ebcb5eb5b, 0.1 USDC to 0xBd7b9f3e0CD3E1f6e698D0eeBb99F96E093BdeE3.0x6ae70bf1ac146895…; in that tx the buyer’s Gateway balance fell by exactly the sum of its charged authorizations (checked by verify.mjs from Arc RPC).application/json; charset=utf-8, sha256 0xd4a387cd5cc32e213f66c4a9493bef39bdf8f9be0226dec60c534d68f0ee0606.0x23ce49fe5d8d2d563c8d298206ddf97dd2ea678f0a8d374eb03c7c6bffcc3d3c; its Merkle proof leads to the root stored in DeliveryLedger round 3.// GET https://kzmttkc.github.io/arc-delivery-receipts/api/v1/arc/sellers/<payTo>.json → { deliveryRate, judged, couldNotTell, couldNotTellResponses, charged, purchases, lastChecked, evidence[] }
client.onBeforePaymentCreation(async (ctx) => {
const payTo = ctx.selectedRequirements.payTo.toLowerCase();
const r = await fetch(`https://kzmttkc.github.io/arc-delivery-receipts/api/v1/arc/sellers/${payTo}.json`);
if (!r.ok) return; // never checked: your call
const s = await r.json();
if (s.couldNotTell > 0) console.warn(`${payTo} charged ${s.couldNotTell} calls sent with no input and returned no usable result. Its listing may not say which input it needs, so read the seller's own docs.`, s.couldNotTellResponses);
if (s.couldNotTell >= 3 && s.delivered === 0) console.warn(`${payTo} charged ${s.couldNotTell} calls and delivered none. Do not call it without the input its docs require.`);
if (s.judged < 3 || s.deliveryRate === null) return; // no or too few judged calls: the rate says nothing either way, your call
if (s.deliveryRate < 0.8) {
return { abort: true, reason: `delivery rate ${s.deliveryRate} over ${s.judged} judged calls` };
}
});
The same per-seller history is the kind of data an agent-transaction insurer would need to price a policy.
git clone https://github.com/kzmttkc/arc-delivery-receipts && cd arc-delivery-receipts && npm ci node verify.mjs public/rounds/3/records.json
What it proves and what it does not: it proves which authorizations the buyer signed, that Circle settled them in batches that debited the buyer by exactly those amounts, and which bytes each seller returned, fixed on Arc before publication. Seller-side credits are netted with other buyers in the same batch, so they can be checked only in aggregate. A “delivered” result says the response passed four published checks, not that its content was useful.
Method: METHOD.md · Code and records: github.com/kzmttkc/arc-delivery-receipts. Independent of Circle; not endorsed by Circle or Arc.