x402 Preflight · blog · troubleshooting
My x402 resource is listed in the Bazaar but buyers say it does not work
A listing is not a server. The catalogue publishes what you declared; the buyer experiences what your route does at that moment. When the two drift apart, nothing errors — the resource simply stops selling, and the only trace is an agent that gives up.
This is the order that finds the cause fastest. Each step is one request or one file, no wallet needed, except the last.
Step 0 — Reproduce the buyer's request, not yours
You have probably been testing with your own script, your own headers, maybe your own cookie. A buyer arrives with none of that: no account, no API key, no wallet yet. Reproduce exactly that.
curl -s -i https://your-api.example.com/paid/route
No -H flags, no auth. Read two things: the status code and whether the response carries a challenge. Everything below depends on which of those two came back.
Step 1 — Is the status a 402?
| You see | What it means | Where to look |
|---|---|---|
402 | Good. The paywall is reached. Go to step 2. | — |
200 | The payload is free. Either the middleware is not on this route or something answered before it. | Route order, CDN cache, static file shadowing the path, a proxy |
401 / 403 | An auth gate sits before the payment layer. The agent has no key, so it never reaches the price. | Middleware order: payment first, auth after |
405 | The verb is wrong — usually the listing advertises one verb and the paid route uses another. | Step 2 |
404 / 410 | The route moved or was retired; the listing stayed. | Refresh the discovery entry after deploy |
5xx | Crash, bad deploy, capacity or maintenance. | Your logs, not your config |
If the challenge is there but unreadable — a 402 with an empty or non-standard body and no PAYMENT-REQUIRED header — treat it as a missing 402. From the agent's side a closed door and no door are the same.
Step 2 — Does the verb match the listing?
This is the most common silent breakage. The catalogue says GET, your handler charges on POST; the agent follows the catalogue, gets a 405, and stops before it ever sees a price. A 405 with Allow: POST is a hint, not a payment request — a well-built checker will follow it once, but a buyer agent that trusts the listing will not.
Two honest fixes: publish the verb that charges, or answer the advertised verb with the challenge. What does not work is documenting the difference somewhere in your docs and hoping the agent reads it.
Step 3 — Does the price match what the listing promised?
Decode the challenge and compare four values against the catalogue entry: amount, network, asset and payTo. A buyer with a spend policy will refuse a price above the listed one, and a payout address that changed without the listing being updated is indistinguishable from a scam from the outside.
curl -s -X POST https://x402.chikocorp.com/api/preflight/inspect \
-H 'content-type: application/json' \
-d '{"resource_url":"https://your-api.example.com/paid/route","method":"GET","max_price_usd":1}'
The response gives you payment.priceUsd, network, asset, payTo and a per-check verdict. If those disagree with the listing, the listing is wrong — fix the source of truth, then wait for the crawler.
Step 4 — Is the Bazaar extension actually valid?
The discovery extension is what lets an agent know how to call you before it pays: an example input and a schema. It is not decoration. If it is missing, the agent knows the price but not the call signature, and a careful buyer will not spend on a request it cannot construct.
Check three things in the 402 response: extensions.bazaar.info (a concrete example), extensions.bazaar.schema (a JSON Schema for the input and output), and that the example validates against the schema. The reference is the Bazaar extension spec and the x402 v2 specification.
Step 5 — Does the facilitator settle?
Everything above is the seller's surface. This step is the other half: a correct challenge that the facilitator rejects still ends in no sale, and the buyer sees a failed payment with no explanation.
Test it with a funded wallet for the smallest amount your route sells. Confirm three facts in order: the facilitator accepts the signature, the transfer lands at payTo, and your handler delivers the resource after settlement. Coinbase documents the seller flow in the x402 seller quickstart and the failure modes in troubleshooting.
This is the only step that costs money, and the only one no read-only checker can do for you.
Step 6 — What sits in front of your route?
When steps 1 to 5 all pass and buyers still fail, the problem is usually an intermediary that is invisible from your application logs:
- A CDN cache. Cached
200responses get served to agents that should have received a challenge. - A WAF or bot rule. Agents with no browser fingerprint get blocked before payment; the seller sees nothing.
- A redirect. The challenge survives a 301 only if the agent follows it and the header is preserved.
- A proxy with small header buffers. A large challenge header — ours is around 14 KB — can be dropped or turned into a 502 by a proxy with default limits.
- Compression middleware. Rare, but it can mangle a base64 header.
Test from outside your network, with the same path a buyer would use, and compare the headers your origin emits with the headers the internet receives. If they differ, you have found it.
The pre-listing checklist
Run this before you list, and again after every deploy that touches payment:
- The listed verb returns
402to an unauthenticated request. - The challenge is readable: amount, network, asset,
payTo. - The price on the wire equals the price in the listing.
extensions.bazaaris present, valid, and its example validates against its schema.- No CDN, WAF or auth gate intercepts the listed path.
- One real purchase settles and delivers, at the smallest amount you sell.
Frequently asked questions
Why does my endpoint answer 200 when the Bazaar says it costs money?
The payment middleware is not in the path of that verb, or something answers first: a static file, a CDN cache, a proxy or an auth gate. The listing describes an intention; the response is what actually happens.
The listing says GET but my route charges on POST. What do buyers see?
They take the GET, receive a 405 or a free 200, and stop. An agent follows the listing, not your implementation.
How long does a corrected listing take to propagate?
The discovery layer is a crawler, not a live proxy. After you fix a route or a price, the catalogue may keep serving the old entry until it recrawls. Verify your endpoint, not the listing, and expect a delay.
Is a healthy challenge enough to sell?
No. It proves a buyer can construct a payment. It does not prove settlement or delivery — those need a real, funded transaction.
Check an endpoint now — free Which tool to use for each problem
How common are these failures? We probed 26,692 endpoints in the Bazaar and counted them.