An x402 endpoint can answer in 400 ms, look healthy in every catalogue, and still cost you an afternoon. The advertised price is not always the charged price. The 402 challenge lives in more than one place, and clients of different generations read different ones. The paid response changes shape. A settlement happens and no delivery follows.
None of that is expensive to find before you pay. It is expensive to find after you have paid and wired the resource into your agent.
This is the six-check pre-flight I run against a resource — plus the values each check returns on a live production endpoint, measured today.
The six checks
1. The bare URL answers 402.
curl -s -o /dev/null -w '%{http_code} %{time_total}s\n' https://<host>/<path>
A catalogue verifier probes exactly the bare URL to decide whether a resource is x402 at all. If the endpoint needs a query parameter and answers 400 without it, the catalogue records a rejection and never reaches the price. Answer 402 first; validate the parameter after payment.
2. The challenge is in both places, with the same terms.
curl -si https://<host>/<path> | sed -n '1,40p'
Current clients parse the PAYMENT-REQUIRED response header (base64 JSON). Older clients read the JSON body. Serve both, and make price, asset, network and payTo agree. A body-only challenge locks out current clients; a header-only challenge locks out older ones.
3. The catalogue's listing agrees with the endpoint.
Ask the facilitator for the listing status of the resource and read lastWrite. listed is what a buyer sees, and the price the catalogue holds is the price the buyer is quoted. A resource listed at one price while the endpoint charges another is a dispute waiting to happen.
4. Asset, network, receiver and amount.
From accepts[]: asset is the USDC contract for the chain, network is the CAIP-2 id (eip155:8453 for Base), payTo is an address you control for that role, and the amount is in atomic units — six decimals for USDC. 1000 is 0.001 USDC; 5000000 is 5.00 USDC. Compare integers, never formatted strings.
5. The schema of the paid response.
Write down the top-level keys and their types, then compare with the previous delivery. A response that changed shape breaks the caller, not the callee — the failure surfaces in your code hours later, not at the request.
6. Delivery evidence.
Two questions, asked before you pay: has this seller delivered before — a named transaction hash, payer and block? And what happens on a settled-but-undelivered payment? Get the answer in writing. PAYMENT-RESPONSE on the paid 200 is the artefact to capture.
What the checks return on a live endpoint
I ran all six against a production x402 resource. Here is the raw shape of three of them.
The 402 challenge
HTTP/1.1 402 Payment Required
content-type: application/json; charset=utf-8
PAYMENT-REQUIRED: <base64 JSON, 2,804 characters>
The body is 7,762 characters of JSON carrying x402Version: 2 and a non-empty accepts[]:
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "1000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x6f60659390aDA1D02B14024d8c9d0C889f2F89c3",
"maxTimeoutSeconds": 60,
"extra": { "name": "USD Coin", "version": "2" }
}
Header and body carry the same terms. amount is 1000 atomic = 0.001 USDC, network is eip155:8453, and the extension list carries bazaar.
The catalogue's lastWrite
GET https://facilitator.payai.network/discovery/listing-status?resource=<resource>
{
"listed": true,
"hidden": false,
"lastProbe": { "at": "2026-10-14T15:14:04.424Z", "status": "alive", "httpStatus": 402 },
"lastWrite": { "status": "updated", "at": "2026-10-15T04:32:05.835Z", "source": "verify" }
}
lastWrite tells you how the listing got there — written by a verify call rather than by a settlement — and lastProbe shows the catalogue last saw a live 402.
The RPC's null receipt
POST https://mainnet.base.org
{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["0xabab...abab"]}
→ HTTP 200 {"jsonrpc":"2.0","id":1,"result":null}
A null receipt arrives as an HTTP 200, not as an error. A confirmer that reads one RPC once and gives up will call a real payment "not found". The order that works: read the receipt, then the transaction, then the block's transfer logs, before answering.
Ten minutes against an afternoon
Six checks, about a minute each, against a resource you are about to pay. Every one of them maps to a defect that has cost me real time: a required query parameter that made a catalogue reject a resource, a body-only challenge, a balance-gated verification, a stale receipt, and a silent schema change.
The full kit — the exact command for every check, a pass / investigate / fail threshold table, a one-page report template and a worked report against a live endpoint — is 5 USDC on Base, one payment and no account:
https://x402-catalog-history.ai-business-surface.workers.dev/preflight/buy
Top comments (0)