When an AI agent decides not to pay, the seller never hears why
More and more AI agents and wallets check an x402 endpoint before they pay it: is the price within the budget, is the payout address the one it was yesterday, can this wallet pay on that network at all? When the answer is no, the agent simply doesn't pay. The seller sees a 402 go out and nothing come back. From the seller's side, a refused offer looks exactly like a client that went away, so they can't tell "my price is above common limits" from "my payout address changed and wallets stopped trusting it" from "my challenge is broken".
Why buyers stop
x402 Doctor runs the same checks an agent runs before it pays, on every endpoint in the x402 Trust Index, every day. These are the most common reasons a check gave in the scan of 1 November for a 402 a buyer could have paid, next to the refusal reason a buyer would send. (Endpoints that were down or answered no 402 are left out: there was nothing to refuse.)
| What the check found | Endpoints | Refusal reason |
|---|---|---|
| Only Circle Gateway, which standard x402 clients can't pay | 1,651 | network_unsupported |
| The 402 names another resource than the URL that was asked | 707 | terms_mismatch |
| A token that is not a known stablecoin | 176 | asset_unsupported |
| Testnet only | 160 | network_unsupported |
| The 402 asks more than the price the seller advertises | 83 | terms_mismatch |
| An invalid challenge | 16 | challenge_invalid |
| A very high or suspicious amount | 24 | policy_blocked |
| No option the buyer can pay | 13 | network_unsupported |
Real buyers add their own reasons on top: a spend limit per payment (price_above_limit), a payout address that changed since last time (payto_changed), a sanctions hit (screening_hit). Every one of these is something a seller can fix, if they hear about it.
How the refusal extension works
We proposed it as an open x402 extension, x402-foundation/x402#4087, so any wallet can send it and any seller can read it. Two steps:
1. The seller says where refusals go. One small entry in the 402's extensions:
"refusal": { "info": { "url": "https://seller.example/.well-known/x402-refusal", "version": "0.1-draft" } }
2. A buyer that doesn't pay posts a short report there, and doesn't wait for the answer:
{ "x402Version": 2, "resource": "https://seller.example/api/weather", "offer": { "hash": "<sha256 of the refused accepts[] entry>", "acceptIndex": 0 }, "reason": "price_above_limit", "detail": "limit $0.05, asked $0.25", "checkedAt": "2026-11-01T08:00:00Z" }
The offer hash is the sha256 of the refused accepts[] entry in canonical JSON (keys sorted, no spaces). The seller can tie the refusal to the exact terms it showed, without the buyer sending them back. The resource goes without its query string, so none of the buyer's input travels with it.
Protected against the obvious abuse
- No buyer identity. A report has no payer address. A wallet can sign one (EIP-191) when it wants the report to count as a real wallet's.
- Only to the seller's own site. Buyers send only to an https url on the same site as the resource. On shared hosting such as onrender.com or vercel.app it must be the exact same host. So a 402 can't turn buyers into a flood against someone else's server.
- Once a day. At most one report per resource and reason a day per buyer, with redirects refused.
- Hints, not commands. Sellers rate-limit the endpoint and never change prices or payout addresses because of unsigned reports.
Add it to your endpoint
In Express it is the extension entry in each paid route plus one small route:
app.post("/.well-known/x402-refusal", express.json({ limit: "4kb" }), (req, res) => { console.log("refusal", req.body?.reason, req.body?.resource); res.status(204).end(); });
Then run x402 Doctor's free check on your endpoint: it now says whether your 402 takes refusals, whether buyers would send to your url, and whether it answers. A full implementation of both sides (reason codes, offer hash, a seller endpoint with optional signature checks and counts) is open source in x402 Doctor's lib/refusal.js.
Who already uses it
- Taking refusals: x402 Doctor, presign-guard, Ichimoku Signal and Fizzl Buy. Each 402 names its own
/.well-known/x402-refusal. - Sending refusals: Fizzl Buy, which buys from other sellers for agents, when it skips a seller. The Fizzl Agent Wallet, when its own checks stop a payment before the owner is asked (never the owner's own "no"). And x402-safe-fetch 0.11 with
tellSellers: true.
It is a draft. Comments on the issue are very welcome, especially from sellers on which reason codes they would want.
How we counted
- Verdicts from the x402 Trust Index scan of 31 October 2026: 38,595 go, 2,572 caution, 906 no_go (42,073 endpoints with an answer; 247 unreachable left out). The scan of 1 November is skewed by one large seller that was down that morning (about 10,600 endpoints answering 502), so we use 31 October for the share.
- Reasons from the 1 November scan's summary, leaving out server errors, endpoints without a 402, unreachable ones and switched-off deployments. One endpoint can have more than one reason.
- These are x402 Doctor's checks, not counts of actual refusals by buyers: no x402 buyer reports refusals to sellers yet, which is the point of the extension.
Questions
What is the x402 refusal extension?
A draft x402 extension (x402-foundation/x402#4087). The seller's 402 names a refusal url on its own site; a buyer that decides not to pay posts a short report there: a reason code from a fixed list, the sha256 of the refused accepts[] entry, the resource without its query, and the time. Nothing is paid and nothing about the buyer is sent.
Can a competitor flood a seller with fake refusals?
Buyers only send to a url on the seller's own site, at most once a day per resource and reason, and sellers rate-limit the endpoint. Unsigned reports are hints that a seller should never act on automatically; a report signed by a wallet (EIP-191) shows a real wallet sent it.
How do I add it to my x402 endpoint?
Add extensions.refusal = { info: { url: "https://<your host>/.well-known/x402-refusal", version: "0.1-draft" } } to each paid route and answer POSTed reports there with 204. x402 Doctor's free check tells you whether it works.
Check your endpoint with x402 Doctor, check a payment before signing with presign-guard, and let an agent buy safely with the Fizzl Agent Wallet.