Skip to content

561 Group · Engineering

How an x402 payment works, step by step, from a real 402 response

Decode a live x402 v2 challenge header by header: the 402, PAYMENT-REQUIRED, the signed EIP-3009 authorization, settlement and the receipt.

Engineering

How an x402 payment works, step by step, from a real 402 response

An x402 payment is an ordinary HTTP exchange with one extra round trip. The client asks for a resource, the server answers 402 Payment Required with a base64 PAYMENT-REQUIRED header describing the price, the client signs a USDC transfer authorization and repeats the request with a PAYMENT-SIGNATURE header, and the server settles the payment through a facilitator and returns the resource with a PAYMENT-RESPONSE receipt. This article decodes a real challenge from the Red Cup seller at market.561.group, captured on 2026-10-22 at 00:30 UTC, so every field below is observed rather than invented.

What is the first request, and what does the seller answer?

The client sends a normal request. We use one of the cheapest offers, an RFC 6901 JSON Pointer lookup that costs $0.0012:

bash
curl -s -i -X POST https://market.561.group/v1/data/json-pointer \
  -H 'content-type: application/json' \
  -d '{"value":{"a/b":[1,null]},"pointer":"/a~1b/1"}'

The response starts like this (other headers omitted):

http
HTTP/2 402
content-type: application/json
cache-control: no-store
payment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQYXltZW50IHJlcXVpcmVkIiwi...

Three details matter. The status is 402. The body is the placeholder {}; the real challenge is in the header, so a client that only reads bodies learns nothing. And cache-control: no-store keeps intermediaries from caching a quote.

What is inside the PAYMENT-REQUIRED header?

Decode the header to see the challenge:

bash
curl -s -i -X POST https://market.561.group/v1/data/json-pointer \
  -H 'content-type: application/json' \
  -d '{"value":{"a/b":[1,null]},"pointer":"/a~1b/1"}' \
  | grep -i '^payment-required' | cut -d' ' -f2 | tr -d '\r' | base64 -d | jq .

The decoded JSON, abbreviated to the load-bearing fields:

json
{
  "x402Version": 2,
  "error": "Payment required",
  "resource": {
    "url": "https://market.561.group/v1/data/json-pointer",
    "mimeType": "application/json",
    "serviceName": "json-pointer",
    "tags": ["json", "rfc6901", "json-pointer"]
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "1200",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x…(the seller's receiving address)",
      "maxTimeoutSeconds": 300,
      "extra": { "name": "USD Coin", "version": "2" }
    }
  ],
  "extensions": { "bazaar": { "...": "..." }, "sign-in-with-x": { "...": "..." } }
}

Reading it field by field:

FieldMeaning
x402Version: 2Protocol version. v2 uses the PAYMENT-REQUIRED, PAYMENT-SIGNATURE and PAYMENT-RESPONSE headers.
scheme: "exact"The buyer authorizes exactly this amount; no more, no less.
network: "eip155:8453"A CAIP-2 chain identifier. 8453 is Base mainnet.
amount: "1200"Atomic units of the asset. USDC has 6 decimals, so 1200 is $0.0012.
assetThe USDC contract on Base (Circle's token), not a symbol.
payToWhere the money goes. A buyer must not simply trust this field; see Never trust the payee.
maxTimeoutSeconds: 300How long a signed authorization may remain valid.
extra.name, extra.versionThe EIP-712 domain parameters of the token, needed to sign correctly.

The extensions object carries optional features. bazaar describes the input and output of the endpoint with a JSON Schema so a discovery index or an agent can see how to call it, and sign-in-with-x offers a wallet-login challenge, covered in Sign-in-with-X. The amounts are exact integers; the seller never converts between dollars and atomic units on the buyer's behalf, because rounding in a payment path is a defect.

How does the client sign a payment?

USDC on Base implements EIP-3009 transferWithAuthorization. The buyer does not send a transaction. It signs an EIP-712 typed message that authorizes a transfer of exactly amount from the buyer to payTo, with a random 32-byte nonce and a validity window. The signature goes into a JSON payload that echoes the accepted requirement, base64-encoded as the PAYMENT-SIGNATURE header. Because the buyer signs rather than submits, the buyer needs no ETH for gas; a facilitator submits the transfer.

In code, the official client library does this for you. In outline:

js
// Outline using the official x402 packages (see their docs for exact imports)
const client = /* x402 client registered with an exact-EVM scheme and your signer */;
const paymentPayload = await client.createPaymentPayload(paymentRequired);
const header = encodePaymentSignatureHeader(paymentPayload);
// repeat the original request with:  PAYMENT-SIGNATURE: <header>

A careful buyer inserts three checks between "read the challenge" and "sign": that the payTo equals an address the buyer's owner approved in advance, that amount is below a budget, and that the resource URL is the one requested. The Red Cup buyer library enforces all three and also detects quote drift (a second challenge that differs from the first accepted one) before signing.

What does the seller do with the signature?

The seller's middleware decodes the header and asks a facilitator to verify it: is the signature valid for this payer, is the nonce unused, does the balance cover the amount, does the requirement match what the seller issued? Our seller uses the Coinbase CDP facilitator. If verification succeeds, the seller runs its handler. Only if the handler returns a success status does it ask the facilitator to settle, which submits the EIP-3009 transfer on Base. A handler that fails or throws causes the authorization to be cancelled instead (see Refuse before you charge).

The successful response carries the resource and a PAYMENT-RESPONSE header containing the settlement result, including the network transaction hash. That receipt is what the buyer records. The Red Cup buyer calls beforePayment with the exact signed request before it is sent, and onSettlement with the facilitator's report before the delivery is read, so the buyer's own books show the intent even if the connection dies.

What if the connection drops after the buyer pays?

The signature is single-use: the facilitator will reject a second submission of the same nonce. For ordinary API calls the buyer simply pays again for a new computation, which costs a fraction of a cent. For expensive digital goods the seller offers the X-Payment-Recovery mechanism so the same signed request returns the same purchased bytes.

How do I try this without spending anything?

Everything up to the signature is free. The unsigned probe above costs nothing and moves no funds. If you want to inspect any x402 v2 resource safely, the Apache-2.0 x402 boundary reference includes observePublicX402Resource, which fetches a public resource, refuses redirects and non-HTTPS targets, validates the challenge against the official schema, and returns a content-addressed digest of what it saw, all without paying or signing. To list what the market sells, read `/.well-known/x402` (130 resources at the time of writing) or the richer `/v1/catalog`.

What should I remember?

An x402 challenge is a quote written by the seller, not an authorization from the buyer. The seller's address, the amount and the asset are all claims; the buyer's software decides which claims to honor. The rest of this series is about making both sides of that exchange careful: start with what a seller should do beyond the spec.

Contents

Explore

Tagged

  • x402
  • http-402
  • usdc
  • eip-3009
  • agent-payments