x402 Example Endpoints
Real x402 example endpoints on a live testnet, grouped by bug class — from a fully compliant flow to every documented way a payment challenge can go wrong.
Encoding (4)
- GET /api/x402/header-raw-json — Sends the PAYMENT-REQUIRED requirements as raw JSON instead of base64.
- GET /api/x402/header-bad-base64 — The PAYMENT-REQUIRED header value is not valid base64.
- GET /api/x402/header-base64-not-json — The PAYMENT-REQUIRED header is valid base64 but decodes to bytes that are not JSON.
- GET /api/x402/amount-as-number — Emits the atomic amount as a JSON number instead of a string.
Header format (15)
- GET /api/x402/compliant-body-get — Returns a valid x402 challenge in the response body only (no PAYMENT-REQUIRED header). Clients that read the challenge from the JSON body rather than decoding the base64 header must handle this path.
- POST /api/x402/compliant-body-post — The body-primary happy path for a POST endpoint. Challenge is delivered in the JSON response body only, no PAYMENT-REQUIRED header.
- GET /api/x402/status-401 — Gates the resource with 401 Unauthorized instead of 402 Payment Required.
- GET /api/x402/status-403 — Gates the resource with 403 Forbidden instead of 402.
- GET /api/x402/status-400 — Gates the resource with 400 Bad Request instead of 402.
- GET /api/x402/status-200-leak — Serves the protected content for free with a 200 and no payment gate.
- GET /api/x402/missing-payment-required — Returns a 402 but omits the PAYMENT-REQUIRED header entirely.
- GET /api/x402/missing-x402-version — The PAYMENT-REQUIRED body omits the x402Version field.
- GET /api/x402/legacy-v1-headers — Emits the legacy v1 X-PAYMENT header instead of the v2 PAYMENT-REQUIRED challenge.
- GET /api/x402/empty-accepts — The PAYMENT-REQUIRED challenge advertises an empty accepts array.
- GET /api/x402/missing-resource — The PAYMENT-REQUIRED challenge omits the resource object.
- GET /api/x402/sensitive-metadata — Leaks fake PII into the resource description before any payment.
- GET /api/x402/eip3009-domain-version-mismatch — Advertises extra.version = "1" when the token's EIP-712 domain version is something else, so the payer signs under a domain the token never uses.
- GET /api/x402/missing-payment-response — Settles and returns 200 but omits the PAYMENT-RESPONSE header.
- GET /api/x402/wrong-transfer-method — Sets extra.assetTransferMethod to something other than permit2, so the facilitator can't move funds.
Network mismatch (8)
- GET /api/x402/unsupported-scheme — Advertises a payment scheme the facilitator does not support.
- GET /api/x402/wrong-network-mainnet — Advertises a mainnet network id while the facilitator runs on testnet.
- GET /api/x402/wrong-token — Advertises an asset contract address that is not SBC.
- GET /api/x402/wrong-payto — Advertises a payTo address that is not the merchant's wallet.
- GET /api/x402/unsupported-network-facilitator — Advertises a network the facilitator does not handle.
- GET /api/x402/multi-network-accepts — Advertises two EVM entries — Base Sepolia first, Radius testnet second — so the client must match by network instead of taking accepts[0].
- GET /api/x402/svm-requirement — Advertises only a Solana (solana:*) requirement, which an EVM testnet client cannot satisfy.
- GET /api/x402/cross-vm-accepts — Advertises a Solana entry and a Radius testnet EVM entry together, forcing the client to select the VM and network it can settle on.
Amount / price (5)
- GET /api/x402/wrong-decimals — Expresses the amount with 18 decimals for a token that has 6.
- GET /api/x402/expired-quote — Sets maxTimeoutSeconds to 0, so the quote is dead on arrival.
- GET /api/x402/overlong-validity — Sets maxTimeoutSeconds to a full year, widening the replay window.
- GET /api/x402/changing-price — Increases the advertised amount on every request.
- GET /api/x402/dollar-string-price — Advertises the amount as a human dollar string instead of atomic units.
Verify / settle (8)
- GET /api/x402/skip-verify — Goes straight to /settle without a /verify pre-check.
- GET /api/x402/verify-only-no-settle — Grants access once /verify succeeds and never calls /settle.
- GET /api/x402/serve-before-settle — Returns 200 immediately and settles in the background.
- GET /api/x402/facilitator-settle-500 — /verify succeeds but /settle returns 500 Internal Server Error, leaving it unknown whether the payment landed on-chain.
- GET /api/x402/facilitator-missing-txhash — /settle reports success: true but omits every transaction-hash field; the PAYMENT-RESPONSE receipt carries no on-chain proof.
- GET /api/x402/facilitator-schema-drift — The facilitator returns { valid: true } instead of the spec's { isValid: true }; the merchant's loose field-name check grants access on the wrong key.
- GET /api/x402/facilitator-wrong-payto-at-settle — The merchant calls /settle with a payTo address different from the one advertised in the challenge; the facilitator rejects it because the payment was signed for a different recipient.
- GET /api/x402/settle-then-fail — The payment settles on-chain, then the handler returns 500.
Replay / idempotency (4)
- GET /api/x402/replay-after-success — Once a payment settles, the same payload re-serves the resource forever with no re-settle.
- GET /api/x402/eip3009-replayed-nonce — After settling successfully, the merchant submits the identical authorization (same from + nonce) a second time. ERC-3009 replay protection lives in the token, so the second attempt cannot move funds.
- GET /api/x402/facilitator-settle-concurrent-race — Two concurrent requests carry the same PAYMENT-SIGNATURE; without a per-permit lock the merchant lets both race to /settle. The facilitator accepts only one — the second racer is charged but gets a confusing 402.
- GET /api/x402/edge-conditional-304 — The paid resource carries an ETag, and a later conditional request is answered 304 from cache without re-payment.
Fail-open / fail-closed (9)
- GET /api/x402/no-facilitator-check — Grants access on the mere presence of a PAYMENT-SIGNATURE header.
- GET /api/x402/trust-client-response — Believes a client-asserted payment result without checking the facilitator.
- GET /api/x402/fail-open-timeout — Serves the resource anyway when the facilitator is unreachable.
- GET /api/x402/dead-facilitator — Points /verify and /settle at a facilitator host that does not exist.
- GET /api/x402/facilitator-rate-limited — The facilitator returns 429 during /verify; the merchant misclassifies it as a payment failure and returns 402 instead of 503 + Retry-After.
- GET /api/x402/facilitator-soft-down-503 — The facilitator is reachable but returns 503 Service Unavailable — it is not a dead URL, just temporarily down.
- GET /api/x402/facilitator-non-json-leaks — The facilitator returns an HTML maintenance page instead of JSON; the merchant forwards the raw HTML in the 402 response body, leaking internal infrastructure details.
- GET /api/x402/facilitator-no-verify-timeout — The merchant's /verify fetch has no AbortController or timeout; when the facilitator is slow the handler ties up the connection forever.
- GET /api/x402/facilitator-already-settled-grants — When /settle returns a failure (e.g., already used), the merchant grants access anyway — a replay attacker can reuse any previously-rejected permit for free.
Edge / CDN (11)
- GET /api/x402/cors-hides-headers — Does not expose the PAYMENT-REQUIRED and PAYMENT-RESPONSE headers via CORS.
- GET /api/x402/head-leak — A HEAD request returns 200 with a leak header and no payment.
- GET /api/x402/edge-cacheable-challenge — Sends the 402 with Cache-Control: public, max-age, so a shared cache stores and replays a stale challenge.
- GET /api/x402/edge-cacheable-paid — Sends the paid 200 publicly cacheable, so a shared edge serves paid content to unpaid clients on the same URL.
- GET /api/x402/edge-missing-vary — Caches the paid 200 but ignores the payment header in the cache key (no Vary), so one payer's response is served to another.
- GET /api/x402/edge-swr-leak — A stale-while-revalidate directive lets the edge serve cached protected content without payment during revalidation.
- GET /api/x402/edge-oversized-header — Pads the PAYMENT-REQUIRED header beyond common proxy header-size caps, so an edge truncates, drops, or 431s it.
- GET /api/x402/edge-rate-limited — Returns 429 (short Retry-After, no challenge) before the 402, so a willing payer can't tell a rate limit from a paywall.
- GET /api/x402/edge-waf-blocks-payment — The large base64 PAYMENT-SIGNATURE trips a managed WAF rule and is rejected with 403 before the payment logic runs.
- POST /api/x402/edge-body-too-large — A paid POST is rejected by a small edge body cap with 413 before reaching the payment logic.
- GET /api/x402/edge-timeout-settle — Settlement is slow and the edge returns 504 while the charge may have completed on-chain — an ambiguous paid state.
Client policy (4)
- GET /api/x402/overpriced-vs-cap — Advertises a price far above a sane per-call cap that an MCP policy gate would enforce.
- GET /api/x402/resource-host-mismatch — The advertised resource origin differs from the server actually being called.
- GET /api/x402/tool-operation-mismatch — The requirement authorizes a different, more privileged tool than the one being requested.
- GET /api/x402/session-price-escalation — The price climbs call over call (1x, 2x, 3x...) to defeat a per-session spend limit.
Scheme variants (11)
- POST /api/x402/upto-correct — Authorizes a maximum but settles only the actual usage (~60%).
- POST /api/x402/upto-charges-max — Settles the full authorized max even when usage is lower.
- POST /api/x402/upto-override-exceeds-max — Sets the settlement amount above the authorized maximum.
- POST /api/x402/upto-zero-free — A settlement override of 0 gives the service away by accident.
- POST /api/x402/upto-missing-facilitator-address — Advertises an upto challenge whose extra carries only name and version — the facilitatorAddress the scheme requires is missing.
- GET /api/x402/eip3009-exact-get — A fully compliant exact endpoint that advertises the EIP-3009 asset transfer method: the payer signs the token's own TransferWithAuthorization and no Permit2 approval exists anywhere in the flow.
- POST /api/x402/eip3009-exact-post — The POST counterpart: a compliant exact/EIP-3009 endpoint that settles a signed TransferWithAuthorization and returns the resource.
- GET /api/x402/dual-transfer-method-accepts — On a chain whose asset supports both, advertises one accepts entry per exact transfer method — EIP-3009 and Permit2 — at the same price on the same network, so the client must read extra.assetTransferMethod rather than assume the chain's default.
- POST /api/x402/batch-settlement-correct — Advertises the batch-settlement scheme: the client authorizes a 5x deposit, and the server settles only the actual (much lower) usage.
- POST /api/x402/batch-settlement-overcharge-deposit — Settles more than the deposit the client authorized under the batch scheme.
- POST /api/x402/batch-settlement-never-reconciles — Holds the full authorized deposit and never settles the lower real usage.