Skip to main content
The x402 HTTP flow is the simplest way to interact with Key0. A single endpoint POST /x402/access handles three cases based on the request body and headers:
  1. Discovery — no planId in the body, returns all available plans.
  2. ChallengeplanId present, no payment-signature header, creates a PENDING record.
  3. SettlementplanId present with a payment-signature header, settles on-chain and returns an AccessGrant.

Sequence Diagram

Three Cases

Send an empty body (or omit planId) to discover all available plans. No PENDING record is created — this is a pure pricing query.

Request

curl -X POST https://api.example.com/x402/access \
  -H "Content-Type: application/json" \
  -d '{}'

Response

HTTP/1.1 402 Payment Required
payment-required: eyJ4NDAyVm... (base64-encoded JSON)
www-authenticate: Payment realm="https://api.example.com", accept="exact"
Content-Type: application/json
{
  "x402Version": 2,
  "resource": { "url": "...", "method": "POST" },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "amount": "100000",
      "payTo": "0xSellerWallet...",
      "maxTimeoutSeconds": 900,
      "extra": {
        "name": "USDC",
        "version": "2",
        "description": "Basic plan - $0.10 USDC"
      }
    }
  ],
  "error": "Payment required"
}
The accepts array contains one entry per plan configured in SellerConfig.plans. Each entry includes the USDC contract address, the amount in base units (6 decimals), and the seller’s wallet address.

HTTP Headers Reference

HeaderDirectionFormatPurpose
payment-requiredServer to Client (402)base64 JSONPaymentRequirements containing plan pricing and wallet address
www-authenticateServer to Client (402)Payment realm=..., accept="exact"HTTP spec compliance; includes challenge=... when a PENDING record is created
payment-signatureClient to Serverbase64 JSONX402PaymentPayload with the signed EIP-3009 authorization
payment-responseServer to Client (200)base64 JSONX402SettleResponse with the on-chain txHash
x-a2a-extensionsClient to Serverpresence checkWhen present, the x402 middleware passes through to the A2A handler instead of processing the x402 flow

EIP-3009 Authorization

The payment-signature header carries a signed EIP-3009 transferWithAuthorization. This means the client signs an off-chain authorization that permits a specific USDC transfer, but never sends a transaction directly and never pays gas. The server (or a facilitator like Coinbase CDP) executes the transferWithAuthorization call on-chain, paying the gas fees. The USDC moves from the client’s wallet to the seller’s wallet in a single atomic transaction.

Decoded payment-signature Structure

{
  "x402Version": 2,
  "network": "eip155:84532",
  "scheme": "exact",
  "payload": {
    "signature": "0xSignedEIP3009...",
    "authorization": {
      "from": "0xBuyer...",
      "to": "0xSeller...",
      "value": "100000",
      "validAfter": "0",
      "validBefore": "1741180560",
      "nonce": "0xRandomNonce..."
    }
  },
  "accepted": {
    "scheme": "exact",
    "network": "eip155:84532",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "amount": "100000",
    "payTo": "0xSeller...",
    "maxTimeoutSeconds": 900,
    "extra": { "name": "USDC", "version": "2" }
  }
}
Key fields:
  • payload.signature — the EIP-3009 signature authorizing the USDC transfer.
  • payload.authorization — the transfer parameters: sender, recipient, amount (in USDC base units, 6 decimals), validity window, and a random nonce.
  • accepted — echoes the PaymentRequirements from the 402 response, so the server can verify the client accepted the correct terms.

Next Steps