x402 payment flow
Pay-per-call USDC on Base. One signature, no accounts, no API keys.
The 4-step flow
- Client POSTs the request normally.
- Server returns HTTP 402 Payment Required with payment instructions (recipient, USDC amount, asset address).
- Client signs an EIP-3009
TransferWithAuthorizationfor that amount. - Client retries with the
X-Paymentheader → server validates, settles USDC, returns the real result.
Step 1 — Request without payment
HTTP request
POST https://blueagent.dev/api/x402/honeypot-check
Content-Type: application/json
{ "token": "0x..." }Step 2 — Server returns 402
HTTP response
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0xb058a1e305d9c720aa5b1bf42b6f2f6294b03b5f",
"maxAmountRequired": "50000",
"extra": { "name": "USD Coin", "version": "2" }
}]
}maxAmountRequired is in USDC base units — 6 decimals. 50000 = $0.05.
Step 3 — Sign the authorization
Sign an EIP-3009 typed data structure with your wallet. Most wagmi / viem hooks handle this in one call:
TypeScript (viem)
import { signTypedData } from "viem/actions";
const signature = await signTypedData(walletClient, {
account,
domain: {
name: "USD Coin",
version: "2",
chainId: 8453,
verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
},
types: {
TransferWithAuthorization: [
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "validAfter", type: "uint256" },
{ name: "validBefore", type: "uint256" },
{ name: "nonce", type: "bytes32" },
],
},
primaryType: "TransferWithAuthorization",
message: {
from: account.address,
to: payTo,
value: BigInt(maxAmountRequired),
validAfter: 0n,
validBefore: BigInt(Math.floor(Date.now() / 1000) + 300),
nonce: randomBytes32(),
},
});Step 4 — Retry with X-Payment
HTTP request
POST https://blueagent.dev/api/x402/honeypot-check
Content-Type: application/json
X-Payment: <base64({ signature, authorization })>
{ "token": "0x..." }Server validates the signature, settles USDC on-chain via the CDP x402 facilitator, runs the tool, returns the real JSON response.
💡 Use a client SDK
The whole flow is ~3 lines with @coinbase/x402 — handles 402 detection, signature, retry. No need to manually wire EIP-3009 unless you want to.
Quick reference
- Network: Base mainnet (
eip155:8453) - Asset: USDC native (
0x833589fCD6…02913) - Facilitator: Coinbase CDP x402
- Settlement window:
validBeforeset to ~5 min by convention - Revenue split: 80% to API provider, 20% to Hub treasury