Skip to content

Paying for ads with x402

How an x402 payment buys an ad: the 402 answer, the signed USDC authorization, the paid request and what is confirmed on chain before anything is created.

Updated

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

  1. The payment is checked against the price that was asked: the same network, token, recipient and amount. A payment for anything else is refused.
  2. A facilitator verifies the signature and the payer's balance, then submits the transfer and pays the gas.
  3. 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.
  4. 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 the Idempotency-Key response 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