Guide · checked 2026-10-19 · about 7 minutes

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

  1. Request. The client calls the URL like any other API.
  2. 402. The server answers 402 Payment Required. In v2 the payment terms are in the PAYMENT-REQUIRED header as base64 JSON: x402Version, resource and an accepts list. Each option names a scheme (usually exact), a network such as eip155:8453 (Base), the amount in the token's smallest unit, the asset contract, payTo and maxTimeoutSeconds.
  3. Sign. The client picks an option it can pay and signs it. For USDC on EVM networks that is an EIP-3009 transferWithAuthorization message: from, to, value, a validity window and a random nonce (exact EVM scheme). Nothing is sent onchain yet.
  4. Retry. The client repeats the same request with PAYMENT-SIGNATURE: base64 JSON holding the chosen option (accepted) and the signature.
  5. 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-RESPONSE header containing the transaction hash.
Whatx402 v2x402 v1
Payment terms (server → client)PAYMENT-REQUIRED header, base64JSON body of the 402
Payment (client → server)PAYMENT-SIGNATUREX-PAYMENT
Receipt (server → client)PAYMENT-RESPONSEX-PAYMENT-RESPONSE
Network namesCAIP-2, e.g. eip155:8453names, e.g. base-sepolia
Price fieldamountmaxAmountRequired

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

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 seeCauseFix
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_balanceNot enough USDC on that network.Fund the wallet on the listed network.
invalid_exact_evm_payload_authorization_valid_beforeThe signed authorization expired (window set from maxTimeoutSeconds).Sign a fresh one. Do not cache payment headers.
invalid_exact_evm_nonce_already_usedA payment header was replayed.Every payment needs a new signature and nonce.
Failed to parse payment requirementsThe 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 hashPossibly 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

Selling rather than buying? See charging for an MCP tool and the other guides.

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.