x402 v2 Complete Guide - No More Version Confusion
v1 vs v2 differences, migration checklist, and working code for agent-to-agent payments.
x402 v2 Complete Guide - No More Version Confusion
There's a Reddit thread from three weeks ago where a developer compiled everything about x402 into a single post because "a lot of the documentation is confusing due to differences between v1 and v2." That shouldn't be necessary. So here's the definitive reference.
v1 vs v2: What Actually Changed
x402 v1 was a proof of concept. Coinbase published it, it worked, and people started building on it. Then v2 shipped with breaking changes and no migration guide. That's where the confusion started.
Here's what's different:
| Aspect | v1 | v2 |
| Payment header | X-PAYMENT with base64-encoded JSON | X-PAYMENT with structured binary encoding |
| Token support | USDC on Base only | USDC on Base, Etherlink, and any EVM chain |
| Facilitator model | Single facilitator per server | Multiple facilitator support, facilitator discovery |
| Response codes | 402 Payment Required with JSON body | 402 Payment Required with structured X-PAYMENT-REQUIRED header |
| Settlement | Immediate on-chain verification | Deferred settlement option (batch mode) |
| Signature scheme | EIP-191 personal sign | EIP-712 typed data (structured, verifiable) |
The biggest change is the signature scheme. v1 used personal_sign (EIP-191) - just signing a message string. v2 uses EIP-712 typed structured data, which means wallets can display exactly what's being signed. Better UX, better security, but completely incompatible signatures.
How x402 v2 Works (Step by Step)
1. Client Makes a Request
$ curl https://api.example.com/expensive-endpoint
2. Server Responds with 402
HTTP/1.1 402 Payment Required
X-PAYMENT-REQUIRED: {"scheme":"exact","network":"base","maxAmountRequired":"1000000","resource":"/expensive-endpoint","facilitator":"0xFac...","description":"API call - 1 USDC"}
The X-PAYMENT-REQUIRED header tells the client: what to pay, how much, on which chain, and to which facilitator.
3. Client Signs and Pays
import { createx402Payment } from 'agentwallet-sdk/x402';
const paymentHeader = await createx402Payment({
amount: '1000000', // 1 USDC (6 decimals)
network: 'base',
facilitator: '0xFac...',
resource: '/expensive-endpoint',
signerPrivateKey: process.env.AGENT_PRIVATE_KEY,
});
4. Client Retries with Payment
$ curl https://api.example.com/expensive-endpoint \
-H "X-PAYMENT: ${paymentHeader}"
5. Server Verifies and Serves
The server (or its facilitator) verifies the EIP-712 signature, confirms the USDC transfer on Base, and returns the response.
Setting Up an x402 Server (v2)
Here's a minimal Express server that accepts x402 payments:
import express from 'express';
import { verifyX402Payment, X402Middleware } from 'agentwallet-sdk/x402';
const app = express();
// Protect any route with x402
app.use('/api/premium', X402Middleware({
amount: '1000000', // 1 USDC per request
network: 'base',
facilitatorAddress: process.env.FACILITATOR_ADDRESS,
rpcUrl: 'https://mainnet.base.org',
}));
app.get('/api/premium/data', (req, res) => {
res.json({ data: 'premium content' });
});
app.listen(3000);
The middleware handles the 402 response, payment verification, and settlement. Your route handler only runs after payment clears.
Common Mistakes (and How to Avoid Them)
1. Using v1 signatures with v2 servers.
If you're getting "invalid signature" errors, check which EIP your signing code uses. personal_sign (EIP-191) won't work with a v2 facilitator expecting EIP-712. There's no backwards compatibility.
2. Wrong USDC decimals.
USDC has 6 decimals, not 18. 1000000 = 1 USDC. 1000000000000000000 = 1 trillion USDC. The number of people who've accidentally set their price to a trillion dollars is non-zero.
3. Hardcoding the facilitator address. Facilitators can change. Use the facilitator discovery endpoint if your SDK supports it, or at minimum make it a config value, not a constant.
4. Not handling the 402 response in agent loops. If your agent hits a 402 and doesn't have a payment handler, it'll either crash or loop forever. Build the payment flow into your agent's tool-use layer, not as an afterthought.
x402 v2 on Multiple Chains
v2 added multi-chain support. The network field in the payment-required header tells the client which chain to use. Currently live:
- Base - The original and most liquid. 115M+ total x402 transactions.
- Etherlink - Tezos L2. Lower fees, growing ecosystem.
- Any EVM chain - The spec is chain-agnostic. If USDC exists on the chain and there's a facilitator, x402 works.
Your agent should read the network field and route to the correct chain. Don't assume Base.
x402 vs Traditional API Keys
Here's the question developers actually ask: why use x402 instead of a Stripe subscription?
x402 wins when:
- Your users are AI agents, not humans (agents don't fill out Stripe Checkout forms)
- You want per-request pricing, not monthly subscriptions
- You want permissionless access (no API key registration, no KYC for $2 API calls)
- Your service is consumed by many agents making small, infrequent requests
Traditional billing wins when:
- Your customers are humans who want invoices
- Usage is predictable and monthly billing is simpler
- You need to identify customers for compliance
- Transaction amounts are large enough that gas fees are negligible
For agent-to-agent commerce, x402 is the clear answer. For human SaaS, keep your Stripe integration.
Migration Checklist: v1 to v2
If you're running a v1 x402 server or client, here's what to change:
- Update signature generation from
personal_sign(EIP-191) to EIP-712 typed data - Update the 402 response format to use the structured
X-PAYMENT-REQUIREDheader - Update payment header parsing on the server side (binary encoding, not base64 JSON)
- Test with a v2 facilitator (the v1 facilitator won't validate v2 signatures)
- Update your USDC contract address if you're on a new chain
- Add multi-chain support to your client if you want Etherlink or other chains
The agent-wallet-sdk x402 module handles all of this. If you're using the SDK, update to v5.0+ and the migration is automatic.
This article was written with AI assistance. All technical claims, code, and architectural decisions were validated by the author.