How does an AI agent pay an x402 endpoint? (Client side, tested)
Quick answer: the client sends the request, gets 402 with a base64 PAYMENT-REQUIRED header, signs a USDC authorization for one of the listed options, and sends the same request again with a base64 PAYMENT-SIGNATURE header (x402 v2 HTTP spec). In JavaScript, @x402/fetch or @x402/axios does it in one call. Two things catch people out: by default the client library pays only recognised USD-pegged assets such as USDC and refuses any payment over $1 until you raise its cap, and the wallet needs USDC on the exact network the server asks for. It does not need ETH for gas.
The problem: a 402 your agent cannot answer
To a client that does not speak x402, a 402 looks like an error, and the call stops there. Even with x402 support, payments fail in ways that are hard to read: a default spending cap, a wallet funded on the wrong network, or an authorization that expired before it was used. Each one surfaces as a long error string or a second 402.
Even a funded, working client does not always get through. vet402, which buys from x402 endpoints with its own money and publishes the results, reported on its home page (catalog snapshot of 19 October 2026, UTC) that it had attempted a paid purchase against 8,457 endpoints, and that at least one attempt settled onchain for 6,155 of them (5,663 with the seller's receipt and 492 without; 72.8%). The other 2,302 were attempted and did not settle. That is vet402's settlement rate, not a count of the client errors above. When a payment does settle it is final, so check payTo, amount, asset and network before signing.
1. The flow, header by header
- Request. The client calls the URL like any other API.
- 402. The server answers
402 Payment Required. In v2 the payment terms are in thePAYMENT-REQUIREDheader as base64 JSON:x402Version,resourceand anacceptslist. Each option names ascheme(usuallyexact), anetworksuch aseip155:8453(Base), theamountin the token's smallest unit, theassetcontract,payToandmaxTimeoutSeconds. - Sign. The client picks an option it can pay and signs it. For USDC on EVM networks that is an EIP-3009
transferWithAuthorizationmessage: from, to, value, a validity window and a random nonce (exact EVM scheme). Nothing is sent onchain yet. - Retry. The client repeats the same request with
PAYMENT-SIGNATURE: base64 JSON holding the chosen option (accepted) and the signature. - Settle. The server verifies the payment, usually through a facilitator. The facilitator broadcasts the transfer and pays the gas, and the server returns the resource with a
PAYMENT-RESPONSEheader containing the transaction hash.
| What | x402 v2 | x402 v1 |
|---|---|---|
| Payment terms (server → client) | PAYMENT-REQUIRED header, base64 | JSON body of the 402 |
| Payment (client → server) | PAYMENT-SIGNATURE | X-PAYMENT |
| Receipt (server → client) | PAYMENT-RESPONSE | X-PAYMENT-RESPONSE |
| Network names | CAIP-2, e.g. eip155:8453 | names, e.g. base-sepolia |
| Price field | amount | maxAmountRequired |
Sources: the v2 and v1 HTTP transport specs. The current @x402/fetch reads a v2 PAYMENT-REQUIRED header and, if that is missing, a JSON body with x402Version: 1. It sends X-PAYMENT only for a v1 payment, which needs a v1 scheme registered on the v1 network name (such as base-sepolia, which eip155:* does not match). registerExactEvmScheme from @x402/evm/exact/client registers both (source). The snippet below registers v2 only, so against a v1 server it throws No client registered for x402 version: 1. To see what a given endpoint sends before you write any client code, paste it into our free x402 endpoint checker or follow testing an x402 endpoint with curl.
2. A runnable client
This uses only the official packages from the coinbase/x402 repository (version 2.28.0 on npm when we checked). @x402/axios has the same shape (wrapAxiosWithPaymentFromConfig), and @x402/mcp covers paying for MCP tool calls. The older x402-fetch and x402-axios packages are the v1 line, kept in the repo's legacy folder.
// pay.mjs: Node 18+ (ESM). npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPaymentFromConfig, decodePaymentResponseHeader } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
// A dedicated wallet that holds only a small amount of USDC. Never your main key.
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY); // "0x..." hex key
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{ network: "eip155:*", client: new ExactEvmScheme(account) }], // any EVM network the server offers
spendControls: { maxAmountPerPayment: "$0.10" }, // the default cap is $1; set your own
});
const res = await fetchWithPayment(process.env.API_URL, { method: "GET" });
console.log("status", res.status);
const receipt = res.headers.get("PAYMENT-RESPONSE"); // present when the server settled
if (receipt) console.log("settled", decodePaymentResponseHeader(receipt));
console.log(await res.text());
npm i @x402/fetch@2.28.0 @x402/evm@2.28.0 viem
EVM_PRIVATE_KEY=0x... API_URL=https://api.example.com/report node pay.mjs
What the wrapper does, from its source: if the first response is not 402 it returns it untouched. On a 402 it parses the terms, keeps the options whose network and scheme you registered, drops options with an unrecognised extra.paymentFlow, applies your spend controls and any policies, and prefers an authorization option (or one that omits paymentFlow) over upfront or escrow ones. The default selector then takes the first option left; pass paymentRequirementsSelector to choose differently. It signs and sends the request again with the payment header. If the request already carries PAYMENT-SIGNATURE or X-PAYMENT, it throws Payment already attempted instead of paying twice.
We ran it on 19 October 2026 (Node 20) against a local @x402/express route priced at $0.001 on Base Sepolia, using the public x402.org test facilitator and a new, unfunded key. This is the exchange it produced, shortened from our log:
-> GET /weather (no payment header)
<- 402 PAYMENT-REQUIRED: eyJ4NDAy... (amount 1000 = $0.001, network eip155:84532)
-> GET /weather PAYMENT-SIGNATURE: eyJ4NDAy...
<- 402 PAYMENT-REQUIRED error: "invalid_exact_evm_insufficient_balance"
Note that the failure came back as a new 402 with the reason in the error field of PAYMENT-REQUIRED, not in a PAYMENT-RESPONSE header. Read both when you debug. More examples, including the axios, MCP and custom (no wrapper) clients, are in the repo's client examples.
3. Fund the wallet
- USDC, on the network the server lists. Check
accepts[].networkandasset. USDC on Base mainnet will not pay a Base Sepolia route, or the other way round. - No ETH needed for USDC with
exact. The spec's EIP-3009 method is "truly gasless" for the client: you only sign, and the facilitator pays the gas (exact EVM scheme). Tokens without EIP-3009 use Permit2, which needs a one-timeapprovethat costs gas unless the server supports a sponsoring extension. - Testnet first. Circle's faucet gives test USDC on Base Sepolia. Our Base Sepolia guide covers the seller side of the same test.
- Use a separate wallet. The key signs payments without asking anyone, so give the agent a wallet that holds only what you would accept losing, and set
spendControls.
4. The $1 default cap
@x402/core sets DEFAULT_MAX_AMOUNT_PER_PAYMENT to "$1" for the stablecoins it recognises (x402Client source, spend-controls example). We pointed the default client, with no spendControls, at our own $29 file endpoint. It sent one request, got the 402, and threw before signing anything:
Failed to create payment payload: All payment requirements were rejected by spendControls.maxAmountPerPayment ($1, including USDC). ...
That is a sensible default for an agent making $0.01 calls, but it means a client copied from a quickstart cannot buy anything over a dollar. Set spendControls: { maxAmountPerPayment: "$30" } or whatever ceiling you accept. Setting maxAmountPerPayment: false removes the dollar cap but still refuses tokens the client does not recognise; spendControls: false turns off both checks. The docs' buyer quickstart states the same defaults. We would not remove the cap for an unattended agent.
5. Common failures and what they mean
| You see | Cause | Fix |
|---|---|---|
No network/scheme registered for x402 version: 2 ... | None of the server's options match a network you registered (we got this by registering only eip155:8453 against a Base Sepolia route). | Register eip155:* or the exact network in accepts. |
rejected by spendControls.maxAmountPerPayment ($1 ...) | The price is above the default $1 cap. | Raise maxAmountPerPayment. |
Second 402 with invalid_exact_evm_insufficient_balance | Not enough USDC on that network. | Fund the wallet on the listed network. |
invalid_exact_evm_payload_authorization_valid_before | The signed authorization expired (window set from maxTimeoutSeconds). | Sign a fresh one. Do not cache payment headers. |
invalid_exact_evm_nonce_already_used | A payment header was replayed. | Every payment needs a new signature and nonce. |
Failed to parse payment requirements | The 402 has no readable PAYMENT-REQUIRED header and no v1 JSON body. In a browser, the server may not expose the header to scripts. | Check the endpoint with the checker. Browser setup: PAYMENT-REQUIRED header and CORS. |
| Error after paying, with a transaction hash | Possibly settlement_pending: broadcast, not yet confirmed. | Look up the hash onchain before retrying. |
Error names are from the core spec's error list and the EVM facilitator's error constants. The spec's list says insufficient_funds; the facilitator we tested returned the more specific invalid_exact_evm_insufficient_balance, so match on both.
Browser clients: PAYMENT-SIGNATURE is not a CORS-safelisted request header, so a cross-origin paid request triggers a preflight. The fetch wrapper also sets Access-Control-Expose-Headers as a request header on the paid retry, so the server's Access-Control-Allow-Headers has to allow both, and its Access-Control-Expose-Headers response header has to list PAYMENT-REQUIRED and PAYMENT-RESPONSE.
6. Before an agent pays
- Decode the 402 and confirm the
payTo,amount,assetandnetworkare what you expect. Our checker shows them in plain terms. - Prefer endpoints with a payment history. CDP Bazaar search ranks by a blend of query relevance and quality, and quality includes recent call volume and unique payers as well as how complete the description and schema are (Coinbase docs; see also how Bazaar listing works). Services such as vet402 publish whether paid calls delivered.
- Keep the
PAYMENT-RESPONSEreceipt. When the server sends one, itstransactionfield is the hash (withnetworkandpayer). The transfer itself is onchain: if the header is missing, or the reason issettlement_pending, look the hash up before you retry.
Selling rather than buying? See charging for an MCP tool and the other guides.
Sponsor slot open: one short text line here for 30 days. See terms.
FAQ
How does an AI agent pay an x402 endpoint?
It sends the request, gets HTTP 402 with a base64 PAYMENT-REQUIRED header listing the accepted payment options, signs a payment for one of them, and repeats the same request with a base64 PAYMENT-SIGNATURE header. The server verifies the payment, usually through a facilitator, settles it, and returns the resource with a PAYMENT-RESPONSE header. The @x402/fetch and @x402/axios packages do all of this inside one call.
What is the difference between x402 v1 and v2 headers?
In v2 the server sends the requirements in the PAYMENT-REQUIRED header, the client pays with PAYMENT-SIGNATURE, and the receipt comes back in PAYMENT-RESPONSE. In v1 the requirements were in the JSON body of the 402, the client paid with X-PAYMENT, and the receipt came back in X-PAYMENT-RESPONSE. The current @x402/fetch can read a v1 402, but it only sends X-PAYMENT if you also register a v1 scheme for the v1 network name (for example with registerExactEvmScheme from @x402/evm/exact/client). A client that registers only eip155:* pays v2 servers only.
Does the paying wallet need ETH for gas?
Not for USDC with the exact scheme. The client signs an EIP-3009 transferWithAuthorization message and the facilitator broadcasts it and pays the gas. Tokens without EIP-3009 use the Permit2 method, which needs a one-time approval that costs gas unless the server supports a sponsoring extension.
Why does my x402 client refuse to pay a $29 endpoint?
@x402/core (2.28.0, the version we tested) has a default per-payment cap of $1 on the stablecoins it recognises. A higher price throws "rejected by spendControls.maxAmountPerPayment ($1 ...)" before any payment is sent. Set spendControls.maxAmountPerPayment to a value you accept, for example "$30".
I paid but got an error. Is the money gone?
Check the PAYMENT-RESPONSE header and the transaction hash before retrying. The spec allows a settlement_pending result: the transfer was broadcast but not yet confirmed, and it may still land. Retrying with a fresh authorization in that state could pay twice. If no transaction was broadcast, nothing moved.
Header names and flow are from the coinbase/x402 v1 and v2 HTTP transport specs and the exact EVM scheme spec, read on 2026-10-19. Library behaviour is from the @x402/fetch, @x402/axios, @x402/evm and @x402/core 2.28.0 source and from our own runs that day. All our runs used new, unfunded keys, so nothing was paid. vet402 figures are as published on its site, not verified by us. Libraries change; pin versions and re-test before relying on this.