Dev.to · 6 min read

x402 Explained: HTTP-Native Micropayments for AI Agents (With Real Code)

x402 Explained: HTTP-Native Micropayments for AI Agents (With Real Code)

x402 Explained: HTTP‑Native Micropayments for AI Agents (With Real Code) Target audience: developers building autonomous AI agents who need a lightweight, standards‑based way to charge for API usage without building a custom billing system. 1. Why look at x402? Autonomous agents often consume services—LLM completions, data look‑ups, tool calls—by issuing HTTP requests. Traditional API keys or subscription models add operational overhead: you must manage secret rotation, rate‑limit per key, and reconcile usage at month‑end. The x402 specification repurposes the existing HTTP status code 402 Payment Required to turn any HTTP endpoint into a pay‑per‑call service. The protocol is deliberately minimal: The server responds 402 with a Payment-Headers field that tells the client how and how much to pay. The client adds a Payment header containing a signed proof of payment (usually an ERC‑20 transfer on an EVM chain). If the proof validates, the server processes the request and returns 200. Otherwise, it repeats the 402. Because it lives entirely in HTTP, x402 works with any language, any client library, and any blockchain that can produce a verifiable transaction receipt. For agents that already hold a wallet (e.g., a MetaMask‑style key pair), the integration cost is a few lines of middleware. 2. The protocol in a nutshell Step Direction Header / Body Meaning 1️⃣ Client → Server GET /svc (no auth) Probe the service 2️⃣ Server → Client 402 Payment RequiredPayment-Headers: {"scheme":"erc20","network":"base","token":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","amount":"1000000000000000"} Ask for 0.001 USDC (6 decimals) on Base 3️⃣ Client → Server GET /svcPayment: {"scheme":"erc20","network":"base","token":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","amount":"1000000000000000","tx":"0x…"} Provide a signed ERC‑20 transfer receipt 4️⃣ Server → Client 200 OK + response body Service delivered if payment verifies The server only needs to verify that: The transaction hash exists on the specified chain. The from address matches the caller’s wallet (or a pre‑approved escrow). The token, amount, and recipient (to) match the values advertised in Payment-Headers. If any check fails, the server returns another 402 with the same headers, allowing the client to retry. 3. Implementing a paid endpoint (Node.js/Express) Below is a self‑contained example that you can drop into an existing Express server. It uses ethers.js to read transaction receipts from an RPC endpoint (here, a public Base RPC). In production you would cache receipts and validate signatures more rigorously. // file: x402-middleware.js import express from 'express'; import { ethers } from 'ethers'; // Configuration – adjust for your token & price const X402_CONFIG = { scheme: 'erc20', network: 'base', // Chain ID 8453 token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base amount: ethers.parseUnits('0.01', 6), // $0.01 USDC (6 decimals) rpcUrl: 'https://base.meowrpc.com', // public RPC, replace with a reliable provider }; const provider = new ethers.JsonRpcProvider(X402_CONFIG.rpcUrl); /** * Express middleware that enforces x402 payment. * Attach it to any route you want to monetize. */ export function x402PaymentRequired() { return async function (req, res, next) { // 1️⃣ If request already contains a valid Payment header, verify it. const paymentHeader = req.headers['payment']; if (paymentHeader) { try { const payment = JSON.parse(paymentHeader); const isValid = await verifyPayment(payment); if (isValid) { // Attach payer address for downstream handlers req.x402Payer = payment.from; return next(); } } catch (_) { // fall through to request payment } } // 2️⃣ No valid payment → ask for it. res.set('Payment-Headers', JSON.stringify({ scheme: X402_CONFIG.scheme, network: X402_CONFIG.network, token: X402_CONFIG.token, amount: X402_CONFIG.amount.toString(), })); return res.status(402).send('Payment Required'); }; } /** * Verify that the supplied payment object represents a correct ERC‑20 transfer. * Returns true if the transaction matches the expected token, amount, recipient, * and was sent by the address stored in payment.from. */ async function verifyPayment(payment) { // Basic schema check if (!payment.tx || !payment.from) return false; try { const tx = await provider.getTransaction(payment.tx); if (!tx) return false; // Ensure it's an ERC‑20 transfer to our token contract if (tx.to?.toLowerCase() !== X402_CONFIG.token.toLowerCase()) return false; // Decode input data: transfer(address to, uint256 value) const iface = new ethers.Interface([ "function transfer(address to, uint256 value)", ]); const { to, value } = iface.parseTransaction({ data: tx.data }); if (to.toLowerCase() !== ethers.getAddress(payment.from).toLowerCase()) { // Actually, `from` in payment is the *payer*; the ERC‑20 transfer's `from` // is the msg.sender, which should equal the payer's address. // For simplicity we assume the payer sent the transfer themselves. return false; } if (value !== X402_CONFIG.amount) return false; // Optional: confirm the tx is finalized (e.g., 1 confirmation) const receipt = await provider.waitForTransaction(payment.tx, 1); return receipt.status === 1; } catch (err) { console.warn('x402 verification error:', err); return false; } } /* ---- Example usage ---- */ const app = express(); app.use(express.json()); // Public info endpoint (no payment) app.get('/info', (req, res) => { res.json({ service: 'x402-demo', version: '1.0' }); }); // Paid endpoint – returns a random number app.get('/rand', x402PaymentRequired(), (req, res) => { res.json({ payer: req.x402Payer, value: Math.random(), timestamp: new Date().toISOString(), }); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log(`🚀 Server listening on :${PORT}`)); What this does The middleware checks for a Payment header. If missing or invalid, it replies 402 with a Payment-Headers advertisement that tells the client exactly what to pay. When a client supplies a header, we fetch the transaction from the RPC, decode the ERC‑20 transfer call, and verify token, amount, and sender. On success we attach req.x402Payer so the handler knows who paid (useful for auditing or rate‑limiting per address). 4. Calling a paid endpoint from an AI agent An agent that already controls an Ethereum wallet can pay automatically. The snippet below shows a generic helper using viem (a lightweight alternative to ethers) to build and send the USDC transfer, then attach the proof. ts // file: x402-client.ts import { createPublicClient, http, parseAbi } from 'viem'; import { base } from 'viem/chains'; import { privateKeyToAccount } from 'viem/accounts'; // ---------- Configuration ---------- const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as `0x${string}`; const AMOUNT = 10n ** 4n; // 0.01 USDC (6 decimals) => 10_000 base units const RPC_URL = 'https://base.meowrpc.com'; const PRIVATE_KEY = process.env.X402_PRIVATE_KEY!; // never commit this!

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News