true402
protocol reference

How 402 works.

One status code, one header. Below is the full handshake, the shape of a 402, and where to point a machine. The complete, machine-readable contract is the OpenAPI spec.

§01 · the handshake

Seven steps, two round-trips.

  1. 01 Agent sends a request with no payment.
  2. 02 Service returns 402 with payment requirements — Base and Solana in the same challenge.
  3. 03 Agent signs the payment with its wallet (USDC — EIP-3009 on Base, a signed transaction on Solana).
  4. 04 Agent retries with the PAYMENT-SIGNATURE header.
  5. 05 Service verifies the payment with the facilitator.
  6. 06 Service processes the request and responds.
  7. 07 The answer is released only once the payment has settled; a settlement that is refused returns 402 and no answer, and nothing is charged.
§02 · anatomy of a 402

Everything needed to pay.

A 402 carries one or more accepts entries. Each says how much, in which asset, on which network, to whom, and via which facilitator — enough for an agent to pay without ever talking to a human. Every stall here quotes two: pick the rail you already hold USDC on, and pay that entry exactly.

402 body
HTTP/1.1 402 Payment Required

{
  "x402Version": 2,
  "error": "Payment required. Sign the USDC authorization in accepts[0] and retry with the PAYMENT-SIGNATURE (x402 v2) or X-PAYMENT (v1) header — or use a ready-made client (see extensions.true402.clients).",
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:8453",              // Base mainnet
    "amount": "5000",                      // atomic USDC, 6 decimals -> $0.005
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "payTo": "0xFe25…A9C4",
    "maxTimeoutSeconds": 300,
    "extra": { "name": "USD Coin", "version": "2" }   // EIP-712 domain - sign EXACTLY this
  }, {
    "scheme": "exact",                     // same scheme; the network is what differs
    "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",  // Solana mainnet (CAIP-2)
    "amount": "5000",                      // same price on either rail
    "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",   // USDC-SPL mint
    "payTo": "ARxR…97hD",
    "maxTimeoutSeconds": 60,
    "extra": { "feePayer": "Hbe1…LBLP" }         // pays the network fee, not the price
  }],
  "resource": {                            // sibling of accepts, not inside it
    "url": "https://true402.dev/api/v1/token-safety",
    "description": "token-safety",
    "mimeType": "application/json",
    "serviceName": "true402"
  }
}
§03 · the payment header

v2 by name, v1 still welcome.

Send the signed payment, base64-encoded, in PAYMENT-SIGNATURE — the x402 v2 name. The v1 name X-PAYMENT is read too, because plenty of clients still send it, including our own npm packages released before 30 September 2026.

  • Either header: same checks, same answer. Both at once is fine when they carry the same payment; two different payments answer 400 — we never guess which one you meant.
  • Either layout: the v2 accepted object (the accepts entry you chose, verbatim), or scheme/network at the top level as in v1. A top level that contradicts accepted is refused.
  • Why that is safe: the header and layout only say where to look. What is verified is the signed authorization, against our own quote — chain, USDC contract, payTo, exact amount. A v1 alias like base, a testnet or another chain is refused, not translated.
  • Once only: an authorization is keyed on its signed nonce, not on the header, so replaying it under the other name or layout is still a replay (402).
  • Receipts: PAYMENT-RESPONSE, mirrored in X-PAYMENT-RESPONSE for v1 clients.
  • v1-only clients can't pay: a client that only understands v1 network names (e.g. x402-fetch 1.x) rejects our CAIP-2 eip155:8453 before it signs. Use a v2 client — @x402/fetch, the official Python or Go packages, or ours.
§04 · the rails

USDC, on two chains.

  • Base: eip155:8453 — USDC via EIP-3009 transferWithAuthorization. The payer signs, the facilitator submits, so the payer needs no native gas and receiving is keyless.
  • Solana: solana — USDC-SPL, the same exact scheme; the network is what tells the two apart. The payload is a signed Solana transaction, and extra.feePayer names the account that pays the network fee.
  • Both, every time: a 402 quotes both rails at the same price; read accepts and pay the entry for the chain you already hold USDC on. Our own client packages currently sign the Base entry.
  • Different clocks: a Base authorization is good for the maxTimeoutSeconds in the quote. A Solana payment is bound to its blockhash instead, which expires roughly 60–90 seconds after signing — not after the quote — so sign it late and send it straight away.
  • Settlement: verified before the handler runs, settled before the answer is released; a refused settlement returns 402 and nothing is charged.
  • Funding: a wallet holding USDC on Base (or Solana) is all a buyer needs — how to fund one.
  • Not on this deployment: no testnet, and the Lightning (BTC) rail that exists in the codebase is switched off — /health/detailed reports it disabled.
§05 · for machines

Discover without docs.