Skip to main content

Complete an x402 payment

x402 turns HTTP 402 into a working payment protocol. You request a resource, the server tells you what it costs, you present a signed payment intent, and you get the resource plus a receipt you can verify against an on-chain batch commitment. This walks the whole loop against a real sequencer.

Prerequisites

  • A payment-gated endpoint (the sequencer ships a demonstration premium route)
  • An Ed25519 signing key registered in the agent key registry
  • The sequencer base URL

Step 1 — Request the resource, get a 402

The requirements arrive in the response body, as a structured error whose details carry the payment terms:
Read the terms from the body, not from a header. amount is in the asset’s smallest unit10000 is 0.01 USDC at 6 decimals, not 10,000 USDC. Getting this wrong by six orders of magnitude is the most common first mistake.

Step 2 — Build and sign the intent

The signing hash is SHA-256 over a domain-separated, order-dependent preimage. Every field is concatenated in exactly this order, integers as big-endian u64:
Then sign those 32 bytes with Ed25519.
The order is part of the hash. Reordering any two fields, encoding an integer little-endian, or sending USDC where the preimage expects usdc all produce a different hash and the signature will be rejected. valid_until must be within max_validity_secs (86,400 — 24 hours) of now.

Step 3 — Retry with X-Payment

The X-Payment header carries standard base64 of the JSON document that POST /api/v1/x402/payments accepts — base64 because raw JSON is not safe in a header value.
A valid intent returns 200 with the resource, plus a receipt header:
A gated request and a direct submission to POST /api/v1/x402/payments run through the same verification and sequencing path. The header route is not a shortcut — the intent gets a sequence number, burns its nonce, and enters the normal batching pipeline either way.

Replay protection, and one trap

The intent is consumed by the request it pays for.
  • The nonce is reserved in a nonce-tracking table keyed by payer. Presenting the same signed intent twice fails the second time with Nonce already used for this payer.
  • Any idempotency_key in the decoded intent is discarded on the header path, deliberately: it is not part of the signed hash, so honouring it would let a replay short-circuit through the idempotency lookup and get the resource twice for one payment.
Do not build retry logic that re-presents the same X-Payment value. A network timeout after the server sequenced your intent has already spent the nonce — retrying returns a nonce error, not the resource. Sign a fresh intent with a new nonce, and reconcile the first one via its receipt_url.

Step 4 — Fetch and verify the receipt

receipt_url becomes fully populated once the intent is batched — sequencing is immediate, batching is not.
The receipt carries a Merkle inclusion proof. Verify it against the anchored batch commitment independently of the sequencer — that verification, including the domain-separated leaf and node hashing, is documented in Set L2 verification.
Verifying the proof is what makes the receipt worth having. An unverified receipt is just the sequencer asserting it did its job; a verified one is arithmetic anybody can check against the chain.

The full loop