Adding x402 to a real API, step by step.
Our publish endpoint has accepted autonomous stablecoin payments since June, and the numbers are public. This is the other half: what we actually built to get there, in the order we would build it again, including the parts that failed on mainnet first.
What does it take to accept x402 payments?
Five things: an endpoint that answers unpaid requests with HTTP 402 and machine-readable payment requirements; a facilitator to verify and settle the signed USDC payment; an idempotent grant so retries cannot double-charge or double-mint; pricing that cannot bleed you (time-box anything with ongoing cost); and a discovery listing so agents can find you. We run all five in production on a Cloudflare Worker with no SDK. Days of work, plus a week of gotchas.
Step 1: price the thing honestly
Before any code: decide what a one-time payment buys, because an anonymous $1 payment that grants a perpetual obligation is a slow leak. Our model: $1 provisions a persistent API key at free-tier limits; individual unlocks are purchasable on top; and anything with ongoing cost to us (custom domains, elevated limits) is time-boxed at $5 per 30 days, renewed via a fresh 402. Bounded things are one-time, unbounded things expire. This rule shaped everything downstream.
Step 2: return a real 402, both versions
The protocol is HTTP with one twist: an unpaid request gets status 402 plus payment requirements (asset, network, amount, receiver). The twist inside the twist is versioning: x402 v2 carries requirements in a PAYMENT-REQUIRED header, v1 carries them in the body, and real wallets are split across both. We shipped v2-only first and watched a funded Coinbase wallet fail to pay us because it only spoke v1. Serve both. The client retries with a signed payment header; that is the whole dance. Our 402 requirements look like this (v2 shape; see the x402 docs for the full schema):
{
"x402Version": 2,
"accepts": [{
"scheme": "exact",
"network": "eip155:8453", // Base mainnet
"asset": "0x8335...2913", // USDC contract
"maxAmountRequired": "1000000", // $1.00, 6 decimals
"payTo": "0xcc98...4333",
"resource": "https://api.stacktr.ee/provision",
"extra": { "name": "USD Coin", "version": "2" } // EIP-712 domain, see gotchas
}]
} Step 3: verify and settle via a facilitator
You do not need chain infrastructure. A facilitator exposes verify and settle endpoints: you POST the payment header, it checks the EIP-3009 signature, executes the on-chain USDC transfer, and pays the gas. We use Coinbase's CDP facilitator on Base mainnet, called with plain fetch; the auth is a per-request Ed25519 JWT we build with crypto.subtle, no SDK. Keep the facilitator URL configurable: it is the one dependency worth being able to swap. The two calls (CDP facilitator docs):
POST {facilitator}/verify { paymentPayload, paymentRequirements } // signature + funds check
POST {facilitator}/settle { paymentPayload, paymentRequirements } // executes transferWithAuthorization The payment itself is an EIP-3009 transferWithAuthorization signed by the payer, so the facilitator can submit it and pay gas without ever holding funds.
Step 4: mint idempotently
Settlements retry, headers replay, networks flake. Every grant we issue is recorded with a unique constraint on (protocol, settlement reference), so a replayed settlement returns the original grant instead of minting twice:
CREATE TABLE payment_grants (
id TEXT PRIMARY KEY,
protocol TEXT NOT NULL, -- 'x402' | 'mpp' | 'stripe' | 'credit'
external_ref TEXT NOT NULL, -- settlement tx / session id
payer TEXT, amount TEXT, currency TEXT,
api_key_id TEXT NOT NULL,
UNIQUE (protocol, external_ref) -- the whole idempotency story
); This one table is also your ledger, and it will disagree with the block explorer: our month-one numbers show why you should log every 402, verify, settle, and mint so you can reconcile the two.
Step 5: get discovered
Nobody types your API into a wallet. Agents find paid endpoints through scanners, so we self-host the discovery feed paths x402 scanners probe and appear on x402scan. That listing alone produced a buyer every two to three days, all month, with zero announcement. Config gotchas: your OpenAPI spec needs x-payment-info on paid routes (an empty security array means "free" to the scanner), and each route needs an input schema or the listing is rejected.
We implemented this twice, on purpose
Everything above describes the hand-rolled integration on api.stacktr.ee: our worker owns the 402, the facilitator calls, and the grants table. But there is a second, much faster mode, and we run that too: agents.stacktr.ee fronts the same product through agentcash's router, which handles the payment negotiation for us and is what carries the MPP-on-Tempo rail. Build-it-yourself buys you control, your own ledger, and no dependency; the router buys you extra rails in an afternoon. If you just want to charge agents for an API, start with a router and graduate to owning the 402 when the volume justifies it.
The gotchas that cost us live transactions
- Base mainnet USDC signs as "USD Coin". The EIP-712 domain name is
USD Coin(version2); testnet usesUSDC. Wrong name means an on-chain revert at settlement. Two live attempts died here; make the asset name configurable. - v1-only wallets are common. Including, at the time, Coinbase's own tooling. Dual-serve or lose payers silently.
- Some agent payment clients cannot pay v2 endpoints at all. Check the client ecosystem's issue trackers before assuming a failed payment is your bug.
- Never advertise rails that are not live. Our MPP, ACP, and AP2 code paths exist but gate off unless configured, and the scaffolded ones fail closed. A discovery badge that lies gets found out by an agent with money.
The human fallback
The dominant real-world case is still a human driving an agent in a terminal. So the 402 also advertises a fallback: the agent requests a pay session, prints a QR code, the human scans and pays by card via ordinary Stripe Checkout, and the agent polls to collect its key. Overpay and the surplus becomes a credit balance the agent draws down silently on later purchases, no 402 round-trip. Same grants table, different rail. Building this doubled the audience for the whole system.
Frequent questions
How long does it take to add x402 to an API? +
Do you need your own crypto infrastructure? +
What should you charge over x402? +
How do agents find an x402 endpoint? +
Does x402 replace Stripe? +
Related guides
Sources and further reading
- x402 protocol docs ↗ The spec: requirements schema, payment header format, v1 vs v2.
- CDP facilitator docs ↗ Verify/settle endpoints we call in step 3.
- EIP-3009 ↗ transferWithAuthorization, the signature scheme underneath every x402 payment.
- coinbase/x402 on GitHub ↗ Reference implementation; where client version gaps surface first.
- x402scan ↗ The scanner that generates most of our discovery; your listing lives here.
See the flow live.
The endpoint this guide describes is in production: a 402, a $1 settlement, a persistent key. No signup on either side.
Sign up free →