Implement x402
Add agentic payments to your stack with just a few lines of code.
Integration Steps
1. Define Pricing Logic
Determine which endpoints require payment and specify the price (e.g., 0.01 USDC). x402 supports dynamic pricing.
2. Add Middleware
Intercept requests on your server. If payment is missing, return `402 Payment Required` with the `WWW-Authenticate` header.
3. Handle Client Response
On the client (or agent), detect 402, parse the header, sign the payment, and retry the request with the proof.
Live Test Endpoint
You can test your agent or script against our live endpoint. It will always return 402 Payment Required with valid payment details for the Base network.
curl -i https://402payment-test.com/api/x402
WWW-Authenticate: x402 address="..." amount="0.01" token="..."Setting Up an Agent Wallet
To enable your agent to pay, it needs a crypto wallet. We recommend using the CDP AgentKit for the easiest integration, or you can manage keys manually.
Option A: CDP AgentKit (Recommended)
Best for AI AgentsThe Coinbase Developer Platform (CDP) AgentKit provides a unified toolkit for creating AI agents that can interact with blockchain. Supports EVM chains (Base, Ethereum) and Solana.
- Install the SDK:
npm install @coinbase/agentkit - Configure your API Key and Private Key from CDP.
- Initialize the wallet provider.
import { CdpAgentkit } from "@coinbase/agentkit";
const agentKit = await CdpAgentkit.configureWithWallet({
networkId: "base-sepolia",
cdpApiKeyName: process.env.CDP_API_KEY_NAME,
cdpApiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY,
});
const address = await agentKit.getWalletAddress();
console.log("Agent Address:", address);Option B: Custom Wallet
For full control, you can manage a private key directly using standard libraries like viem or web3.py.
Node.js (viem)
import { privateKeyToAccount } from 'viem/accounts';
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
console.log("Address:", account.address);Python (web3.py)
from web3 import Web3
w3 = Web3(Web3.HTTPProvider(RPC_URL))
account = w3.eth.account.from_key(PRIVATE_KEY)
print("Address:", account.address)Solana (@solana/web3.js)
import { Keypair } from '@solana/web3.js';
const keypair = Keypair.fromSecretKey(
Buffer.from(process.env.SOLANA_PRIVATE_KEY, 'base64')
);
console.log("Address:", keypair.publicKey.toBase58());Why Solana for x402?
Solana offers sub-second finality (~400ms) and ultra-low fees (~$0.00025), making it ideal for high-frequency micro-payments and AI agents.
Funding Your Agent
To pay for transactions, your agent needs ETH (for gas) and USDC (for payments) on Base Sepolia. You can get free testnet funds from the Coinbase Faucet.
Complete Code Examples
Ready-to-use implementations for Node.js and Python. These examples show the complete flow: detect 402, parse payment details, execute payment, and retry with proof.
Node.js (Complete)
viem + wagmiimport { createWalletClient, http, parseUnits } from 'viem';
import { privateKeyToAccount } => 'viem/accounts';
import { base } from 'viem/chains';
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const ERC20_ABI = [{
name: 'transfer',
type: 'function',
stateMutability: 'nonpayable',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' }
],
outputs: [{ name: '', type: 'bool' }]
}];
async function fetchWith402(url, privateKey) {
// Initialize wallet
const account = privateKeyToAccount(privateKey);
const client = createWalletClient({
account,
chain: base,
transport: http()
});
// 1. Initial request
let res = await fetch(url);
if (res.status === 402) {
console.log('Payment Required');
// 2. Parse payment details
const auth = res.headers.get('WWW-Authenticate');
const addressMatch = auth.match(/address="([^"]+)"/);
const amountMatch = auth.match(/amount="([^"]+)"/);
const recipient = addressMatch[1];
const amount = amountMatch[1];
console.log(`Paying ${amount} USDC to ${recipient}`);
// 3. Execute payment
const hash = await client.writeContract({
address: USDC_ADDRESS,
abi: ERC20_ABI,
functionName: 'transfer',
args: [recipient, parseUnits(amount, 6)]
});
console.log(`Payment sent: ${hash}`);
// 4. Retry with proof
res = await fetch(url, {
headers: {
'Authorization': `x402-proof ${hash}`
}
});
}
return res.json();
}
// Usage
const data = await fetchWith402(
'https://402payment-test.com/api/x402',
process.env.PRIVATE_KEY
);
console.log('Data:', data);Python (Complete)
requests + web3.pyimport requests
import re
from web3 import Web3
from eth_account import Account
# Base network RPC
w3 = Web3(Web3.HTTPProvider('https://mainnet.base.org'))
USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
ERC20_ABI = [{
'name': 'transfer',
'type': 'function',
'stateMutability': 'nonpayable',
'inputs': [
{'name': 'to', 'type': 'address'},
{'name': 'amount', 'type': 'uint256'}
],
'outputs': [{'name': '', 'type': 'bool'}]
}]
def fetch_with_402(url, private_key):
# Initialize account
account = Account.from_key(private_key)
# 1. Initial request
res = requests.get(url)
if res.status_code == 402:
print('Payment Required')
# 2. Parse payment details
auth = res.headers['WWW-Authenticate']
recipient = re.search(r'address="([^"]+)"', auth).group(1)
amount = re.search(r'amount="([^"]+)"', auth).group(1)
print(f'Paying {amount} USDC to {recipient}')
# 3. Execute payment
usdc = w3.eth.contract(
address=USDC_ADDRESS,
abi=ERC20_ABI
)
tx = usdc.functions.transfer(
recipient,
int(float(amount) * 10**6) # USDC has 6 decimals
).build_transaction({
'from': account.address,
'nonce': w3.eth.get_transaction_count(account.address),
'gas': 100000,
'gasPrice': w3.eth.gas_price
})
signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Payment sent: {tx_hash.hex()}')
# 4. Retry with proof
res = requests.get(url, headers={
'Authorization': f'x402-proof {tx_hash.hex()}'
})
return res.json()
# Usage
import os
data = fetch_with_402(
'https://402payment-test.com/api/x402',
os.environ['PRIVATE_KEY']
)
print('Data:', data)Implementation Flows
There are three main approaches to integrating x402: manual approval (human-in-the-loop), delegated authorization (approve once, agent pays), and autonomous agents (fully pre-authorized). The key difference is who controls the wallet and when.
Flow 1: Manual Approval (Human-in-the-Loop)
The user's wallet (e.g., MetaMask, Coinbase Wallet) prompts them to approve each payment. Best for user-facing applications where transparency is critical.
🔑 Authorization: Browser Wallet
- • Private key stored in wallet extension (MetaMask/Coinbase Wallet)
- • App uses
wagmito request signing - • Wallet shows popup asking for approval
- • User clicks "Approve" in wallet UI
User initiates request
App calls API endpoint
402 response received
Parse payment details from header
Show payment prompt
UI displays amount and asks for approval
User approves in wallet
MetaMask/Coinbase Wallet popup
Payment executed & request retried
Transaction confirmed, resource served
// Example: Manual approval with wagmi
import { useAccount, useWriteContract } from 'wagmi';
function PayForResource() {
const { address } = useAccount();
const { writeContract } = useWriteContract();
async function handleRequest() {
const res = await fetch('/api/premium-data');
if (res.status === 402) {
const auth = res.headers.get('WWW-Authenticate');
const { address: recipient, amount } = parseAuth(auth);
// This triggers the wallet popup (MetaMask/Coinbase Wallet)
// User must approve in their wallet - NO window.confirm needed!
const tx = await writeContract({
address: USDC_ADDRESS,
abi: ERC20_ABI,
functionName: 'transfer',
args: [recipient, parseUnits(amount, 6)]
});
// Retry with proof
const data = await fetch('/api/premium-data', {
headers: { 'Authorization': `x402-proof ${tx}` }
});
return data.json();
}
}
}Flow 2: Autonomous Agent (Pre-Authorized)
The agent has direct access to a wallet (via private key or delegated signer) and can pay automatically. Best for backend agents, bots, or trusted automation.
🔑 Authorization: Private Key
- • Private key stored in environment variable
- • Agent uses
viemto sign directly - • No popup - automatic signing
- • No human intervention needed
Agent configured with wallet access
Private key in env vars or secure vault
Agent requests resource
Automated API call
402 detected, budget checked
Verify amount is within spending limits
Payment executed automatically
No human intervention
Request retried with proof
Resource served, transaction logged
// Example: Autonomous agent with budget control
import { privateKeyToAccount } from 'viem/accounts';
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
class AgentWallet {
constructor(privateKey, dailyBudget) {
this.account = privateKeyToAccount(privateKey);
this.client = createWalletClient({
account: this.account,
chain: base,
transport: http()
});
this.dailyBudget = dailyBudget;
this.spentToday = 0;
}
async fetchWith402(url) {
const res = await fetch(url);
if (res.status === 402) {
const { address, amount } = parseAuth(res.headers.get('WWW-Authenticate'));
// Budget check
if (this.spentToday + parseFloat(amount) > this.dailyBudget) {
throw new Error('Daily budget exceeded');
}
// Automatic payment (no user prompt)
const hash = await this.client.writeContract({
address: USDC_ADDRESS,
abi: ERC20_ABI,
functionName: 'transfer',
args: [address, parseUnits(amount, 6)]
});
this.spentToday += parseFloat(amount);
console.log(`Auto-paid ${amount} USDC. Budget: ${this.spentToday}/${this.dailyBudget}`);
// Retry with proof
return fetch(url, {
headers: { 'Authorization': `x402-proof ${hash}` }
});
}
return res;
}
}
// Usage
const agent = new AgentWallet(process.env.AGENT_PRIVATE_KEY, 10); // $10/day limit
const data = await agent.fetchWith402('/api/premium-data');Flow 3: Delegated Authorization (Hybrid)
User approves once, granting the agent a time-limited or budget-limited permission to pay automatically. Best for user-controlled agents with spending limits.
🔑 Authorization: Delegated Permission
- • User grants one-time approval (allowance/session key)
- • Agent stores delegation token
- • No popup after initial approval
- • User can revoke anytime
User grants permission once
Approve spending limit or create session key
Agent stores delegation token
Session key or allowance reference
Agent pays automatically (within limits)
No popup, but enforces budget/time constraints
User can revoke anytime
Permission is revocable on-chain
Option A: ERC-20 Allowance (Simplest)
User approves the agent contract to spend USDC on their behalf.
// User approves once (in browser)
await usdcContract.approve(agentContractAddress, parseUnits("100", 6));
// Agent can now spend up to 100 USDC without asking
// (runs on backend)
const agent = new AgentWithAllowance(userAddress);
await agent.payFor402(url, amount); // No popup!
// User can revoke
await usdcContract.approve(agentContractAddress, 0);Option B: ERC-4337 Session Keys (Most Flexible)
User creates a temporary signing key with spending limits and expiration.
// User creates session key (in browser)
const sessionKey = await smartAccount.createSessionKey({
validUntil: Date.now() + 24 * 60 * 60 * 1000, // 24 hours
spendingLimit: parseUnits("10", 6), // Max 10 USDC
allowedTargets: [recipientAddress] // Only specific addresses
});
// Agent uses session key (backend)
const agent = new AgentWithSessionKey(sessionKey);
await agent.fetchWith402(url); // Pays automatically within limits
// Session key expires after 24h or can be revokedOption C: Safe Wallet Modules (Enterprise)
Add agent as a Safe module with granular permissions.
// User enables module (in Safe UI)
await safe.enableModule(agentModuleAddress);
// Agent executes payments (backend)
await agentModule.executePayment({
to: recipient,
amount: parseUnits("0.01", 6),
// Safe enforces rules: daily limits, whitelisted addresses, etc.
});
// User can disable module anytime
await safe.disableModule(agentModuleAddress);✅ Benefits of Delegated Authorization
- User retains control (can revoke anytime)
- Agent can pay automatically (no popups)
- Spending limits + time constraints
- No need to share private keys
- Auditable on-chain
How to Authorize an Agent to Pay Automatically
📝 How to Get a Private Key for Your Agent
For Flow 2 (Autonomous Agent), you need a private key. Here's how to create one:
Method 1: Generate with viem (Recommended)
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
// Generate a new random private key
const privateKey = generatePrivateKey();
console.log("Private Key:", privateKey);
// Derive the address
const account = privateKeyToAccount(privateKey);
console.log("Address:", account.address);
// Save privateKey to .env file (NEVER commit to git!)
// AGENT_PRIVATE_KEY=0x...Method 2: Use an Existing Wallet
You can export a private key from MetaMask or Coinbase Wallet:
- Open your wallet extension
- Go to Settings → Security & Privacy
- Click "Show Private Key" (requires password)
- Copy the key and store it securely
⚠️ Warning: Never use your personal wallet's private key for agents! Create a dedicated wallet for your agent with limited funds.
Method 3: CDP AgentKit (Easiest)
CDP AgentKit handles wallet creation automatically:
import { CdpAgentkit } from "@coinbase/agentkit";
const agentKit = await CdpAgentkit.configureWithWallet({
networkId: "base-sepolia",
cdpApiKeyName: process.env.CDP_API_KEY_NAME,
cdpApiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY,
});
// AgentKit manages the wallet for you!
const address = await agentKit.getWalletAddress();✅ Option A: Environment Variables (Simplest)
Store the agent's private key in environment variables. The agent signs transactions directly.
- Pros: Simple, no extra infrastructure
- Cons: If key leaks, unlimited spending
- Best for: Trusted environments, low-value transactions
✅ Option B: Spending Limits + Allowlists (Recommended)
Agent has a wallet, but enforces budget limits and only pays approved addresses.
- Pros: Limits blast radius if compromised
- Cons: Requires custom logic
- Best for: Production agents with moderate risk
const ALLOWED_RECIPIENTS = ['0x123...', '0x456...'];
if (!ALLOWED_RECIPIENTS.includes(recipient)) throw new Error('Unauthorized recipient');✅ Option C: Multi-Sig or Approval Contracts (Most Secure)
Use a smart contract wallet (e.g., Safe) that requires multiple signatures or time-delayed approvals.
- Pros: Maximum security, revocable permissions
- Cons: Complex setup, higher gas costs
- Best for: High-value agents, DAO-managed bots
⚠️ Security Best Practices
- Never commit private keys to git
- Use separate wallets for agents (don't reuse personal wallets)
- Implement daily/hourly spending caps
- Log all transactions for audit trails
- Consider using hardware wallets or HSMs for high-value agents