DEV Community

Swift Bot Labs
Swift Bot Labs

Posted on

x402 Explained: How an Autonomous Agent Pays for an API Call over HTTP 402

Ref: KB-2410-07

Your agent is halfway through a task and needs one piece of paid data: a premium market feed, a geocoding call, a search result. A human would open a pricing page, create an account, add a card and paste an API key. An agent cannot do most of that. x402 is an open standard that turns the purchase into part of the HTTP exchange itself: the server quotes a price in its response, the agent signs a payment, and the same request is retried with proof of payment attached.

This article explains what x402 is, walks through the negotiation flow message by message, separates negotiation (HTTP headers) from settlement (the on-chain transfer), and ends with a complete worked example using the exact field values from the official specification. Every technical claim links to its source.

What x402 is

The x402 specification describes itself as "an open payment standard that enables clients to pay for external resources" (x402 spec v2, section 1). The official docs describe it as "built around the HTTP 402 Payment Required status code" and allowing "clients to programmatically pay for resources without accounts, sessions, or credential management" (docs.x402.org). It is published under the Apache-2.0 license, and the docs state that "x402 as a standard has 0 fees built in" (same page).

The status code it builds on is old. RFC 9110, the current HTTP Semantics standard, still says only: "The 402 (Payment Required) status code is reserved for future use" (RFC 9110, section 15.5.3). x402 gives that code a concrete, machine-readable meaning.

A note on repositories: the reference code started at github.com/coinbase/x402. Its README now says the repo "moved ... under the x402 Foundation" and that coinbase/x402 "is now a development fork"; the canonical home is github.com/x402-foundation/x402.

The three parties

The spec defines three components (spec v2, section 3):

  • Resource server: the API that wants to be paid.
  • Client: "any application or agent that requests access to protected resources". This is your agent.
  • Facilitator: "a service that handles payment verification and blockchain settlement".

The facilitator is optional. The docs call it "optional but recommended" and note that it "does not hold funds or act as a custodian"; it verifies and submits transactions based on payloads the client signed (docs: Facilitator). A server can also verify and settle locally.

Negotiation vs. settlement

This distinction is the most useful mental model for an agent developer.

  • Negotiation happens in HTTP. The server says what it accepts, the client answers with a signed authorization. Nothing has moved on any chain yet. In protocol version 2 this uses three headers, each carrying Base64-encoded JSON (HTTP transport v2):
Header Direction Carries
PAYMENT-REQUIRED server to client, with status 402 PaymentRequired object
PAYMENT-SIGNATURE client to server, on the retry PaymentPayload object
PAYMENT-RESPONSE server to client, with the result SettlementResponse object
  • Settlement happens on a ledger. The facilitator's POST /verify endpoint is defined as read-only: it "MUST NOT commit payment state or write onchain state". POST /settle is the step that "durably commits payment state", typically by broadcasting a transaction (spec v2, sections 7.1 and 7.2).

If you have read older tutorials: protocol version 1 used X-PAYMENT for the client's payload and X-PAYMENT-RESPONSE for the result, sent the requirements in the 402 response body, and named networks with plain strings such as base-sepolia. Version 2 renamed the headers and switched to CAIP-2 network identifiers such as eip155:84532 (V1 to V2 migration guide; HTTP transport v1). The spec's version history dates v2.0 to 2025-12-09.

The flow, step by step

In the default authorization flow, payment is "verified before the resource executes and settled afterward" (spec v2, section 2). The Coinbase Developer Platform docs list the same order: the server asks the facilitator to verify, performs the requested work, then asks the facilitator to settle, and finally returns the resource (CDP: How x402 works).

  1. The agent sends an ordinary request.
  2. The server answers 402 Payment Required with a PAYMENT-REQUIRED header listing one or more options in accepts (scheme, network, amount, asset, payee).
  3. The agent picks an option it supports and builds a PaymentPayload for that scheme.
  4. The agent retries the request with the payload in PAYMENT-SIGNATURE.
  5. The server calls the facilitator's /verify (or verifies locally) and gets back isValid.
  6. If valid, the server does the work.
  7. The server calls /settle; the facilitator submits the transfer.
  8. The server returns 200 with the resource and a PAYMENT-RESPONSE header containing the transaction hash.

Not every scheme follows this order. The spec also defines an upfront flow (settle, then run the resource) and an escrow flow (settle a deposit, run, settle the final charge), and says clients should prefer authorization when both are offered (spec v2, section 6.1).

Why the agent never needs gas: EIP-3009

For the exact scheme on EVM chains, the recommended transfer method is EIP-3009 transferWithAuthorization, used by tokens such as USDC (exact scheme on EVM). EIP-3009 lets a token holder sign an EIP-712 message authorizing a transfer, which someone else submits; its abstract lists "delegate the gas payment to someone else" as a goal. Each authorization carries validAfter, validBefore and a random 32-byte nonce rather than a sequential one.

So the agent's wallet signs a message; it does not send a transaction. The facilitator broadcasts it and pays the gas. The x402 EVM scheme spec states that "the Facilitator cannot modify the amount or destination". Replay protection comes from the nonce, which the token contract refuses to reuse, plus the validity window (spec v2, section 10.1).

