--- name: x402 description: "HTTP 402 payment protocol for AI agent commerce — three-actor model (Client, Resource Server, Facilitator), ERC-3009 transferWithAuthorization, server middleware (@x402/express), client patterns in TypeScript and Python, facilitator integration, agent-to-agent payments, pricing strategies, and replay protection. Works on Base, Ethereum, Arbitrum, Optimism, Polygon, and Solana." license: Apache-2.0 metadata: author: 0xinit version: "1.0" chain: multichain category: AI Agents tags: - x402 - http-402 - payment-protocol - ai-agents - agent-commerce - erc-3009 - coinbase --- # x402 x402 is an open payment protocol that revives the HTTP 402 "Payment Required" status code to enable instant stablecoin payments directly over HTTP. It uses a three-actor model — Client (buyer), Resource Server (seller), and Facilitator (settlement) — to let any HTTP endpoint accept payment without API keys, sessions, or accounts. The protocol is built on EIP-3009 `transferWithAuthorization` for gasless USDC transfers, meaning clients sign an off-chain authorization and the facilitator broadcasts the on-chain settlement. Package ecosystem: - **Server (TypeScript)**: `@x402/express`, `@x402/hono`, `@x402/next` - **Client (TypeScript)**: `@x402/fetch`, `@x402/axios` - **Client (Python)**: `pip install x402` - **Client (Go)**: `go get github.com/coinbase/x402/go` - **Core**: `@x402/core`, `@x402/evm`, `@x402/svm` CDP Facilitator endpoint: `https://api.cdp.coinbase.com/platform/v2/x402` ## What You Probably Got Wrong > LLMs have stale training data. These are the most common mistakes. - **"x402 requires API keys or OAuth"** -- There are no API keys. Payment IS authentication. The client signs a USDC transfer authorization, includes it in the `X-PAYMENT` header, and the server verifies it. If the signature and balance check pass, the request is authorized. No accounts, no sessions, no bearer tokens. - **"x402 only works on Base"** -- The protocol supports any EVM chain where USDC implements EIP-3009 (Base, Ethereum, Arbitrum, Optimism, Polygon) and Solana. Network identifiers use CAIP-2 format: `eip155:8453` for Base mainnet, `eip155:1` for Ethereum, `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` for Solana mainnet. - **"Nonces are sequential counters"** -- x402 nonces are random 32-byte values (`bytes32`), not incrementing integers. Each authorization generates a fresh random nonce. The ERC-3009 contract tracks used nonces on-chain to prevent replay. Using sequential nonces or reusing a nonce will cause settlement to revert. - **"The client pays gas"** -- The client never submits an on-chain transaction. The client signs an off-chain EIP-712 authorization. The facilitator broadcasts the `transferWithAuthorization` call and pays gas. CDP's hosted facilitator absorbs gas costs on Base mainnet for free-tier usage (1,000 tx/month). - **"x402 supports any ERC-20 token"** -- The primary path uses USDC via EIP-3009 `transferWithAuthorization` for truly gasless transfers. A Permit2 fallback exists for arbitrary ERC-20 tokens, but it requires the user to have previously approved the Permit2 contract, which needs native gas. For the simplest integration, use USDC. - **"The old package name is @coinbase/x402"** -- The current packages use the `@x402/*` namespace (e.g., `@x402/express`, `@x402/fetch`). Earlier versions used `x402-express` and `x402-fetch` without the scope. The `@coinbase/x402` name was never published. - **"The payment header is called PAYMENT-SIGNATURE"** -- The header name is `X-PAYMENT`. It contains a base64-encoded JSON payload with the authorization, signature, scheme, and network. The server responds with `X-PAYMENT-RESPONSE`. ## Core Concepts ### Three-Actor Model ``` ┌──────────┐ 1. Request resource ┌─────────────────┐ │ │ ─────────────────────────────► │ │ │ │ 2. HTTP 402 + payment │ Resource │ │ Client │ ◄───────────────────────────── │ Server │ │ (Buyer) │ 3. Request + X-PAYMENT │ (Seller) │ │ │ ─────────────────────────────► │ │ │ │ 6. HTTP 200 + resource │ │ │ │ ◄───────────────────────────── │ │ └──────────┘ └────────┬────────┘ │ 4. Verify │ 5. Settle │ ┌────────▼────────┐ │ │ │ Facilitator │ │ (Settlement) │ │ │ └─────────────────┘ ``` ### Payment Flow Lifecycle 1. **Client** sends a standard HTTP request to a protected endpoint 2. **Resource Server** responds with HTTP 402 and payment requirements in the response body (scheme, network, amount, asset, payTo address) 3. **Client** constructs an EIP-3009 authorization (from, to, value, validAfter, validBefore, nonce), signs it with EIP-712, and retries the request with the `X-PAYMENT` header containing the base64-encoded payload 4. **Resource Server** forwards the payment to the **Facilitator** for verification (`/verify` endpoint) 5. **Facilitator** validates the signature, checks balance, and simulates the transfer 6. **Resource Server** serves the resource 7. **Resource Server** requests settlement from the **Facilitator** (`/settle` endpoint) 8. **Facilitator** calls `transferWithAuthorization` on the USDC contract to move funds on-chain ### HTTP 402 Response Format When a client hits a paid endpoint without a payment header, the server returns: ``` HTTP/1.1 402 Payment Required Content-Type: application/json { "x402Version": 2, "accepts": [ { "scheme": "exact", "network": "eip155:8453", "amount": "10000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0xSELLER_ADDRESS", "maxTimeoutSeconds": 60, "extra": { "assetTransferMethod": "eip3009", "name": "USDC", "version": "2" } } ], "error": "X-PAYMENT header is required" } ``` The `amount` is in the token's smallest unit. For USDC (6 decimals), `10000` = $0.01. ## ERC-3009 transferWithAuthorization EIP-3009 enables gasless token transfers by separating authorization from execution. The token holder signs an off-chain message; anyone can submit the signed authorization to the contract. ### Function Signature ```solidity function transferWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, bytes signature ) external; ``` ### EIP-712 Domain ```solidity EIP712Domain({ name: "USDC", version: "2", chainId: , verifyingContract: }) ``` ### Authorization Struct (for signing) ```solidity TransferWithAuthorization({ from: address, to: address, value: uint256, validAfter: uint256, validBefore: uint256, nonce: bytes32 }) ``` Key properties: - `nonce` is a random `bytes32`, not a sequential counter. The contract maintains a mapping of `(authorizer, nonce) => bool` to track used nonces. - `validAfter` and `validBefore` define a time window. Set `validAfter` to `0` (or current timestamp) and `validBefore` to current timestamp + `maxTimeoutSeconds`. - The facilitator calls this function — the signer never needs native gas. - USDC on Base, Ethereum, Arbitrum, Optimism, and Polygon all implement EIP-3009. ## Server Setup (TypeScript) ### Express Middleware ```bash npm install @x402/core @x402/evm @x402/express ``` ```typescript import express from "express"; import { paymentMiddleware, x402ResourceServer } from "@x402/express"; import { ExactEvmScheme } from "@x402/evm/exact/server"; import { HTTPFacilitatorClient } from "@x402/core/server"; const app = express(); const facilitatorClient = new HTTPFacilitatorClient({ url: "https://api.cdp.coinbase.com/platform/v2/x402", }); const resourceServer = new x402ResourceServer(facilitatorClient) .register("eip155:8453", new ExactEvmScheme()); app.use( paymentMiddleware( { "GET /api/weather": { accepts: { scheme: "exact", network: "eip155:8453", price: "$0.01", payTo: process.env.PAYMENT_ADDRESS as `0x${string}`, }, description: "Current weather data", }, "POST /api/analyze": { accepts: { scheme: "exact", network: "eip155:8453", price: "$0.05", payTo: process.env.PAYMENT_ADDRESS as `0x${string}`, }, description: "AI text analysis", }, }, resourceServer, ), ); app.get("/api/weather", (_req, res) => { res.json({ temperature: 72, unit: "F", location: "San Francisco" }); }); app.post("/api/analyze", (req, res) => { res.json({ sentiment: "positive", confidence: 0.92 }); }); app.listen(3000, () => { console.log("x402 server running on port 3000"); }); ``` Hono and Next.js middleware follow the same pattern — install `@x402/hono` or `@x402/next` and use the same `paymentMiddleware` + `x402ResourceServer` setup with framework-specific imports. ## Client Setup (TypeScript) ### Using @x402/fetch (Recommended) The `wrapFetchWithPaymentFromConfig` function wraps the native `fetch` API to automatically handle 402 responses — detect the payment requirement, sign the authorization, and retry with the `X-PAYMENT` header. ```bash npm install @x402/core @x402/evm @x402/fetch viem ``` ```typescript import { wrapFetchWithPaymentFromConfig } from "@x402/fetch"; import { ExactEvmScheme } from "@x402/evm"; import { privateKeyToAccount } from "viem/accounts"; import type { Hex } from "viem"; const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex); const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, { schemes: [ { network: "eip155:8453", client: new ExactEvmScheme(account), }, ], }); const response = await fetchWithPayment("https://api.example.com/api/weather"); if (!response.ok) { throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); console.log(data); ``` ### Multi-Network Client ```typescript import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; import { registerExactEvmScheme } from "@x402/evm/exact/client"; import { registerExactSvmScheme } from "@x402/svm/exact/client"; import { privateKeyToAccount } from "viem/accounts"; import { Keypair } from "@solana/web3.js"; import type { Hex } from "viem"; const evmAccount = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as Hex); const solanaKeypair = Keypair.fromSecretKey( Buffer.from(process.env.SOLANA_PRIVATE_KEY as string, "base64") ); const client = new x402Client(); registerExactEvmScheme(client, { signer: evmAccount }); registerExactSvmScheme(client, { signer: solanaKeypair }); const fetchWithPayment = wrapFetchWithPayment(fetch, client); // Automatically handles both EVM and Solana payment requirements const evmResponse = await fetchWithPayment("https://api.example.com/evm-data"); const solResponse = await fetchWithPayment("https://api.example.com/sol-data"); ``` For manual header construction without the wrapper, see the `agent-client` and `python-client` examples in this skill's `examples/` directory. ## Client Setup (Python) ```bash pip install "x402[evm,requests]" ``` ### Using the x402 Client ```python from x402 import x402ClientSync from x402.evm import ExactEvmScheme from eth_account import Account wallet = Account.from_key("0xYOUR_PRIVATE_KEY") client = x402ClientSync() client.register("eip155:*", ExactEvmScheme(signer=wallet)) # Make a paid request — the client handles the 402 flow automatically import requests session = requests.Session() response = session.get("https://api.example.com/api/weather") if response.status_code == 402: payment_required = response.json() payload = client.create_payment_payload(payment_required) response = session.get( "https://api.example.com/api/weather", headers={"X-PAYMENT": payload.to_header()}, ) print(response.json()) ``` For manual EIP-712 signing without the `x402` package, see the `python-client` example in this skill's `examples/` directory. ## Facilitator Patterns ### CDP Hosted Facilitator (Default) Coinbase Developer Platform provides a hosted facilitator with two endpoints: | Endpoint | Method | Purpose | |----------|--------|---------| | `/verify` | POST | Validate signature, check balance, simulate transfer | | `/settle` | POST | Execute `transferWithAuthorization` on-chain | Pricing: - **Free tier**: 1,000 transactions/month - **Standard**: $0.001 per transaction thereafter - **Base mainnet**: Zero gas fees (gas absorbed by facilitator) ```typescript import { HTTPFacilitatorClient } from "@x402/core/server"; const facilitator = new HTTPFacilitatorClient({ url: "https://api.cdp.coinbase.com/platform/v2/x402", }); ``` ### Custom Facilitator Run your own facilitator for full control over settlement: ```typescript import { createFacilitatorServer } from "@x402/core/facilitator"; import { ExactEvmScheme } from "@x402/evm/exact/server"; import { createWalletClient, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { base } from "viem/chains"; import type { Hex } from "viem"; // Facilitator wallet pays gas for settlement const facilitatorAccount = privateKeyToAccount( process.env.FACILITATOR_PRIVATE_KEY as Hex ); const walletClient = createWalletClient({ account: facilitatorAccount, chain: base, transport: http(), }); const server = createFacilitatorServer({ schemes: { "eip155:8453": new ExactEvmScheme({ walletClient }), }, }); server.listen(4000, () => { console.log("Custom facilitator on port 4000"); }); ``` ### Settlement Flow 1. Resource server sends the payment payload to facilitator `/verify` 2. Facilitator checks: signature validity, signer balance, authorization parameters, transfer simulation 3. If valid, resource server serves the response and calls facilitator `/settle` 4. Facilitator submits `transferWithAuthorization(from, to, value, validAfter, validBefore, nonce, signature)` to the USDC contract 5. Facilitator returns the transaction hash and receipt The facilitator cannot modify the amount or destination. It serves only as the transaction broadcaster. ## Agent-to-Agent Payments x402 enables autonomous agent commerce where AI agents discover services, negotiate prices, and pay without human intervention. ### Discovery Pattern ```typescript // Agent discovers paid endpoints via standard HTTP const discoveryResponse = await fetch("https://agent-service.com/.well-known/x402"); const services = await discoveryResponse.json(); // services contains available endpoints with pricing // { // "endpoints": [ // { "path": "/api/summarize", "price": "$0.02", "network": "eip155:8453" }, // { "path": "/api/translate", "price": "$0.01", "network": "eip155:8453" } // ] // } ``` ### Autonomous Payment Execution ```typescript import { wrapFetchWithPaymentFromConfig } from "@x402/fetch"; import { ExactEvmScheme } from "@x402/evm"; import { privateKeyToAccount } from "viem/accounts"; import type { Hex } from "viem"; const agentAccount = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as Hex); const agentFetch = wrapFetchWithPaymentFromConfig(fetch, { schemes: [ { network: "eip155:8453", client: new ExactEvmScheme(agentAccount) }, ], }); // Agent autonomously pays for another agent's service async function callPaidService(url: string, body: Record): Promise { const response = await agentFetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!response.ok) { throw new Error(`Service call failed: ${response.status}`); } return response.json(); } // Multi-step agent workflow: summarize -> translate -> store const summary = await callPaidService("https://summarizer.agent/api/summarize", { text: "Long document content...", }); const translation = await callPaidService("https://translator.agent/api/translate", { text: summary, targetLanguage: "es", }); ``` ## Pricing Strategies ### Per-Request (Fixed Price) Charge a flat fee per API call. Simplest model. ```typescript "GET /api/weather": { accepts: { scheme: "exact", network: "eip155:8453", price: "$0.01", payTo: process.env.PAYMENT_ADDRESS as `0x${string}`, }, description: "Weather data — $0.01 per request", }, ``` ### Tiered Pricing Different prices per endpoint based on compute cost. ```typescript const routes = { "GET /api/basic-query": { accepts: { scheme: "exact", network: "eip155:8453", price: "$0.001", payTo: paymentAddress, }, description: "Basic database query", }, "POST /api/ai-analysis": { accepts: { scheme: "exact", network: "eip155:8453", price: "$0.05", payTo: paymentAddress, }, description: "AI-powered analysis — higher compute cost", }, "POST /api/image-generation": { accepts: { scheme: "exact", network: "eip155:8453", price: "$0.10", payTo: paymentAddress, }, description: "Image generation — GPU cost", }, }; ``` ### Dynamic Pricing Adjust prices based on demand, time of day, or resource utilization. ```typescript function getDynamicPrice(basePrice: number, currentLoad: number): string { // Surge pricing: 1x at 0% load, up to 3x at 100% load const multiplier = 1 + (currentLoad / 100) * 2; const price = basePrice * multiplier; return `$${price.toFixed(4)}`; } ``` ## Replay Protection x402 uses two mechanisms to prevent payment replay: ### Random Nonces Every authorization includes a random 32-byte nonce. The USDC contract's `authorizationState` mapping tracks whether a `(from, nonce)` pair has been used. Attempting to settle with a used nonce reverts with `authorization is used or canceled`. ```typescript import { keccak256, encodePacked, toHex } from "viem"; // Generate a unique nonce per authorization function generateNonce(): Hex { const randomBytes = crypto.getRandomValues(new Uint8Array(32)); return toHex(randomBytes); } ``` ### Expiration Timestamps The `validBefore` field ensures authorizations cannot be settled after their time window. Best practice: set `validBefore` to `now + maxTimeoutSeconds` (typically 30-60 seconds). ```typescript const now = Math.floor(Date.now() / 1000); const authorization = { validAfter: BigInt(0), validBefore: BigInt(now + 60), // 60-second window nonce: generateNonce(), // ... other fields }; ``` If a facilitator does not settle within the `validBefore` window, the authorization expires and the client's funds are never moved. The client can safely retry with a new authorization. ## Permit2 Fallback For ERC-20 tokens that do not implement EIP-3009, x402 supports a Permit2-based fallback. ### How It Differs | Feature | EIP-3009 | Permit2 | |---------|----------|---------| | Gasless setup | Yes (native) | Requires one-time approval tx | | Token support | USDC only | Any ERC-20 | | Contract | Token contract itself | Uniswap Permit2 contract | | Proxy | None | `x402ExactPermit2Proxy` at `0x4020CD856C882D5fb903D99CE35316A085Bb0001` | The Permit2 path requires the user to have approved the Permit2 contract (`0x000000000022D473030F116dDEE9F6B43aC78BA3`). If the allowance is missing, the server returns HTTP 412 with error code `PERMIT2_ALLOWANCE_REQUIRED`. ## Supported Networks | Network | CAIP-2 Identifier | USDC Address | Status | |---------|-------------------|--------------|--------| | Base | `eip155:8453` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | Primary | | Base Sepolia | `eip155:84532` | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | Testnet | | Ethereum | `eip155:1` | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | Supported | | Arbitrum | `eip155:42161` | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` | Supported | | Optimism | `eip155:10` | `0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85` | Supported | | Polygon | `eip155:137` | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` | Supported | | Solana | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | Supported | Last verified: February 2026 ## References - **Spec**: https://github.com/coinbase/x402/tree/main/specs - **TypeScript SDK**: https://github.com/coinbase/x402/tree/main/typescript - **Python SDK**: https://pypi.org/project/x402/ - **Go SDK**: https://github.com/coinbase/x402/tree/main/go - **CDP Docs**: https://docs.cdp.coinbase.com/x402/welcome - **EIP-3009**: https://eips.ethereum.org/EIPS/eip-3009 - **x402.org**: https://www.x402.org - **Cloudflare x402 Foundation**: https://blog.cloudflare.com/x402/