Skip to main content
Checkouts created with the Coinbase Business Checkouts API return an 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

Paying-agent setup (a CDP account, funded USDC wallet, Node.js) is not required to launch. Skip it unless you want to sanity-check with a test agent at the end of this guide.

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; the rat#view scope is required (see Authentication).
The response includes both a hosted url (for humans) and an x402_url (for agents):
Share 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 until status reaches a terminal value:
In the expected path the checkout moves 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

A COMPLETED 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 own x402_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_ID and CDP_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 through fromCdpEvmAccount, so no raw private keys are needed.
  • Node.js 22.18 or later, and npm.
Store them in .env:
Point an x402 client at the 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

The snippets below are ES modules that use top-level await. Add "type": "module" to your package.jsonnpm 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).
A 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.

Next steps