x402 is a way to pay for an HTTP request inside the request itself. This page follows one payment through our API header by header, so you can see what your client signs and what the server does with it.
The shortest paid request
Ad credit has its price in its address. A request with nothing in it is answered with that price:
curl -si -X POST https://cryptoadvertising.net/api/v1/credits/5
HTTP/2 402
payment-required: eyJ4NDAyVmVyc2lvbiI6Miwi...
The PAYMENT-REQUIRED header is base64 JSON. Decoded, it says what to pay:
{
"x402Version": 2,
"resource": { "url": "https://cryptoadvertising.net/api/v1/credits/5" },
"accepts": [
{ "scheme": "exact", "network": "eip155:8453", "amount": "5000000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x1313c840F693EB2d8FD4Bb3B8Ed9e55B134B47D8",
"maxTimeoutSeconds": 600, "extra": { "name": "USD Coin", "version": "2" } }
]
}
There is one entry in accepts per network: Base (eip155:8453), Polygon
(eip155:137) and Arbitrum (eip155:42161). The amount is in the token's
smallest unit, so 5000000 is 5 USDC. Sign the entry for the network you hold
USDC on.
What the client signs
Scheme exact on these networks is an EIP-3009 transferWithAuthorization:
a typed message that lets exactly that amount move from your wallet to payTo,
once, within a time window. Your wallet does not send a transaction and needs
no gas. The client puts the signature in the PAYMENT-SIGNATURE header and
sends the same request again.
curl -si -X POST https://cryptoadvertising.net/api/v1/credits/5 \
-H 'payment-signature: eyJ4NDAyVmVyc2lvbiI6Miwi...'
HTTP/2 201
payment-response: eyJzdWNjZXNzIjp0cnVlLCJ0cmFuc2FjdGlvbiI6...
idempotency-key: 6f1c2a9e-...
The body carries credit_usd, the payment with its transaction hash, the
balance, and api_key: the key of a new account that holds the credit.
What happens between the two requests
- The payment is checked against the price that was asked: the same network, token, recipient and amount. A payment for anything else is refused.
- A facilitator verifies the signature and the payer's balance, then submits the transfer and pays the gas.
- We read the transfer from the chain ourselves. Credit is created only when our own node shows it; a facilitator's word is not enough.
- The authorization is recorded. The same one cannot pay twice.
Nothing is created before step 3. A quote and a 402 answer create nothing.
Paying for one order instead
POST /api/v1/ads works the same way, with the order as the body. An order has
no price until it is written, so this request needs the order and an
Idempotency-Key header before it answers 402. The price is the one
POST /api/v1/quote names for the same order.
Repeating a request safely
A paid request that is repeated with the same Idempotency-Key returns the
first answer; it never pays twice and never places a second ad.
- For
POST /api/v1/ads, send a random UUID and keep it until you have the answer. - For
POST /api/v1/credits/{amount_usd}, the header is optional. If you send none, the server makes one and returns it in theIdempotency-Keyresponse header. Use it if you have to ask again.
If a payment is still being confirmed, the answer is payment_pending: repeat
the same request a few seconds later.
The same thing over MCP
The MCP server carries the same exchange in
tool calls: a tool that needs payment answers with the PaymentRequired object,
and takes the signed payment in _meta["x402/payment"] of the next call.
Reference
- llms.txt: the API in plain text.
- openapi.json: every answer and error code.
- /.well-known/x402: the paid resources of this site.