tinyx402

How x402 payments work

x402 brings back the long-unused HTTP 402 Payment Required status code. The server states a price, the client attaches a signed payment, and a facilitator verifies and settles it on-chain. No accounts, invoices or card networks are involved.

The four messages

  1. 01client → tinyx402GET /api/v1/search?query=…No payment attached.
  2. 02tinyx402 → client402 + PAYMENT-REQUIREDPrice, asset, network, payTo.
  3. 03client sign EIP-3009 authorizationOff-chain. No gas, no broadcast.
  4. 04client → tinyx402GET … + PAYMENT-SIGNATURE
  5. 05tinyx402 → facilitator/verifySignature, balance, amount, expiry.
  6. 06tinyx402 run the searchIf this fails, stop here: nothing is settled.
  7. 07tinyx402 → facilitator/settleOnly for 2xx responses.
  8. 08facilitator → WhitechaintransferWithAuthorizationFacilitator pays the gas.
  9. 09tinyx402 → client200 + PAYMENT-RESPONSEResult plus receipt with the tx hash.

1. Payment required

Unpaid requests get 402 with a PAYMENT-REQUIRED header. Decoded, it looks like this:

json
{
  "x402Version": 2,
  "resource": { "url": "https://…/api/v1/search?query=x402", "description": "Structured web search results" },
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:1874",
    "asset": "0xcedfB56113143d55b2358b8CFbB0dEB5Ad2f55a7",
    "amount": "10000",
    "payTo": "0xYourPayToAddress",
    "maxTimeoutSeconds": 300,
    "extra": { "name": "Tether USD", "version": "1" }
  }]
}

extra is the token's EIP-712 domain. The client needs it to produce a signature the USDT contract will accept.

2. Sign, don't send

The client signs an EIP-3009 TransferWithAuthorization message: from you, to payTo, value amount, a validity window and a random nonce. Signing is free and off-chain. Your wallet never broadcasts a transaction, so you need no native gas token.

3. Verify, work, settle

tinyx402 asks the facilitator to verify the signature and your balance, then does the work. It only asks the facilitator to settle when the response is successful. The facilitator submits transferWithAuthorization on Whitechain and pays the gas.

4. Receipt

The final response carries PAYMENT-RESPONSE:

json
{ "success": true, "transaction": "0x8c1f…", "network": "eip155:1874", "payer": "0xYou…" }

Where it lives in the code

tinyx402 uses the official x402 SDKs (@x402/next, @x402/core, @x402/evm, @x402/fetch) and doesn't call the token contract itself.

StepFileWhat
Price and PAYMENT-REQUIREDlib/x402.tspaid() builds the exact route config (asset, amount, payTo, EIP-712 extra).
Middlewarelib/x402.tspaidRoute() wraps every handler in withX402 from @x402/next.
Facilitatorlib/x402.tsHTTPFacilitatorClient({ url: FACILITATOR_URL }), registered on x402ResourceServer with ExactEvmScheme.
Verify, then settlewithX402 / paidStream()/verify before the handler runs, /settle only for responses under 400.
Browser clientlib/pay-client.tscreatePaidFetch(): @x402/fetch plus the wallet's signTypedData.
Script clientscripts/pay.ts@x402/fetch with a private-key signer.

The middleware is per route (withX402) rather than a proxy.ts paymentProxy. paymentProxy would also settle failed responses, and it can't price from the request body or stream.

Probe it yourself:

bash
curl -i "$SITE/api/v1/search?query=x402"        # 402 + PAYMENT-REQUIRED
curl https://whitechain.007575.xyz/supported     # facilitator: exact on eip155:1874

Streaming: Agent (SSE)

/api/v1/agent/run-sse verifies the payment before the stream opens and settles only when the run reports COMPLETED. Headers are already sent by then, so the receipt arrives as the last event:

text
event: payment
data: {"success":true,"transaction":"0x…","payer":"0x…","network":"eip155:1874"}

event: done
data: {"ok":true}

If the run fails, you receive event: error with "charged": false, and the authorization is never submitted.

Long-running jobs: Research and Datasets

Research (5–45 minutes) and dataset fills (5–20 minutes) outlast any HTTP request. Their paid call starts the job and settles as soon as TinyFish accepts it. The response carries the job ID plus events_url and poll_url, which you can follow for free. A job that later fails is not refunded automatically.