HTTP 402 sat unused in the spec for 27 years. It was defined in 1998 as "Payment Required", browsers never implemented it, and it became the running joke of the web. That joke is ending, because the first clients that actually want to pay per request are arriving: AI agents.
When a language model hits your API and needs 100 tokens of a paid service, it can't open a Stripe checkout page. It has no browser, no saved card, and often no company behind it. What it does have is a wallet.
The pattern
The flow is refreshingly simple:
- Client calls your endpoint.
- You answer
402 Payment Requiredwith a payment request (amount, token, chain, your address). - Client sends USDC to that address and retries with the tx hash.
- You verify the transfer on-chain and return
200with the goods.
No accounts, no API keys to leak, no KYC, no chargebacks. The blockchain is the receipt. Coinbase codified a version of this as the x402 standard, but the core idea works with nothing but an EVM RPC.
A working implementation in Node
I wanted this as a one-function dependency, so I shipped @josealbertomaquina/usdc-paywall — zero deps, BigInt math against the official USDC contract addresses on Base and Polygon, and a confirmation-depth guard so you don't trust mempool promises:
const express = require('express')
const { verifyPayment } = require('@josealbertomaquina/usdc-paywall')
const app = express()
const seen = new Set() // replay protection: one tx unlocks once
const WALLET = process.env.USDC_WALLET
app.get('/api/report', async (req, res) => {
const { tx } = req.query
if (!tx) return res.status(402).json({
error: 'payment_required',
amount: 3, token: 'USDC', chain: 'base', to: WALLET,
})
if (seen.has(tx)) return res.status(402).json({ error: 'tx_already_used' })
const r = await verifyPayment({
txHash: tx, wallet: WALLET, amount: 3, chain: 'base',
})
if (!r.ok) return res.status(402).json(r)
seen.add(tx)
res.json({ data: 'your paid content' })
})
app.listen(3000)
The check is an ERC-20 Transfer log lookup: it confirms the transfer went to your wallet, for at least the requested amount, of the real USDC contract, with enough confirmations. Because verification is read-only, you can run it against any public RPC.
There's a CLI too, which makes it trivial to gate scripts or one-shot agent tasks:
npx usdc-paywall --tx 0x… --to $WALLET --amount 3 --chain base \
&& ./run-the-paid-job.sh
Why I think the 402 moment is real now
- Agents are the ideal payer: they follow protocols literally, don't abandon checkout funnels, and can hold keys.
- Fees finally fit: a USDC transfer on Base or Polygon costs fractions of a cent, so a $0.05 API call is economically viable for the first time.
- Stablecoins remove the volatility objection for both sides.
Honest caveats
- Replay protection is on you: cache the tx hashes you've already accepted (the
Setabove gets you started; use Redis in production). - Confirmation depth is a trade-off between UX and finality. On L2s, a couple of seconds of depth is usually fine for small amounts.
- Human UX is still rough — this is for machine clients and developer tooling today, not your grandma's newsletter.
The code is on GitHub: essentoclothes-sudo/usdc-paywall — MIT, tested with Node's built-in runner, and it runs anywhere Node runs, Raspberry Pis included.
Top comments (0)