# x402 Marketplace

Reference catalog for HTTP 402 payment clients. Test your client against compliant endpoints and intentional protocol quirks, all settling real payments on the Radius testnet.

- **Compliant endpoints:** 21
- **Quirk endpoints:** 76
- **Network:** eip155:72344 (chain 72344)
- **Asset:** SBC `0x33ad9e4BD16B69B5BFdED37D8B5D9fF9aba014Fb` (6 decimals)
- **Facilitator:** https://facilitator.testnet.radiustech.xyz
- **Pay to:** `0x05354779F8E5397624928a461b97b190CAFcc23B`
- **Builder code (ERC-8021):** `grug402` — declared in every 402 challenge under `https://x402.org/extensions/builder-code` for on-chain attribution
- **Built by people at Radius (https://radiustech.xyz) — Built for Radius workshop. Stablecoin-native settlement infrastructure for the agentic internet.**

See the [Agent Readiness page](https://grug402.dev/agent-ready) for every machine-readable discovery surface.

## Happy Path

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Compliant | GET | [Canonical GET (real verify and settle)](https://grug402.dev/scenario/compliant-get.md) `/api/x402/compliant-get` | The textbook x402 flow: an unpaid GET gets a 402 with a PAYMENT-REQUIRED challenge, then the server runs a real /verify and /settle against the Radius testnet facilitator. |
| Compliant | POST | [Canonical POST with body](https://grug402.dev/scenario/compliant-post.md) `/api/x402/compliant-post` | The same compliant flow as the canonical GET, for a POST that carries a JSON body. |
| Compliant | GET | [Body-primary GET challenge](https://grug402.dev/scenario/compliant-body-get.md) `/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. |
| Compliant | POST | [Body-primary POST challenge](https://grug402.dev/scenario/compliant-body-post.md) `/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. |
| Compliant | GET | [Minimal price (1 atomic unit)](https://grug402.dev/scenario/cheapest.md) `/api/x402/cheapest` | A compliant endpoint priced at the smallest atomic unit, 0.000001 SBC. |
| Compliant | GET | [Single testnet accepts entry](https://grug402.dev/scenario/multi-accepts.md) `/api/x402/multi-accepts` | Advertises one testnet requirement that a correct client selects by matching its network. |
| Compliant | GET | [Correct idempotency (cached retry)](https://grug402.dev/scenario/idempotent-retry.md) `/api/x402/idempotent-retry` | Caches the result by payment id, so retrying the same payment returns the cached 200 without charging twice. |
| Compliant | GET | [Weather API (pay-per-call)](https://grug402.dev/scenario/weather-now.md) `/api/x402/weather-now` | A themed but fully compliant data endpoint, gated by a real settlement. |
| Compliant | GET | [FX rate API (pay-per-call)](https://grug402.dev/scenario/fx-rate.md) `/api/x402/fx-rate` | A themed compliant endpoint that returns an exchange rate after settlement. |
| Compliant | POST | [Haiku generator (POST, paid)](https://grug402.dev/scenario/haiku-generator.md) `/api/x402/haiku-generator` | A compliant POST endpoint that returns generated content after settlement. |
| Compliant | GET | [Joke API (pay-per-call)](https://grug402.dev/scenario/joke-api.md) `/api/x402/joke-api` | A compliant GET that returns a joke after a real settlement. |
| Compliant | GET | [Dictionary API (pay-per-call)](https://grug402.dev/scenario/dictionary-api.md) `/api/x402/dictionary-api` | A compliant GET that returns a definition after settlement. |
| Compliant | GET | [Quote API (pay-per-call)](https://grug402.dev/scenario/quote-api.md) `/api/x402/quote-api` | A compliant GET that returns a quote after settlement. |
| Compliant | GET | [MPP push happy path (EIP-3009)](https://grug402.dev/scenario/mpp-push-happy.md) `/api/mpp/mpp-push-happy` | Compliant MPP push flow: server issues WWW-Authenticate: Payment challenge, client submits EIP-3009 authorization credential, server validates structure and returns 200 with Payment-Receipt. |
| Compliant | GET | [MPP permit happy path (EIP-2612)](https://grug402.dev/scenario/mpp-permit-happy.md) `/api/mpp/mpp-permit-happy` | Compliant MPP permit flow: challenge requests an EIP-2612 permit credential. Client submits signed permit in Authorization: Payment, server validates and returns 200. |

## 402 / Challenge Construction

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Quirk | GET | [Returns 401 instead of 402](https://grug402.dev/scenario/status-401.md) `/api/x402/status-401` | Gates the resource with 401 Unauthorized instead of 402 Payment Required. |
| Quirk | GET | [Returns 403 instead of 402](https://grug402.dev/scenario/status-403.md) `/api/x402/status-403` | Gates the resource with 403 Forbidden instead of 402. |
| Quirk | GET | [Returns 400 instead of 402](https://grug402.dev/scenario/status-400.md) `/api/x402/status-400` | Gates the resource with 400 Bad Request instead of 402. |
| Quirk | GET | [Returns 200 with content (no gate)](https://grug402.dev/scenario/status-200-leak.md) `/api/x402/status-200-leak` | Serves the protected content for free with a 200 and no payment gate. |
| Quirk | GET | [402 without PAYMENT-REQUIRED header](https://grug402.dev/scenario/missing-payment-required.md) `/api/x402/missing-payment-required` | Returns a 402 but omits the PAYMENT-REQUIRED header entirely. |
| Quirk | GET | [PAYMENT-REQUIRED is raw JSON (not base64)](https://grug402.dev/scenario/header-raw-json.md) `/api/x402/header-raw-json` | Sends the PAYMENT-REQUIRED requirements as raw JSON instead of base64. |
| Quirk | GET | [PAYMENT-REQUIRED is invalid base64](https://grug402.dev/scenario/header-bad-base64.md) `/api/x402/header-bad-base64` | The PAYMENT-REQUIRED header value is not valid base64. |
| Quirk | GET | [Base64 decodes to non-JSON](https://grug402.dev/scenario/header-base64-not-json.md) `/api/x402/header-base64-not-json` | The PAYMENT-REQUIRED header is valid base64 but decodes to bytes that are not JSON. |
| Quirk | GET | [Atomic amount as a JSON number](https://grug402.dev/scenario/amount-as-number.md) `/api/x402/amount-as-number` | Emits the atomic amount as a JSON number instead of a string. |
| Quirk | GET | [Missing x402Version](https://grug402.dev/scenario/missing-x402-version.md) `/api/x402/missing-x402-version` | The PAYMENT-REQUIRED body omits the x402Version field. |
| Quirk | GET | [Legacy v1 headers (X-PAYMENT)](https://grug402.dev/scenario/legacy-v1-headers.md) `/api/x402/legacy-v1-headers` | Emits the legacy v1 X-PAYMENT header instead of the v2 PAYMENT-REQUIRED challenge. |
| Quirk | GET | [Empty accepts array](https://grug402.dev/scenario/empty-accepts.md) `/api/x402/empty-accepts` | The PAYMENT-REQUIRED challenge advertises an empty accepts array. |
| Quirk | GET | [Unsupported payment scheme](https://grug402.dev/scenario/unsupported-scheme.md) `/api/x402/unsupported-scheme` | Advertises a payment scheme the facilitator does not support. |
| Quirk | GET | [Advertises mainnet, settles on testnet](https://grug402.dev/scenario/wrong-network-mainnet.md) `/api/x402/wrong-network-mainnet` | Advertises a mainnet network id while the facilitator runs on testnet. |
| Quirk | GET | [Wrong token contract address](https://grug402.dev/scenario/wrong-token.md) `/api/x402/wrong-token` | Advertises an asset contract address that is not SBC. |
| Quirk | GET | [Wrong decimals (18 instead of 6)](https://grug402.dev/scenario/wrong-decimals.md) `/api/x402/wrong-decimals` | Expresses the amount with 18 decimals for a token that has 6. |
| Quirk | GET | [Wrong payTo address](https://grug402.dev/scenario/wrong-payto.md) `/api/x402/wrong-payto` | Advertises a payTo address that is not the merchant's wallet. |
| Quirk | GET | [Missing resource URL](https://grug402.dev/scenario/missing-resource.md) `/api/x402/missing-resource` | The PAYMENT-REQUIRED challenge omits the resource object. |
| Quirk | GET | [Already-expired quote (timeout 0)](https://grug402.dev/scenario/expired-quote.md) `/api/x402/expired-quote` | Sets maxTimeoutSeconds to 0, so the quote is dead on arrival. |
| Quirk | GET | [Overlong validity window (1 year)](https://grug402.dev/scenario/overlong-validity.md) `/api/x402/overlong-validity` | Sets maxTimeoutSeconds to a full year, widening the replay window. |
| Quirk | GET | [Price changes every request](https://grug402.dev/scenario/changing-price.md) `/api/x402/changing-price` | Increases the advertised amount on every request. |
| Quirk | GET | [Dollar-string price ($0.0001)](https://grug402.dev/scenario/dollar-string-price.md) `/api/x402/dollar-string-price` | Advertises the amount as a human dollar string instead of atomic units. |
| Quirk | GET | [Sensitive data in metadata](https://grug402.dev/scenario/sensitive-metadata.md) `/api/x402/sensitive-metadata` | Leaks fake PII into the resource description before any payment. |
| Quirk | GET | [Wrong asset transfer method](https://grug402.dev/scenario/wrong-transfer-method.md) `/api/x402/wrong-transfer-method` | Sets extra.assetTransferMethod to something other than permit2, so the facilitator can't move funds. |
| Quirk | GET | [Advertises a zero price but still gates](https://grug402.dev/scenario/zero-price-gated.md) `/api/x402/zero-price-gated` | Advertises an amount of 0 yet still demands a payment header. |
| Quirk | GET | [MPP challenge returns 401 instead of 402](https://grug402.dev/scenario/mpp-wrong-status.md) `/api/mpp/mpp-wrong-status` | Issues the MPP payment challenge with HTTP 401 Unauthorized instead of the required 402 Payment Required. |
| Quirk | GET | [MPP 402 without WWW-Authenticate](https://grug402.dev/scenario/mpp-missing-authenticate.md) `/api/mpp/mpp-missing-authenticate` | Returns 402 but omits the WWW-Authenticate: Payment header entirely. The client receives a payment signal but no challenge to respond to. |
| Quirk | GET | [WWW-Authenticate challenge is raw JSON (not Payment scheme)](https://grug402.dev/scenario/mpp-raw-json-authenticate.md) `/api/mpp/mpp-raw-json-authenticate` | Emits WWW-Authenticate with the challenge embedded as raw JSON instead of the "Payment method=\"evm/charge\" challenge=\"<base64>\"" format. |
| Quirk | GET | [WWW-Authenticate uses Bearer instead of Payment scheme](https://grug402.dev/scenario/mpp-bearer-authenticate.md) `/api/mpp/mpp-bearer-authenticate` | Returns WWW-Authenticate: Bearer realm="..." — the OAuth scheme — instead of the Payment scheme. MPP clients have no credential type to submit. |

## Verification Gaps

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Quirk | GET | [Skips /verify, settles directly](https://grug402.dev/scenario/skip-verify.md) `/api/x402/skip-verify` | Goes straight to /settle without a /verify pre-check. |
| Quirk | GET | [Grants on /verify, never settles](https://grug402.dev/scenario/verify-only-no-settle.md) `/api/x402/verify-only-no-settle` | Grants access once /verify succeeds and never calls /settle. |
| Quirk | GET | [No verification at all](https://grug402.dev/scenario/no-facilitator-check.md) `/api/x402/no-facilitator-check` | Grants access on the mere presence of a PAYMENT-SIGNATURE header. |
| Quirk | GET | [Trusts client-supplied result](https://grug402.dev/scenario/trust-client-response.md) `/api/x402/trust-client-response` | Believes a client-asserted payment result without checking the facilitator. |
| Quirk | GET | [Fails open when the facilitator is down](https://grug402.dev/scenario/fail-open-timeout.md) `/api/x402/fail-open-timeout` | Serves the resource anyway when the facilitator is unreachable. |

## Replay / Idempotency / Race

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Quirk | GET | [Replay after success (no binding)](https://grug402.dev/scenario/replay-after-success.md) `/api/x402/replay-after-success` | Once a payment settles, the same payload re-serves the resource forever with no re-settle. |
| Quirk | GET | [Serves before settlement (verify/settle gap)](https://grug402.dev/scenario/serve-before-settle.md) `/api/x402/serve-before-settle` | Returns 200 immediately and settles in the background. |

## Web / HTTP Layer

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Quirk | GET | [CORS hides payment headers](https://grug402.dev/scenario/cors-hides-headers.md) `/api/x402/cors-hides-headers` | Does not expose the PAYMENT-REQUIRED and PAYMENT-RESPONSE headers via CORS. |
| Quirk | GET | [HEAD reveals paid metadata](https://grug402.dev/scenario/head-leak.md) `/api/x402/head-leak` | A HEAD request returns 200 with a leak header and no payment. |

## Schemes (exact / upto / batch)

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Compliant | POST | [upto: settles actual usage](https://grug402.dev/scenario/upto-correct.md) `/api/x402/upto-correct` | Authorizes a maximum but settles only the actual usage (~60%). |
| Quirk | POST | [upto: always charges the max](https://grug402.dev/scenario/upto-charges-max.md) `/api/x402/upto-charges-max` | Settles the full authorized max even when usage is lower. |
| Quirk | POST | [upto: override exceeds the max](https://grug402.dev/scenario/upto-override-exceeds-max.md) `/api/x402/upto-override-exceeds-max` | Sets the settlement amount above the authorized maximum. |
| Quirk | POST | [upto: settles zero (free)](https://grug402.dev/scenario/upto-zero-free.md) `/api/x402/upto-zero-free` | A settlement override of 0 gives the service away by accident. |
| Compliant | POST | [batch-settlement: deposit, then reconcile usage](https://grug402.dev/scenario/batch-settlement-correct.md) `/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. |
| Quirk | POST | [batch-settlement: settles beyond the deposit](https://grug402.dev/scenario/batch-settlement-overcharge-deposit.md) `/api/x402/batch-settlement-overcharge-deposit` | Settles more than the deposit the client authorized under the batch scheme. |
| Quirk | POST | [batch-settlement: never reconciles down](https://grug402.dev/scenario/batch-settlement-never-reconciles.md) `/api/x402/batch-settlement-never-reconciles` | Holds the full authorized deposit and never settles the lower real usage. |
| Compliant | GET | [exact: canonical scheme identifier (client sends the wrong one)](https://grug402.dev/scenario/client-scheme-mismatch.md) `/api/x402/client-scheme-mismatch` | A fully compliant exact endpoint. The scheme identifier in the payment payload MUST be the canonical "exact" — a client that instead sends a non-canonical name like "exact-evm" is rejected by the facilitator with invalid_exact_evm_scheme even though its signature recovers a valid payer. |

## Multi-Network Selection (MCP)

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Compliant | GET | [Genuine multi-entry accepts (match by network)](https://grug402.dev/scenario/multi-network-accepts.md) `/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]. |
| Quirk | GET | [SVM-only requirement (solana:*)](https://grug402.dev/scenario/svm-requirement.md) `/api/x402/svm-requirement` | Advertises only a Solana (solana:*) requirement, which an EVM testnet client cannot satisfy. |
| Compliant | GET | [Cross-VM accepts (EVM and SVM together)](https://grug402.dev/scenario/cross-vm-accepts.md) `/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. |

## Client Policy Gate (MCP)

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Quirk | GET | [Overpriced vs per-call cap](https://grug402.dev/scenario/overpriced-vs-cap.md) `/api/x402/overpriced-vs-cap` | Advertises a price far above a sane per-call cap that an MCP policy gate would enforce. |
| Quirk | GET | [Resource host mismatch](https://grug402.dev/scenario/resource-host-mismatch.md) `/api/x402/resource-host-mismatch` | The advertised resource origin differs from the server actually being called. |
| Quirk | GET | [Tool / operation mismatch](https://grug402.dev/scenario/tool-operation-mismatch.md) `/api/x402/tool-operation-mismatch` | The requirement authorizes a different, more privileged tool than the one being requested. |
| Quirk | GET | [Session price escalation](https://grug402.dev/scenario/session-price-escalation.md) `/api/x402/session-price-escalation` | The price climbs call over call (1x, 2x, 3x...) to defeat a per-session spend limit. |

## Facilitator Dependency

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Quirk | GET | [Dead facilitator URL](https://grug402.dev/scenario/dead-facilitator.md) `/api/x402/dead-facilitator` | Points /verify and /settle at a facilitator host that does not exist. |
| Quirk | GET | [Facilitator can't support the network](https://grug402.dev/scenario/unsupported-network-facilitator.md) `/api/x402/unsupported-network-facilitator` | Advertises a network the facilitator does not handle. |
| Quirk | GET | [Facilitator rate-limits /verify (429 → misclassified 402)](https://grug402.dev/scenario/facilitator-rate-limited.md) `/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. |
| Quirk | GET | [Facilitator temporarily unavailable (503)](https://grug402.dev/scenario/facilitator-soft-down-503.md) `/api/x402/facilitator-soft-down-503` | The facilitator is reachable but returns 503 Service Unavailable — it is not a dead URL, just temporarily down. |
| Quirk | GET | [Settle returns 500 (ambiguous paid state)](https://grug402.dev/scenario/facilitator-settle-500.md) `/api/x402/facilitator-settle-500` | /verify succeeds but /settle returns 500 Internal Server Error, leaving it unknown whether the payment landed on-chain. |
| Quirk | GET | [Settle succeeds without txHash](https://grug402.dev/scenario/facilitator-missing-txhash.md) `/api/x402/facilitator-missing-txhash` | /settle reports success: true but omits every transaction-hash field; the PAYMENT-RESPONSE receipt carries no on-chain proof. |
| Quirk | GET | [Non-JSON facilitator response leaked in 402](https://grug402.dev/scenario/facilitator-non-json-leaks.md) `/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. |
| Quirk | GET | [Schema drift: valid vs isValid (loose check grants)](https://grug402.dev/scenario/facilitator-schema-drift.md) `/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. |
| Quirk | GET | [No timeout on /verify (hangs indefinitely)](https://grug402.dev/scenario/facilitator-no-verify-timeout.md) `/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. |
| Quirk | GET | [Wrong payTo used at settle time](https://grug402.dev/scenario/facilitator-wrong-payto-at-settle.md) `/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. |
| Quirk | GET | [Grants access despite settle failure](https://grug402.dev/scenario/facilitator-already-settled-grants.md) `/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. |
| Quirk | GET | [Concurrent settle race (second racer gets 402)](https://grug402.dev/scenario/facilitator-settle-concurrent-race.md) `/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. |

## Paid-but-Denied / Unpaid-Service

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Quirk | GET | [Settles, then the handler fails](https://grug402.dev/scenario/settle-then-fail.md) `/api/x402/settle-then-fail` | The payment settles on-chain, then the handler returns 500. |
| Quirk | GET | [Success without PAYMENT-RESPONSE](https://grug402.dev/scenario/missing-payment-response.md) `/api/x402/missing-payment-response` | Settles and returns 200 but omits the PAYMENT-RESPONSE header. |
| Quirk | GET | [MPP paid-but-denied (403 after valid credential)](https://grug402.dev/scenario/mpp-paid-denied.md) `/api/mpp/mpp-paid-denied` | Accepts a structurally valid push credential but returns 403 Forbidden with a Payment-Receipt failure header. The payment credential was consumed without delivering the resource. |

## Edge / CDN Controls

| Status | Method | Endpoint | Expected behavior |
| --- | --- | --- | --- |
| Compliant | GET | [Correct edge cache headers (compliant)](https://grug402.dev/scenario/edge-correct-headers.md) `/api/x402/edge-correct-headers` | The 402 challenge is no-store; the paid 200 is private, no-store with Vary: PAYMENT-SIGNATURE — and it still does a real verify and settle. |
| Quirk | GET | [Cacheable 402 challenge](https://grug402.dev/scenario/edge-cacheable-challenge.md) `/api/x402/edge-cacheable-challenge` | Sends the 402 with Cache-Control: public, max-age, so a shared cache stores and replays a stale challenge. |
| Quirk | GET | [Cacheable paid response (shared cache)](https://grug402.dev/scenario/edge-cacheable-paid.md) `/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. |
| Quirk | GET | [Missing Vary on payment](https://grug402.dev/scenario/edge-missing-vary.md) `/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. |
| Quirk | GET | [stale-while-revalidate leak](https://grug402.dev/scenario/edge-swr-leak.md) `/api/x402/edge-swr-leak` | A stale-while-revalidate directive lets the edge serve cached protected content without payment during revalidation. |
| Quirk | GET | [Oversized challenge header](https://grug402.dev/scenario/edge-oversized-header.md) `/api/x402/edge-oversized-header` | Pads the PAYMENT-REQUIRED header beyond common proxy header-size caps, so an edge truncates, drops, or 431s it. |
| Quirk | GET | [Conditional 304 replay](https://grug402.dev/scenario/edge-conditional-304.md) `/api/x402/edge-conditional-304` | The paid resource carries an ETag, and a later conditional request is answered 304 from cache without re-payment. |
| Quirk | GET | [Rate-limited before payment](https://grug402.dev/scenario/edge-rate-limited.md) `/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. |
| Quirk | GET | [WAF blocks the payment payload](https://grug402.dev/scenario/edge-waf-blocks-payment.md) `/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. |
| Quirk | POST | [Body too large (413)](https://grug402.dev/scenario/edge-body-too-large.md) `/api/x402/edge-body-too-large` | A paid POST is rejected by a small edge body cap with 413 before reaching the payment logic. |
| Quirk | GET | [Edge timeout during settle](https://grug402.dev/scenario/edge-timeout-settle.md) `/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. |
