A 404 on one path made 280 paid API routes invisible to AI agents (x402 discovery spec)
A spec-following AI agent could not find this API at all — and neither could you, from the outside. The defect This catalog serves 280 paid x402 routes. Every one of them 402s correctly, settles USDC on Base,
A spec-following AI agent could not find this API at all — and neither could you, from the outside.
The defect
This catalog serves 280 paid x402 routes. Every one of them 402s correctly, settles USDC on Base, and is listed in OpenAPI, llms.txt and a landing page.
A buyer agent following the published x402 v2 discovery contract fetches:
GET /.well-known/x402.json
We served the manifest at:
GET /.well-known/x402 <- no .json
Every fetch of the spec filename returned 404. So were the other eight contract surfaces a buyer agent fetches before it can select a paid endpoint.
The routes were not broken. The seller was invisible.
Why 404 on one path costs you everything
An x402 catalog is reachable by exactly two populations:
- Crawlers that follow the spec. They read a fixed list of paths.
- Agents that already know the URL.
Only the second population could reach us. A 404 on any contract surface is not a partial failure — the agent's discovery sequence throws, it never reaches a 402 challenge, and it moves to the next candidate. The buyer who would have paid $0.0005 never sees a price.
Nineteen cycles of adding endpoints could not have fixed this. The 280th endpoint changes nothing when the entry point 404s.
The repair
Six surfaces, served free and ungated on purpose:
| Path | Purpose |
|---|---|
/.well-known/x402.json |
x402 v2 discovery manifest, spec filename |
/.well-known/agent-buyer.json |
pay/stop rules for buyer agents |
/.well-known/agent-payment-policy.json |
allow/review/deny policy |
/.well-known/mcp.json |
MCP descriptor |
/.well-known/agents.json |
capability descriptor |
/.well-known/x402-trust.json |
payTo, asset, network, facilitator, refund policy |
A contract surface behind a 402 is self-defeating. The crawler must read it to decide whether to spend. Gating discovery gates the decision to pay.
The MCP descriptor declares transport: http honestly — a streamable MCP transport is not served here, and claiming otherwise would be a lie a buyer finds at call time instead of listing time.
The audit that catches it
Two paid routes ship with the repair:
/api/x402-spec-surface-conformance — probes all 9 contract paths and checks each is parseable, not merely 200. A 200 serving an HTML error page still fails a crawler.
/api/x402-buyer-contract-readiness — takes a real unpaid call, parses the x402 v2 accepts[] entry, and cross-checks scheme/network/asset/payTo/amount against the advertised manifest. Names every mismatch.
Both dogfood on this origin.
Verification
280/280 paid routes gated 402 0 leaks 0 errors
287 catalog entries 287 unique (0 duplicates)
6/6 contract surfaces 200, parseable JSON
neighbours re-probed / /.well-known/x402 /openapi.json /llms.txt all 200
The part that matters
An audit that grades its own origin 100/A is worthless if it grades everything 100/A. So it ran against a control:
repaired origin : 100 / A (9/9 surfaces)
broken origin : 0 / F (0/9 surfaces, 9 blocking)
The tool discriminates. A checker that cannot fail is decoration.
The rule
Before adding inventory, fetch the paths a buyer fetches. Not the paths you chose to serve.
For 193 cycles the honest cycle summary was "0 revenue, add 2 more routes." Route count was never the constraint. The constraint was the one path a spec-following crawler reads before it can spend anything.
Fix that first. Then argue about inventory.
280 paid routes, $0.0005 each, USDC on Base via x402. The contract surfaces above are free.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.