Skip to main content
x402 is the HTTP 402 payment protocol. A paywalled resource answers a request with 402 Payment Required and a payment-required header describing what it accepts. The Payments primitive gives each agent its own crypto wallet (USDC on Base) and lets it settle those 402s autonomously: quote the price, sign a USDC payment, retry the request, keep the receipt.
Payments is opt-in. Unlike most primitives it is disabled in a fresh AccountKit and must be enabled explicitly — see Account Kits. The wallet is per-agent: there is no company-level fallback mount, so CLI and API calls need a subject (naive use <user>).

CLI First

Tools

The wallet’s admin verbs (fund, transfer, policy, sweep) are deliberately not MCP tools and not in the SDK — they are the operator surface only. See Governance.

How it works

pay is the full buy-side flow. quote runs only the first two steps, which is why it has no side effects at all.
1

Fetch

The resource is fetched normally. If it returns anything other than 402, nothing was paywalled — pay returns the body with paid: false and no receipt is written.
2

Read the requirements

On a 402, the payment-required header is decoded into a list of accepts options (amount, asset, network, payTo).
3

Select under policy

The options are filtered against the wallet’s perTxMax, the per-call max_amount, and the allowed networks. The effective cap is min(max_amount, perTxMax).
4

Check balance and budget

The wallet balance and (if set) the rolling dailyBudget counter are checked. A refusal here happens before anything is signed and emits a payment.denied event.
5

Sign

The custody provider signs an EIP-3009 TransferWithAuthorization. The provider re-enforces perTxMax inside the custody plane at signing time.
6

Retry once

The request is retried with a PAYMENT-SIGNATURE header attached.
7

Hard stop

If the retried request fails, that is the end — there is no second retry. A payment.failed event is emitted and the call throws. On success, the payment-response envelope is stored and a receipt is written.

Quote

accepts[].amount is in atomic units. USDC has 6 decimals, so 500000 = 0.50 USDC. Everything else in this primitive — perTxMax, dailyBudget, --max-amount, fund/transfer amount, and a receipt’s amount — is a decimal USDC string (e.g. "1.00").
If the resource is not paywalled, quote returns free: true with the real HTTP status and an empty accepts — fetch it normally, no payment needed.

Pay

Returns the resource body plus the receipt:

Receipts

Every settled payment is recorded. direction is buy (paid out) or sell (received).
GET /v1/users/{user_id}/payments/receipts and GET /v1/users/{user_id}/wallet/balances return bare arrays, not wrapped objects. Receipt and wallet JSON fields are camelCase (txHash, payTo).

Spend control

This is the part that most deserves reading. Unlike Cards and Trading, payments have no approval workflow — there is no pending_approval, no approval queue, and that is deliberate, not an omission. Three controls bound a payment instead:
Why no approval prompt? perTxMax is enforced twice: once at runtime in the pay flow, and again as a static account policy inside the custody plane, checked when the payment is signed. That second check holds even if the agent runtime is fully compromised — a guarantee an approval prompt cannot make, since a compromised runtime can simply stop asking. Fund the wallet with what you can afford to lose, set perTxMax, and the ceiling is structural.
There are deliberately no origin or payee allowlists and no velocity rules. --max-amount (or max_amount) adds a per-call ceiling on top of perTxMax for one specific payment.

Governance

The wallet splits into two surfaces. Per spec §3.7, the admin verbs are operator-surface only — not agent tools: The operator verbs are gated server-side by enforceWalletAdmin, and the CLI refuses them client-side too. An agent key is refused. They are also absent from the MCP tool list and the SDK entirely — non-exposure plus an explicit gate. An AccountKit can additionally deny wallet administration for a user by listing the payments.wallet.admin capability in primitives_config.payments.capabilities.deny.

Agents as code

Declare a wallet on an agent with has.crypto. This implies the payments slug in the allowlist and adds a crypto_wallet provisioning step.
Do not reach for limits.approve: [skills.payments.pay]. Payments has no approval workflow, so such a rule compiles but is never enforced — it would give you a gate you believe in and do not have. Bound payments with has.crypto’s perTxMax / dailyBudget and with how much you fund the wallet.

Configuration

Network: USDC on Base (eip155:8453).
When WALLET_ENABLED is false, the crypto_wallet provisioning step resolves to needs_action — it never fails the deployment. With WALLET_PROVIDER=cdp, credentials are BYO from the tenant vault; missing creds produce a typed WalletNotConfigured error and a “connect your CDP account” prompt rather than an opaque failure.

Custody backends

Typical Workflows (Agent Perspective)

Paying for a paywalled resource

The resource turned out to be free

The payment was refused

Setting up a wallet (operator, not the agent)