TL;DR
- x402 revives HTTP 402 Payment Required for per-request payments.
- Server replies 402 with how to pay; client retries with X-PAYMENT.
- A facilitator verifies/settles on-chain; server returns 200.
- Great fit for AI agents, paid APIs, and metered endpoints.
What is x402?
x402 is a protocol that embeds per-request payments straight into the HTTP flow. Instead of API keys or prepaid credits, your server can say “pay X in token Y on chain Z” via a 402 response. The client signs a payment payload and retries—result: no new transport, no exotic gateways—just HTTP.
Why it’s trending now
- Fits how we already build: works with normal routes, middleware, and headers.
- Perfect for agents: machine-to-machine payments without manual top-ups or accounts.
- Lower integration friction: you can prototype with a thin client wrapper and a facilitator, then harden.
Developer mental model (fast)
- It’s just HTTP: 402 to request payment, 200 on success, plus structured JSON and headers.
- Two headers: client sends X-PAYMENT, server returns X-PAYMENT-RESPONSE (receipt/metadata).
- Facilitator: handles on-chain verification/settlement so your app logic stays clean.
How it works (in one diagram)
Client ── GET /premium-data ───────────────▶ Server
◀─ 402 + { price, asset, chain } ────
Client signs payment payload
Client ── GET /premium-data + X-PAYMENT ───▶ Server
Server ── verify/settle ─▶ Facilitator ─▶ Chain
◀────────────────────────── 200 OK (+ X-PAYMENT-RESPONSE)
Minimal code you can reason about
// Server (Express-style pseudo-code)
app.get("/premium", async (req, res) => {
const payHeader = req.header("X-PAYMENT");
if (!payHeader) {
return res.status(402).json({
x402Version: 1,
accepts: [{
scheme: "exact",
network: "base-mainnet",
asset: "USDC_ADDRESS",
maxAmountRequired: "1000000", // 1 USDC (6 decimals)
description: "Access: /premium",
payTo: "0xMerchantAddress",
resource: "/premium",
mimeType: "application/json",
maxTimeoutSeconds: 30
}]
});
}
const verified = await verifyAndSettle(payHeader); // via your facilitator
if (!verified) return res.status(402).json({ error: "payment_required" });
res.setHeader("X-PAYMENT-RESPONSE", buildReceipt(verified));
return res.json({ data: "paid content" });
});
// Client wrapper
async function fetchWithPay(url: string, init: RequestInit = {}) {
const res = await fetch(url, init);
if (res.status !== 402) return res;
const reqs = await res.json(); // payment requirements
const choice = pick(reqs.accepts); // choose chain/token
const xPayment = await createPaymentHeader(choice); // wallet signs
return fetch(url, { ...init, headers: { ...(init.headers||{}), "X-PAYMENT": xPayment }});
}
Ship with eyes open
- **Wallets & keys: **your client (or agent) must be able to sign. UX and limits matter.
- Fees/finality: choose networks/tokens that make micro-payments sane; consider batching/deferred patterns.
- Idempotency: protect against duplicates (receipts, request IDs).
- Observability: log payment attempts, verification outcomes, and unlock decisions.
x402 & Embedded Wallets
If you want the x402 handshake without wrestling with wallet UX, key management, or gas policy, Openfort gives you embedded wallets, 4337/7702-ready flows, and paymaster/bundler infra that pairs neatly with per-request payments. We also wrote a dev-first explainer of the flow here: Inside x402: Enabling Payments with HTTP 402.
Top comments (0)