402tools402 docs
docs · live

The 402 dance

The "402 dance" is the canonical name for the 3-step exchange between a buyer agent and a tools402 endpoint. It's the same on all three chains; only the on-chain transfer mechanism differs. Below is the EVM version (Base / Polygon). The Solana version differs only in step 2.

#The 3 steps

#1. Ask the endpoint — get a 402 quote

http
POST /v1/pdf-md HTTP/1.1
Host: api.tools402.dev
Content-Type: multipart/form-data

[your request body]

Response :

http
HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "x402Version": 1,
  "accepts": [
    {
      "scheme": "exact",
      "network": "base",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0xD6E8aF2F65B4C9ACC7BF14A3096056e89E312878",
      "maxAmountRequired": "10000",
      "maxTimeoutSeconds": 60
    },
    {
      "scheme": "exact",
      "network": "polygon",
      "asset": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
      "payTo": "0xD6E8aF2F65B4C9ACC7BF14A3096056e89E312878",
      "maxAmountRequired": "10000",
      "maxTimeoutSeconds": 60
    },
    {
      "scheme": "spl-transfer",
      "network": "solana",
      "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "payTo": "Gt9EC4XYqD9pUmTFAfBy9b3gbGG8eiv3ZNLMLCuyU8w8",
      "maxAmountRequired": "10000",
      "maxTimeoutSeconds": 60
    }
  ],
  "error": "X-PAYMENT header is required"
}

Pick one entry in accepts[] — the chain you have USDC on. The other entries describe the same payment on different chains; pay on one.

#2. Pay USDC on-chain

EVM (Base / Polygon)

Transfer the maxAmountRequired atomic amount in USDC to payTo. The simplest path is a vanilla ERC-20.transfer() :

bash
cast send 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \
     "transfer(address,uint256)" \
     0xD6E8aF2F65B4C9ACC7BF14A3096056e89E312878 10000 \
     --rpc-url https://mainnet.base.org

Wait for at least one block confirmation before step 3. On Base that's ~1.2 s. On Polygon ~2 s.

For gas-less payments via EIP-3009 transferWithAuthorization, see /buy-side/typescript.

Solana

The Solana flow is different : you partial-sign an SPL transfer and the facilitator broadcasts. See /chains/solana for the full flow.

#3. Retry with X-Payment receipt

http
POST /v1/pdf-md HTTP/1.1
Host: api.tools402.dev
Content-Type: multipart/form-data
X-Payment: eyJ4NDAyVi…ifQ

[your request body — same as step 1]

The X-Payment header is a base64url-encoded JSON with the tx hash :

json
{
  "x402Version": 1,
  "scheme": "exact",
  "network": "base",
  "payload": { "tx_hash": "0xabc…" }
}

The marketplace verifies the tx on-chain (correct to, amount, asset, not already consumed) and resolves your endpoint call.

Response (200 OK) :

http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "markdown": "# Your receipt …",
  "page_count": 1
}

#Idempotency

The marketplace deduplicates by tx hash. If your network drops and you retry step 3 with the same X-Payment, you get the same response. The endpoint runs once; you don't pay twice for the same retry.

#Replay protection

Tx hashes are recorded in a replay log (SQLite WAL, R2-backed). A second attempt to use the same tx hash at step 3 — on a different endpoint or after the first call — returns 409 Conflict. One on-chain transfer authorises one endpoint call.

#Failure modes

  • Quote expired (> maxTimeoutSeconds since step 1) → re-quote at step 1, pay again at step 2. Your previous USDC is lost (the marketplace doesn't refund off-chain).
  • Tx not confirmed yet at step 3 → wait one block, retry. The marketplace polls the chain for ~10 s before declaring the tx missing.
  • Tx amount mismatch (paid less than maxAmountRequired) → returns 402 with the same quote. Pay the correct amount.
  • Tx already consumed (replay attempt) → 409 Conflict.
  • Upstream 5xx after settlement (proxy mode) → marketplace retries upstream for up to 60 s. If still 5xx, returns 502 with the tx hash in X-Tools402-Tx header. The buyer's payment is not refunded — the tx is auditable on chain.

#Why no off-chain refund

By design. The payment is verified on the request, not on the response. If the upstream compute fails after settlement, the buyer has on-chain proof of payment (the tx hash) and can dispute with the seller directly. The marketplace stays neutral — it cannot freeze either party's funds.

A persistent failure (seller's endpoint consistently returns 5xx) flips the listing to degraded then suspended. New buyer calls are blocked until the seller resumes (signs POST /v1/_seller/<wallet>/resume).