> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cdp.coinbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# x402 in the CDP SDK

The [CDP SDK](/get-started/overview) ships a set of x402 primitives that make it much
easier to pay for and gate x402-protected APIs using CDP-managed wallets and the CDP hosted
facilitator, with no private keys to store.

<Note>
  The CDP SDK x402 integration is **TypeScript only** at this time. For Python and Go, use the
  vanilla [x402 packages](https://github.com/x402-foundation/x402) directly, as shown throughout the
  [Quickstart for Buyers](/x402/quickstart-for-buyers) and [Quickstart for Sellers](/x402/quickstart-for-sellers).
</Note>

## What the CDP SDK adds

[x402](https://www.x402.org/) is an HTTP-native payment protocol: a resource server answers a
request with `402 Payment Required` and a list of accepted payment options, the client signs a
payment and retries with a `PAYMENT-SIGNATURE` header, and a [facilitator](/x402/core-concepts/facilitator)
verifies and settles the payment on-chain.

The CDP SDK layers four conveniences on top of the raw `@x402/*` packages:

* **CDP-managed wallets** — no private keys to store. Signing wallets (EVM/Solana EOA or EVM Smart Contract Wallets) are provisioned and signed by CDP.
* **Hosted facilitator** — `createCdpFacilitatorClient()` is a drop-in for the CDP hosted
  facilitator, authenticated with your CDP API keys via JWT.
* **Spend controls** — per-payment and rolling caps, network/asset/payee allowlists, and an
  approaching-limit callback, all enforced client-side.
* **Direct signing** — `account.signX402Payment()` signs a payment payload with any CDP account for
  custom payment flows that don't go through an x402 client SDK (e.g. non-HTTP transports like gRPC
  or MCP), with no `/x402` subpath import required.

Everything is **drop-in compatible** with the upstream `@x402` ecosystem: `CdpX402Client` extends
`x402Client`, `X402Server` extends `x402HTTPResourceServer`, and `createCdpFacilitatorClient()` returns
a standard `HTTPFacilitatorClient`. The SDK ships **no HTTP framework packages of its own**, you
thread its primitives into the `@x402/*` client wrappers and server middleware you already use
(`@x402/fetch`, `@x402/axios`, `@x402/express`, `@x402/hono`, `@x402/next`).

## Installation

Everything x402 lives in a subpath export of the main package:

```bash theme={null}
npm install @coinbase/cdp-sdk
```

```typescript theme={null}
import {
  CdpX402Client,
  createX402Server,
  createCdpFacilitatorClient,
} from "@coinbase/cdp-sdk/x402";
```

### The `@x402/*` packages are optional peer dependencies

`@x402/core`, `@x402/evm`, `@x402/svm`, and `@x402/extensions` are **optional peer dependencies** of
`@coinbase/cdp-sdk`. They are **not installed transitively**, so a bare `npm install
@coinbase/cdp-sdk` does not pull them in. This keeps consumers who never touch x402 from installing
four extra packages, while keeping a single shared copy of each `@x402` package for consumers who do.

Install the SDK together with x402 primitives:

```bash theme={null}
npm install @coinbase/cdp-sdk @x402/core @x402/evm @x402/svm @x402/extensions
```

| Package            | Used for                                                          |
| ------------------ | ----------------------------------------------------------------- |
| `@x402/core`       | `x402Client`, `x402ResourceServer`, `HTTPFacilitatorClient`       |
| `@x402/evm`        | EVM signer adapter; `exact` / `upto` / `batch-settlement` schemes |
| `@x402/svm`        | Solana `exact` scheme                                             |
| `@x402/extensions` | x402 extensions, e.g. bazaar discovery extension                  |

If using HTTP, install the integration package that fits your use case::

* **Clients:** `@x402/fetch` (`wrapFetchWithPayment`) or `@x402/axios` (`wrapAxiosWithPayment`).
* **Servers:** `@x402/express`, `@x402/hono`, or `@x402/next`.

## Environment variables

| Variable                      | Required for                                | Notes                                                                   |
| ----------------------------- | ------------------------------------------- | ----------------------------------------------------------------------- |
| `CDP_API_KEY_ID`              | client, server, facilitator access          | CDP API key ID                                                          |
| `CDP_API_KEY_SECRET`          | client, server, facilitator access          | CDP API key secret                                                      |
| `CDP_WALLET_SECRET`           | client, server (when provisioning a wallet) | Not needed when the server uses `payToConfig: { type: "address" }`      |
| `CDP_X402_SERVER_ENVIRONMENT` | server (optional)                           | `"production"` (default) or `"development"` — controls default networks |
| `CDP_X402_RPC_URLS`           | client (optional)                           | JSON map of CAIP-2 → RPC URL for non-Base networks                      |

All credential fields can also be passed explicitly via config; explicit values take precedence over
env vars.

## The primitives

This is the intended way to use x402 with the CDP SDK: pick the primitive that matches what you're
building, and it handles wallet provisioning, scheme registration, and facilitator wiring for you.

* **[CDP Client](#cdp-client)** — pay for an x402-protected API from a CDP-managed wallet.
* **[CDP Server](#cdp-server)** — gate your API and settle through the CDP facilitator.
* **[CDP Facilitator](#cdp-facilitator)** — already have an x402 server; just want CDP settlement.
* **[Direct signing](#direct-signing)** — the exception case: sign a payment yourself, outside of a
  client SDK, for a custom payment flow.

### CDP Client

`CdpX402Client` is a drop-in extension of `x402Client` that lazily provisions a CDP-managed wallet
(no private keys to store) and registers payment schemes on first use. Pass it to any upstream x402
client wrapper — `wrapFetchWithPayment` (`@x402/fetch`) or `wrapAxiosWithPayment` (`@x402/axios`).

```typescript theme={null}
import { CdpX402Client } from "@coinbase/cdp-sdk/x402";
import { wrapFetchWithPayment } from "@x402/fetch";

// Reads CDP_API_KEY_ID, CDP_API_KEY_SECRET, CDP_WALLET_SECRET from env.
const client = new CdpX402Client();

const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("https://api.example.com/paid-endpoint");
```

Full example in the repo [here](https://github.com/coinbase/cdp-sdk/blob/main/examples/typescript/x402/clients/payForApi.ts).
See the [Quickstart for Buyers](/x402/quickstart-for-buyers) for the full flow, including funding the
wallet and discovering services via the Bazaar.

#### Spend controls

Spend controls are enforced **client-side** on any `x402Client`. With `CdpX402Client`, pass
`spendControls` in the constructor (the SDK wires them internally); for a plain `x402Client`, call
`applySpendControls(client, controls)` directly.

```typescript theme={null}
const USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";

const client = new CdpX402Client({
  spendControls: {
    maxAmountPerPayment: { atomic: 10_000n, asset: USDC_BASE },
    maxCumulativeSpend: { atomic: 50_000n, asset: USDC_BASE },
    maxCumulativeSpendWindow: "24h",
    allowedNetworks: ["eip155:8453"],
  },
});
```

**Supported controls:**

| Field                        | Type                     | Notes                                                                                                                                                    |
| ---------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `maxAmountPerPayment`        | `Amount`                 | Hard per-payment cap.                                                                                                                                    |
| `maxCumulativeSpend`         | `Amount`                 | Rolling cumulative cap. Requires `asset` (cross-asset atomic units can't be summed).                                                                     |
| `maxCumulativeSpendWindow`   | `Duration`               | Rolling window, e.g. `"24h"`. Omit for all-time.                                                                                                         |
| `allowedNetworks`            | `Network[]`              | CAIP-2 network allowlist.                                                                                                                                |
| `allowedAssets`              | `Asset[]`                | Asset allowlist.                                                                                                                                         |
| `allowedPayees`              | `Address[]`              | Payee allowlist.                                                                                                                                         |
| `onApproachingLimit`         | `(spent, limit) => void` | Fired when confirmed spend crosses a threshold.                                                                                                          |
| `approachingLimitThresholds` | `number[]`               | Fractions of the cap that trigger the callback. Default `[0.8, 0.95]`.                                                                                   |
| `maxLedgerEntries`           | `number`                 | Ledger cap. Default `10_000`.                                                                                                                            |
| `store`                      | `SpendStore`             | Durable backend. Default is in-memory and process-local; provide a `SpendStore` (Redis, Postgres, DynamoDB) to enforce caps across restarts or replicas. |

**When a payment is blocked**, the client throws a `SpendControlError` with a machine-readable `code`.
Switch on `err.code` rather than matching message text:

* `per_payment_cap`
* `cumulative_cap`
* `network_not_allowed`
* `asset_not_allowed`
* `payee_not_allowed`
* `amount_unparseable`
* `ledger_capacity_exceeded`
* `configuration_invalid`
* `already_applied`

Full example in the repo
[here](https://github.com/coinbase/cdp-sdk/blob/main/examples/typescript/x402/clients/payForApiWithSpendControls.ts).

### CDP Server

`createX402Server` provisions a receiver wallet, wires the CDP hosted facilitator, registers payment
schemes and extensions, and returns a fully initialized `X402Server` (which extends
`x402HTTPResourceServer`). Pass it to any framework middleware.

```typescript theme={null}
import express from "express";
import { createX402Server } from "@coinbase/cdp-sdk/x402";
import { paymentMiddlewareFromHTTPServer } from "@x402/express";

const app = express();

// Reads CDP_API_KEY_ID, CDP_API_KEY_SECRET, CDP_WALLET_SECRET from env.
const server = await createX402Server({
  routes: { "GET /report": { price: "$0.01", description: "AI-generated report" } },
});

// `server` IS an x402HTTPResourceServer — pass it to any x402 middleware.
app.use(paymentMiddlewareFromHTTPServer(server));
console.log("Receiving EVM payments at", server.payToEvmAddress);
```

Full example in the repo [here](https://github.com/coinbase/cdp-sdk/blob/main/examples/typescript/x402/servers/express/server.ts)
(run with `APPROACH=2`). See the [Quickstart for Sellers](/x402/quickstart-for-sellers) for Hono/Next.js
variants, `payToConfig`, mainnet setup, and discovery metadata.

### CDP Facilitator

If you already run an `x402ResourceServer` (or call a framework's `paymentMiddleware` manually) and
just want CDP settlement, drop `createCdpFacilitatorClient()` in as the facilitator — it returns a
standard `HTTPFacilitatorClient`, so it's a one-line swap with no other changes to your server setup.

```typescript theme={null}
import { x402ResourceServer } from "@x402/core/server";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { createCdpFacilitatorClient } from "@coinbase/cdp-sdk/x402";

// Reads CDP_API_KEY_ID and CDP_API_KEY_SECRET from env.
const facilitatorClient = createCdpFacilitatorClient();

// The vanilla x402 way to build a resource server — only the facilitator argument changes.
const server = new x402ResourceServer(facilitatorClient)
  .register("eip155:8453", new ExactEvmScheme());
```

Only API key credentials are needed — no wallet secret, since the facilitator verifies and settles
payments but never signs them. Full example in the repo
[here](https://github.com/coinbase/cdp-sdk/blob/main/examples/typescript/x402/servers/express/server.ts)
(run with `APPROACH=1`).

### Direct signing

Every CDP account (EVM server account, EVM smart account, and Solana account) also exposes
`signX402Payment(paymentRequired, acceptedIndex)`, which signs an x402 payment payload directly — no
`CdpX402Client`, no `wrapFetch`/`wrapAxios` wrapper, no `/x402` subpath import. Reach for this only
when you're building a custom payment flow that doesn't go through a client SDK — e.g.
attaching a payment to a non-HTTP transport (gRPC, MCP, a message queue) instead of an HTTP retry.

```typescript theme={null}
import { CdpClient } from "@coinbase/cdp-sdk";

const cdp = new CdpClient();
const account = await cdp.evm.getOrCreateAccount({ name: "agent" });

// paymentRequired is the x402 PaymentRequired object returned by a resource server
// (e.g. the body of a 402 response); acceptedIndex selects which entry in
// paymentRequired.accepts to sign.
const payment = await account.signX402Payment(paymentRequired, 0);
```

The same method exists on EVM smart accounts (`exact` only) and Solana accounts (`exact` only). See
the [MCP Server guide](/x402/mcp-server) for a full example that returns the signed payment in an MCP
tool result's `_meta`.

## Payment schemes

The CDP SDK works with three schemes:

| Scheme             | Networks              | Behavior                                                                                    |
| ------------------ | --------------------- | ------------------------------------------------------------------------------------------- |
| `exact` (default)  | EVM + Solana          | Fixed amount, locked at signing time.                                                       |
| `upto`             | EVM-only (`eip155:*`) | Usage-based: client authorizes a max; server settles the actual amount (≤ max) via Permit2. |
| `batch-settlement` | EVM-only (`eip155:*`) | High-throughput payment channels: deposit once, sign off-chain vouchers per request.        |

A `CdpX402Client` with an EOA wallet registers `exact` + `upto` for EVM and `exact` for Solana. Smart
Contract Wallets register `exact` only (their ERC-1271/ERC-6492 contract signatures settle via the
EIP-3009 `exact` flow). On the server, `createX402Server` registers `exact` + `upto` for EVM and
`exact` for Solana by default, and rejects `upto`/`batch-settlement` on non-EVM networks.

## Extensions (auto-injected by `createX402Server`)

CDP extensions are advertised automatically on every route:

| Extension                    | When applied                                 | Purpose                                                                                   |
| ---------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `eip2612GasSponsoring`       | routes with an EVM accept option             | Sponsored Permit2 via EIP-2612 permit — the CDP facilitator covers gas.                   |
| `erc20ApprovalGasSponsoring` | routes with an EVM accept option             | Sponsored ERC-20 `approve(Permit2, MaxUint256)` — the CDP facilitator covers gas.         |
| `bazaar`                     | routes with a parseable `"METHOD /path"` key | Minimal discovery metadata so your endpoint is listed in the [x402 Bazaar](/x402/bazaar). |

Gas-sponsoring declarations are harmless on EIP-3009 / Solana routes. Any `extensions` you set on a
route always take precedence over the auto-injected values — override `extensions.bazaar` to supply
richer discovery metadata.

## Networks and RPC

CAIP-2 identifiers are used throughout. `createX402Server` defaults to Base mainnet + Solana mainnet
in `"production"` and Base Sepolia + Solana Devnet in `"development"`. The SDK exports constant sets
(`CDP_SERVER_DEFAULT_NETWORKS`, `CDP_SERVER_DEVELOPMENT_NETWORKS`, and their EVM/SVM-only variants)
if you want to reference them directly.

On the client side, **Base and Base Sepolia resolve an RPC automatically** via your CDP project's
authenticated node endpoint — no configuration needed. All other EVM networks (Polygon, Arbitrum,
Optimism, and so on) have no default RPC; supply one via the `rpcUrls` config option or the
`CDP_X402_RPC_URLS` env var:

```json theme={null}
{ "eip155:137": "https://your-rpc-provider.example.com/polygon" }
```

## Funding

Client wallets need funds before they can pay. Print a client's address with
`client.getAddresses()`, then fund it. On Base Sepolia (testnet), fund with USDC using:

* **CDP Faucet (portal):** [portal.cdp.coinbase.com](https://portal.cdp.coinbase.com) → "Onchain Tools" → "Faucet"
* **Programmatically:** `cdp.evm.requestFaucet({ address, network: "base-sepolia", token: "usdc" })`

The CDP faucet funds the same wallets the CDP x402 facilitator settles against — no separate faucet
needed.

## What to read next

* [Quickstart for Buyers](/x402/quickstart-for-buyers) — pay for x402-protected APIs with a CDP wallet
* [Quickstart for Sellers](/x402/quickstart-for-sellers) — gate your API and settle via the CDP facilitator
* [MCP Server with x402](/x402/mcp-server) — pay for APIs from an MCP server
* [x402 Bazaar](/x402/bazaar) — discover and list x402 services
