x402_url — a payment endpoint an AI agent or x402 client can pay programmatically. Paying it authorizes a gasless USDC payment on Base (EIP-3009); Coinbase then captures and settles it server-side, with no hosted page, wallet pop-up, or human required.
You do not implement the paying-agent side to go live. Create a checkout, expose the x402_url, and treat the checkout reaching COMPLETED as settlement.
Checkouts use an authorize-then-capture flow. Paying the
x402_url authorizes (escrows) the funds; Coinbase captures and settles them server-side. Treat the checkout reaching COMPLETED as the source of truth for settlement — see Confirm settlement.Prerequisites
- A Coinbase Business account with a CDP API key. See Authentication.
1. Create a checkout
Create a checkout with the Create Checkout endpoint. Authenticate with a JWT Bearer token signed with your CDP API key secret; therat#view scope is required (see Authentication).
url (for humans) and an x402_url (for agents):
x402_url with agents (or your x402 client). Only single-checkout responses carry it — List Checkouts does not — so keep the value from the create response or re-fetch the checkout by ID.
Humans can pay the hosted url instead — open it in a browser, connect a wallet on Base, and approve the (gasless) USDC authorization. Then confirm settlement.
2. Confirm settlement
After a payment is authorized, poll the Get Checkout endpoint untilstatus reaches a terminal value:
ACTIVE → PROCESSING → COMPLETED. PROCESSING is set when Coinbase receives the successful authorization and starts the capture, and COMPLETED when the capture settles. Do not require PROCESSING: you may never observe it if you poll infrequently. Ordinary payment rejections (bad signature, wrong amount, insufficient payer balance) come back as another 402 and leave the checkout ACTIVE, so they are safe to retry. A checkout that has reached FAILED is not — create a new one instead.
You can also subscribe to webhooks for real-time notifications instead of polling.
Refunds
ACOMPLETED or PARTIALLY_REFUNDED checkout can be refunded (fully or partially, up to the remaining amount) with the Refund Checkout endpoint. Refunds are asynchronous — poll the checkout to watch refundedAmount and status (REFUNDED or PARTIALLY_REFUNDED).
Optional: sanity-check with a test agent
This is not required to go live. Use it only if you want to pay your ownx402_url once and confirm the checkout reaches COMPLETED.
Agents pay x402_url with an x402 client (the CDP SDK is optional). Humans pay the hosted url in a browser. The sample below uses a CDP-managed account as one convenient test agent — it is not how your customers’ agents must pay.
Checkouts advertise the EVM auth-capture scheme, which matches the authorize-then-capture flow.
CdpX402Client registers this scheme by default. If you use a client that handles only the exact
scheme, register AuthCaptureEvmScheme from @x402/evm
to pay a checkout.
To run the sample you need:
- A CDP Secret API Key (
CDP_API_KEY_IDandCDP_API_KEY_SECRET), a Wallet Secret (CDP_WALLET_SECRET, generated separately under Security in the non-custodial wallet dashboard), and USDC on Base to fund the CDP-managed account. The sample signs throughfromCdpEvmAccount, so no raw private keys are needed. - Node.js 22.18 or later, and npm.
.env:
x402_url from step 1. It is a POST endpoint: the first request returns 402 Payment Required with the payment requirements base64-encoded in the PAYMENT-REQUIRED response header, and the client retries with the signed payment in the PAYMENT-SIGNATURE request header. On success, the authorization result comes back in the PAYMENT-RESPONSE response header. With @x402/fetch, wrapFetchWithPayment handles that challenge-and-retry automatically.
1
Install the client packages
await. Add "type": "module" to your package.json — npm init -y writes "commonjs", which fails on the import statements. Run each snippet with node <filename>.ts; Node.js 22.18 or later runs TypeScript directly.2
Create and fund the payer account
Run this first. It creates a CDP-managed EVM account and prints its address. Send that address USDC on Base before you pay — the server rejects an underfunded payer with another
402 whose message names insufficient balance.3
Pay the checkout
wrapFetchWithPayment signs whatever the server asks for, so cap it. applySpendControls bounds every payment the client will sign — raise or lower the cap to suit your agent before you point it at mainnet.Sandbox checkouts do not currently return an
x402_url, so agentic payments cannot be tested there. Use a live checkout with a small amount, and only fund the address with what you intend to pay. Create the checkout after the payer is funded — unpaid checkouts expire at expiresAt (24 hours from creation if you omit it).200 response means the payment was authorized, not settled. The funds are held in escrow until Coinbase captures them. Confirm the checkout reaches COMPLETED (step 2) rather than treating the HTTP response alone as final.