x402, explained: the HTTP status code agents have been waiting for
The HTTP status code 402 Payment Required has been in the spec since 1995 and was, for thirty years, an in-joke at every HTTP standards meeting. Nobody knew what to put in it. Nobody wanted to be the one to define it. So it sat there, reserved, getting funnier by the year.
The x402 payment standard ended the joke. It defined a small, opinionated wire format for what a 402 response should look like and how a client should answer it. The whole thing fits on a postcard. Here's what it says.
The 402 response
When a client hits a paid resource without a payment header, the server replies:
HTTP/1.1 402 Payment Required
X-USDC-Amount: 0.0042
X-USDC-Chain: base
X-USDC-PayTo: 0xA1f3...7C2d
X-USDC-Nonce: 01HD5K8...
X-USDC-Expires: 2026-05-25T19:14:21ZFive headers. A price, a chain, a destination, a nonce, an expiry. Anything richer (a description of the resource, a split-payment table, an alternate currency) lives in the body as JSON. The headers are the part the client has to parse to decide whether to pay.
The payment retry
If the client decides to pay, it retries the same request with a payment header:
POST /v1/score HTTP/1.1
X-USDC-Payment: eyJjaGFpbi...
X-USDC-Signer: 0x9A3f...c821
Content-Type: application/json
{"input":"..."}The payment header is a compact, signed envelope containing the nonce, the chain, the amount, the destination, and the signer. Critically, the payment header is not on-chain yet. It's a promise the server can settle by submitting it.
The settlement step
The server validates the signature, validates the nonce hasn't been used, and submits the payment to the chain. On confirmation (which on Base is roughly 1-2 seconds), it adds a receipt header to the forwarded request:
X-USDC-Receipt: eyJzZXR0bGVk...The receipt is what your handler persists alongside its business state. It's signed by the gateway's payment key and contains the chain transaction hash, the settlement block, the final price (often slightly different from the quote thanks to dynamic gas), and any custom metadata you attached at the gateway.
Why this works
The whole design hinges on three principles that have held elsewhere in the HTTP world.
Use the status codes. The HTTP spec already had the right status code waiting; it just lacked the contract for what should go in it. Building x402 on 402 means caches, proxies, load balancers, and observability tools already do the right thing. A 402 doesn't get retried by your existing retry policy. A 402 doesn't get logged as an error in your handler.
Make the client dumb. A client that supports x402 needs to be able to (a) read five response headers and (b) sign one request header. That's it. The whole rest of the SDK is convenience — typing, receipts, retries, policy enforcement — but the wire protocol is small enough that you can hand-roll a client in an afternoon. We've seen people do it as a one-liner with curl.
Don't invent new identity. The signer field in the payment header is just a blockchain address. There's no new account system, no API key, no client_id/client_secret. You already have a wallet; that's your identity. Authorization happens at sign time, not in advance.
What we got wrong, and fixed
The first version of x402 we shipped expected the client to quote a price in advance. The server would say "this costs 0.0042 USDC" and the client would say "okay, here's 0.0042 USDC." This works fine until the price changes between quote and settlement — say, gas spikes during settlement on Base.
We addressed this with two changes. First, we allow the client to declare a maxSpend instead of an exact amount, so the server can settle at any price up to that cap. Second, we made receipts always carry the final settled amount, not the quote. The dashboard and ledger always reflect the truth of what hit the chain, not the optimism of what was promised.
What's next for the standard
We're contributing to the x402-stream RFC for token-by-token billing on streaming LLM endpoints, and to x402-batch for amortizing settlement across many small payments. Both are backwards compatible with the current wire version. The first OpenUSDC release with x402-stream support is scheduled for the 0.8 milestone.
If you want to chip in, the RFC repo is on GitHub and we hold office hours every other Friday on Discord.