x402 & HTTP 402 Fundamentals · 7 min read · Jun 29, 2026

How a 402 Payment Required Response Actually Works, Step by Step

Most explanations of x402 describe the handshake in one sentence — "call it, get a 402, pay, retry" — and that's accurate but skips the part worth actually reading: what's inside the response, what the client has to build in reply, and what the server checks before it runs the route. This post walks through all four stages against a real DimeBox endpoint.

Stage 1: the unpaid call

A client calls a paid route with no payment information attached — there's nothing special about this request, it's exactly how you'd call any JSON API:

curl -i -X POST https://dimebox.app/api/inboxes/acme-support/send \
  -H 'Content-Type: application/json' \
  -d '{"to":"[email protected]","subject":"Hi","body":"..."}'

Stage 2: the 402 response

The server responds 402 Payment Required with a PAYMENT-REQUIRED header. That header is a base64-encoded JSON payload describing the terms: the price, the asset (USDC), the network (a CAIP-2 identifier like eip155:8453 for Base mainnet), and which scheme to use to construct a valid payment. Nothing about the route itself has run yet — no side effect has happened, nothing has been charged.

import base64, json
payment_required = json.loads(base64.b64decode(response.headers["PAYMENT-REQUIRED"]))
# { "scheme": "exact", "network": "eip155:8453", "maxAmountRequired": "...", ... }

Stage 3: signing a matching payment

The client's x402 library reads those terms and produces a signed payment that matches them exactly — same amount, same network, same asset. Under the exact scheme that DimeBox and most of the current ecosystem use, this signature is an EIP-3009 transferWithAuthorization: an authorization the recipient can submit on-chain without the signer paying gas. See the exact scheme explained and what EIP-3009 actually does for what's inside that signature. The result gets base64-encoded into a PAYMENT-SIGNATURE header and the original request is replayed with it attached:

curl -X POST https://dimebox.app/api/inboxes/acme-support/send \
  -H 'Content-Type: application/json' \
  -H 'PAYMENT-SIGNATURE: <signed payment>' \
  -d '{"to":"[email protected]","subject":"Hi","body":"..."}'

Stage 4: verification, execution, settlement

On the retry, the server hands the signature to a facilitator to verify it's well-formed and matches the quoted terms before running anything — see x402 facilitators explained for exactly what that step checks and who's trusted to check it. Only after verification does the route itself run. And the payment only actually settles on-chain if the route's own response comes back under 400 — if sending the email fails validation, or the route errors for any other reason, nothing is charged, even though a valid signature was presented. The response looks like any other successful call once this is done:

# -> HTTP/1.1 202 Accepted
# -> { "data": { ... }, ... }

What a well-behaved client checks before retrying

Not every 402 response should trigger an automatic retry. A client worth trusting with real funds checks that the quoted network matches one it's actually configured to pay on, that the quoted asset is USDC and not something it doesn't hold, and that the amount is one it's willing to pay before ever constructing a signature. None of that is enforced by the protocol itself — x402 defines the shape of the exchange, not a policy for when a caller should agree to pay. That policy decision stays entirely with whoever configured the client, which is exactly where it should sit for software that's spending real money on its own.

Why the retry is a full request, not a lighter follow-up

It's worth noticing that stage 3 replays the entire original request — same body, same headers, plus one more. x402 doesn't maintain any session state between the 402 and the retry; the server doesn't remember that this particular client asked once already. That statelessness is deliberate: it means a caller can't be tracked across the unpaid call and the paid one, and it means the server doesn't need to hold anything in memory waiting for a retry that might never come. Every request is self-contained.

This is also why a response header rather than a response body carries the payment terms and the signature: headers survive being inspected and retried by generic HTTP tooling (proxies, logging middleware, retry libraries) without anyone needing to understand the JSON body's shape.

What happens if the retry never comes

Nothing. A 402 response has no side effect and no expiry logic tied to the server holding a slot open for you — the server didn't reserve anything, didn't start a timer that needs cleaning up, and doesn't care whether the client ever retries at all. This is part of why the handshake scales to agents calling many different APIs: an agent that decides mid-flow that a given call isn't worth the quoted price can simply not retry, and nothing on the server side needs to notice or clean up after that decision.

Trying it yourself

The buyer quickstart has this entire four-stage exchange wrapped in runnable Node and Python examples using current x402 client libraries, so you don't have to hand-construct a signature to see it work. For doing the same thing without spending real USDC first, see Base Sepolia vs Base mainnet.

New to agent email? Start with Email for Agents: The Complete Guide to Giving AI Agents a Real Inbox for the full picture.