AGENTS

x402: pay-per-call for AI agents

x402 is Coinbase's open standard for charging per HTTP request. Your agent's server returns 402 Payment Required with a JSON body describing the price; the client pays and retries with proof. It's the missing piece that lets autonomous agents charge per-token, per-byte, per-API-call. well below Stripe's $0.30 minimum.

Tab is a turnkey backend for x402. One API call mints a spec-compliant 402 response that points to a real Tab checkout. Settlement is on-chain, multi-currency, and you keep the same dashboard, receipts, refunds, and webhooks as any other Tab payment.

Authentication

Every call to POST /api/x402/charge requires an API key with write scope. Grab one (or create a key) at /dashboard/keys. Live keys start with sk_live_, test keys with sk_test_. The key must be sent in the Authorization: Bearer … header on every /chargerequest. Without it you'll get a 401 with no x402 challenge.

GET /api/x402/verify?orderId=… is public — no Bearer header needed. The order id is a 24-char nanoid, unguessable, so the id itself is the secret. Returns { paid: true | false } plus order metadata so your agent can decide whether to release the resource.

The flow

  1. Caller hits your agent's URL without an X-PAYMENT header.
  2. Your agent calls POST /api/x402/charge on Tab with the price + the resource URL.
  3. Tab returns a spec-compliant 402 JSON body. Your agent forwards it verbatim with status 402.
  4. Caller pays via the Tab checkout URL embedded in extra.tab.checkoutUrl(or via the standard EIP-3009 path, if they're a Coinbase facilitator client).
  5. Caller retries the original request. Your agent verifies via GET /api/x402/verify?orderId=… and, on paid: true, returns the resource.

Minting a 402

POST https://thetab.bar/api/x402/charge
Authorization: Bearer sk_live_…
Content-Type: application/json

{
  "amount": "0.50",
  "chain": "base",
  "resource": "https://your-agent.example.com/research",
  "description": "Run a 500-word research summary on the supplied topic"
}

Response (HTTP 402, forward this verbatim to the caller):

{
  "x402Version": 1,
  "error": "X-PAYMENT header required",
  "accepts": [
    {
      "scheme": "exact",
      "network": "base",
      "maxAmountRequired": "500000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x…recipient",
      "resource": "https://your-agent.example.com/research",
      "description": "Run a 500-word research summary on the supplied topic",
      "mimeType": "application/json",
      "outputSchema": null,
      "maxTimeoutSeconds": 60,
      "extra": {
        "name": "USDC",
        "version": "2",
        "tab": {
          "checkoutUrl": "https://thetab.bar/pay/ord_8H3kZ…",
          "orderId": "ord_8H3kZ…",
          "verifyUrl": "https://thetab.bar/api/x402/verify?orderId=ord_8H3kZ…",
          "chain": "base",
          "currency": "USDC"
        }
      }
    }
  ]
}

Verifying a payment

On the caller's retry, hit the verify endpoint with the orderId from the original 402 response:

GET https://thetab.bar/api/x402/verify?orderId=ord_8H3kZ…

// when settled:
{
  "paid": true,
  "orderId": "ord_8H3kZ…",
  "txHash": "0x…",
  "chain": "base",
  "amount": "0.50",
  "currency": "USDC",
  "payerAddress": "0x…"
}

// still waiting:
{ "paid": false, "status": "awaiting_payment", "expiresAt": 17040… }

Or skip polling and use a Tab webhook subscription, every x402 order fires order.created on issue and order.completed on settlement, identical to any other Tab payment.

End-to-end in 20 lines

import express from "express";
const app = express();

const TAB = "https://thetab.bar";
const KEY = process.env.TAB_API_KEY!;

app.get("/research", async (req, res) => {
  const paymentHeader = req.header("x-payment");
  const orderId = req.query.orderId as string | undefined;

  if (!paymentHeader && !orderId) {
    // No payment yet, mint a 402.
    const r = await fetch(`${TAB}/api/x402/charge`, {
      method: "POST",
      headers: { authorization: `Bearer ${KEY}`, "content-type": "application/json" },
      body: JSON.stringify({
        amount: "0.50",
        chain: "base",
        resource: `${req.protocol}://${req.host}/research`,
        description: "AI-generated research summary",
      }),
    });
    res.status(402).json(await r.json());
    return;
  }

  // Caller is retrying, verify the order settled.
  const v = await fetch(`${TAB}/api/x402/verify?orderId=${orderId}`).then((r) => r.json());
  if (!v.paid) return res.status(402).json({ error: "still waiting", status: v.status });

  // Paid, return the resource.
  res.json({ summary: await runResearch(req.query.topic as string) });
});

app.listen(3000);

What you don't have to build

  • EIP-3009 / EIP-712 signing flow. Tab's checkout handles it.
  • Multi-chain routing. Tab handles USDC on Base, USDT on BSC, USDC on Solana, etc.
  • Refunds, call /api/orders/[id]/refund if your agent fails to deliver and the caller's funds go straight back on-chain.
  • Dashboards, receipts, exports, every x402 charge shows up at /dashboard/orders the same as any other Tab payment.

Cost

Same 1% protocol fee as any Tab payment. No per-call surcharge, no minimum amount, a $0.01 x402 charge nets the merchant $0.0099. That's 30× lower than Stripe's $0.30 minimum, which is what makes per-token agent billing finally viable.