A worked example, end to end

The values below are the example values from the official HTTP transport and EVM scheme specs, so you can check every field against the source. The network is Base Sepolia testnet (eip155:84532) and the asset 0x036CbD53842c5426634e7929541eC2318f3dCF7e is that network's USDC, listed with 6 decimals in the x402 network and token table.

1. The agent asks. POST https://api.example.com/premium-data with body {"query": "latest market data"} and no payment header.

2. The server quotes. It returns HTTP/1.1 402 Payment Required with a PAYMENT-REQUIRED header. Decoded:

{
  "x402Version": 2,
  "error": "PAYMENT-SIGNATURE header is required",
  "resource": {
    "url": "https://api.example.com/premium-data",
    "description": "Access to premium market data",
    "mimeType": "application/json"
  },
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:84532",
    "amount": "10000",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
    "maxTimeoutSeconds": 60,
    "extra": { "name": "USDC", "version": "2" }
  }]
}
Enter fullscreen mode Exit fullscreen mode

amount is in atomic units: 10000 with 6 decimals is 0.01 USDC. extra.name and extra.version are the token's EIP-712 domain values, needed to build the signature.

3. The agent decides and signs. Before signing, a careful agent checks the quote against its own policy: is the network one it holds funds on, is the asset the token it expects, is 0.01 USDC within its per-call budget? The core spec lists "client-side budget management" as out of scope, so the policy belongs to the client implementation (more on SDK spend controls below). It then signs an EIP-3009 authorization:

"authorization": {
  "from": "0x857b06519E91e3A54538791bDbb0E22373e36b66",
  "to": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
  "value": "10000",
  "validAfter": "1740672089",
  "validBefore": "1740672154",
  "nonce": "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480"
}
Enter fullscreen mode Exit fullscreen mode

The window is 65 seconds. to equals the quoted payTo, and value equals the quoted amount.

4. The agent retries. Same POST, now with PAYMENT-SIGNATURE: <base64>. Decoded, the PaymentPayload contains x402Version: 2, the resource, an accepted field that copies the chosen accepts entry, and a payload holding the 65-byte signature and the authorization above.

5. Verification (no money moves). The server POSTs { x402Version, paymentPayload, paymentRequirements } to the facilitator's /verify. For EIP-3009 the facilitator checks that the signature recovers to from, that the payer has enough balance, that amount and validity window meet the requirements, that token and network match, and simulates the transferWithAuthorization call (exact EVM scheme, Phase 2). It answers with a VerifyResponse such as {"isValid": true, "payer": "0x857b..."}.

6. The work. The API produces the market data.

7. Settlement (money moves). The server POSTs the same structure to /settle. The facilitator calls transferWithAuthorization on the USDC contract with the signed parameters, and 0.01 USDC moves from the agent's address to payTo.

8. Delivery. The server returns HTTP/1.1 200 OK with the data and a PAYMENT-RESPONSE header that decodes to:

{
  "success": true,
  "transaction": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
  "network": "eip155:84532",
  "payer": "0x857b06519E91e3A54538791bDbb0E22373e36b66"
}
Enter fullscreen mode Exit fullscreen mode

(The hash is the spec's placeholder.) If settlement fails, the same header comes back on a 402 with "success": false and an errorReason such as insufficient_funds (HTTP transport v2).

In code

You rarely build these headers by hand. The buyer quickstart shows a fetch wrapper that performs steps 2 to 4 automatically:

import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));

const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const res = await fetchWithPayment("https://api.example.com/premium-data");
Enter fullscreen mode Exit fullscreen mode

The same guide documents Python (pip install "x402[httpx]") and Go clients.

Practical advice for agent builders

These are recommendations from our own experience, not requirements of the spec:

  • Put a policy layer in front of the signer. Allow-list networks and assets, cap the price per call and per day, and flag quotes whose payTo changes between calls to the same endpoint. The reference SDK helps here: per its buyer quickstart, the client by default "only pays recognized USD-pegged assets (e.g. USDC) and caps each payment at $1", configurable through spend controls, and an onBeforePaymentCreation hook can cancel a payment before it is signed (docs: Lifecycle Hooks). Daily totals and per-endpoint rules are still yours to write.
  • Use a dedicated low-balance wallet. The key that signs authorizations should hold only what the agent may spend.
  • Log the PAYMENT-RESPONSE. The transaction hash is your receipt; check it on a block explorer when reconciling.
  • Handle settlement_pending. The spec defines it as non-terminal: the transaction was broadcast but not yet confirmed, and the response must include its hash so you can check the chain before retrying (spec v2, section 9).
  • Start on testnet. The docs say the public x402.org facilitator "is intended for development and testnet workflows" (docs: Facilitator).

Summary

x402 makes paying for an API call part of the HTTP exchange. The 402 response is a price quote, the retry carries a signed authorization, and the facilitator first verifies it without touching the chain, then settles it on-chain. For an agent, that means no accounts or API keys, only a funded wallet and a spending policy you control.

Written by Swift Bot Labs. Independent and educational; we have no affiliation with the x402 Foundation or Coinbase.

Sources

Top comments (0)