x402 turns HTTP 402 Payment Required — reserved but unused since 1997 — into a live protocol Coinbase shipped in 2025. When an agent calls your API, it can pay in USDC instantly, with no card or invoice. AIDE's commerce-x402 and x402-pass checks verify your stack is wired correctly.
What AIDE actually checks
commerce-x402 validates three things:
- Does an unauthenticated request to a protected endpoint return 402?
- Does the 402 response include
WWW-Authenticate: x402? - Is there a valid x402 paywall body (
scheme: exact,network,maxAmountRequired,payTo,asset)?
x402-pass additionally probes the end-to-end payment flow — facilitator + settlement.
Why x402?
Traditional API monetization (Stripe + API key) is awkward for agents:
- Issuing API keys requires human oversight
- Invoice cycles run 30 days; agents act in seconds
- Sharing card numbers is a risk surface for an agent
x402 collapses this: an agent receiving a 402 pays USDC from its wallet (seconds, on-chain) and replays the request. Micropayments under one cent are economically viable.
Minimum server
Express + x402-express middleware is the shortest path:
// server.js
import express from "express"
import { x402Middleware } from "x402-express"
const app = express()
app.use(
"/api/scan",
x402Middleware({
network: "base",
payTo: "0xYourReceiverAddress",
asset: {
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
decimals: 6,
symbol: "USDC"
},
pricing: {
"/api/scan": "0.05" // 5 cents per scan
},
facilitator: "https://x402.org/facilitator"
})
)
app.post("/api/scan", async (req, res) => {
// If we got here, payment is verified — run the real scan
const result = await runScan(req.body.url)
res.json(result)
})
app.listen(3000)
The middleware:
- Looks for
Authorization: x402 ...on each request. - If missing or invalid, returns 402 + paywall payload.
- If present, asks the facilitator to verify the signature.
- Once settled, hands the request to your handler.
Anatomy of the 402 response
HTTP/1.1 402 Payment Required
WWW-Authenticate: x402
Content-Type: application/json
{
"x402Version": 1,
"accepts": [{
"scheme": "exact",
"network": "base",
"maxAmountRequired": "50000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0xYourReceiverAddress",
"resource": "/api/scan",
"description": "AIDE site scan, 0.05 USDC per result",
"mimeType": "application/json",
"maxTimeoutSeconds": 60
}],
"error": "X-PAYMENT header is required"
}
Key fields:
| Field | Meaning | AIDE impact |
|---|---|---|
| scheme: exact | Exact-amount payment; tipping/subscription variants aren't standardized yet | Other schemes are out of spec |
| network | Which chain (base, base-sepolia, polygon) | Non-mainnet → AIDE WARNING; testnet payments aren't "real" |
| maxAmountRequired | Cap, in atomic units (6 decimals for USDC) | Excessive (>$1) → WARNING |
| payTo | Receiver wallet | Zero-address or non-contract → FAIL |
| maxTimeoutSeconds | Signature TTL | <30 s — agents can't react in time |
Second request: signed payment
The agent receives the 402, mints an EIP-3009 signature via the facilitator, and replays the call:
POST /api/scan HTTP/1.1
Authorization: x402 eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLCJuZXR3b3JrIjoiYmFzZSIsInBheWxvYWQiOiJ7XCJzaWduYXR1cmVcIjoiMHguLi4ifSJ9
{"url": "https://stripe.com"}
Authorization: x402 <base64> payload contains a signed transferWithAuthorization call. The server pings the facilitator:
const verification = await fetch("https://x402.org/facilitator/verify", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ paymentPayload, paymentRequirements })
})
isValid: true triggers settlement, then your /api/scan handler runs.
Common mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| Protected endpoint still returns 200 | Middleware not on the route | Order matters — app.use(x402Middleware) must come before the handler |
| Facilitator verify always fails | Network mismatch (testnet vs mainnet) | Manifest says network: "base" but facilitator URL might be on base-sepolia; align them |
| 402 response blocked by CORS | Browser-based agents can't reach you | Add Access-Control-Expose-Headers: WWW-Authenticate |
| maxAmountRequired too low | Gas cost > payment | On Base mainnet anything under 0.001 USDC isn't economic; minimum 0.01 |
Testing — what AIDE does
Manual probe with curl:
# 1. Unauthenticated call — should return 402
curl -i https://api.your-site.com/api/scan \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com"}'
Expected:
HTTP/1.1 402 Payment Required
WWW-Authenticate: x402
If not present: commerce-x402 FAIL. Then:
# 2. Trigger a full AIDE scan
curl -X POST https://api.aide.tr/v1/scans \
-H 'Content-Type: application/json' \
-d '{"url":"https://your-site.com","profile":"ai_ready"}'
commerce-x402 should be PASS. x402-pass may stay WARNING (E2E payment isn't tested by default).
Production hardening
- Replay protection: Each signature has a
nonce— never accept the same nonce twice. The facilitator doesn't track this for you; keep aseen_noncestable in your DB. - No refunds: x402 has no protocol-level refund. If your handler fails after settlement, the content wasn't delivered and the agent retries. Design your handler to be idempotent.
- Dynamic pricing: You can charge per-agent. The middleware supports a
priceModifiercallback. - Audit log: Persist every successful settlement to ClickHouse/Postgres — for tax reporting you'll need tx hash and amount.
Related resources
- x402 Spec
- x402-express on npm
- x402.org Facilitator
- AIDE check details:
/learn/commerce-x402,/learn/x402-pass - Broader commerce stack:
/learn/agentic-commerce-architecture-guide