Multi-facilitator, multi-chain x402 — Base, Polygon, Avalanche

Published

TL;DR

What changed

Until today, every 402 we issued contained exactly one accepts[] entry — Base mainnet via xpay. The buyer signed an EIP-3009 transferWithAuthorization for our single payTo address, the gate forwarded it to https://facilitator.xpay.sh/verify, and on success forwarded the same payload to /settle. Simple, auditable, but locked to a single facilitator and a single chain.

The 2026-05-15 rollout splits that into three independent facilitator routes, each with its own on-chain recipient address so the buyer's signature uniquely commits to one of them:

{
  "x402Version": 2,
  "error": "PAYMENT-SIGNATURE header is required",
  "resource": {
    "url": "https://war-tracker.com/api/v1/events",
    "description": "Paginated event corpus at /api/v1/events.",
    "mimeType": "application/json",
    "serviceName": "War-Tracker Events Corpus",
    "tags": ["war","conflict","events","dataset","news"]
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "5000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x…xpay…",
      "maxTimeoutSeconds": 60,
      "extra": {"name": "USD Coin", "version": "2"}
    },
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "6000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x…cdp…",
      "maxTimeoutSeconds": 60,
      "extra": {"name": "USD Coin", "version": "2"}
    },
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "6000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x…payai…",
      "maxTimeoutSeconds": 60,
      "extra": {"name": "USD Coin", "version": "2"}
    },
    { "…": "eip155:137 entries for cdp + payai" },
    { "…": "eip155:43114 entry for payai" }
  ],
  "extensions": { "bazaar": "…" }
}

A buyer wallet that holds USDC on Polygon or Avalanche can now pay us directly on that chain — no swap, no bridge, no per-chain seller integration on their end.

Facilitators in detail

xpay (https://facilitator.xpay.sh)

Coinbase Developer Platform (https://api.cdp.coinbase.com/platform/v2/x402)

PayAI (https://facilitator.payai.network)

How the gate routes the right facilitator

Every facilitator has a distinct on-chain payTo that we control. The buyer's EIP-3009 transferWithAuthorization signature commits to a specific (token, from, to, value, nonce, validAfter, validBefore) tuple — including the recipient address. So when the gate receives a signed payload back, the (network, payTo) pair uniquely identifies which facilitator's /verify and /settle endpoints to hit.

If the buyer picks the index-1 (CDP) offer on Base, the gate builds a fresh CDP JWT (120-second lifetime, EdDSA over Ed25519), posts the payload to https://api.cdp.coinbase.com/platform/v2/x402/verify, short-circuits a 402 on rejection, otherwise runs the underlying handler and POSTs to /settle. CDP's response shape carries the settled tx hash and we forward it to the client in the PAYMENT-RESPONSE header.

Economics

Pricing is per-route, USDC-denominated, billed atomically (no subscriptions, no minimums). Where the rollout adds nuance is:

Live mainnet receipts

Every advertised (facilitator, chain) cell was validated with a real paid request against /share/{id}/{slug} before this post went live. The settled txs below are public on-chain — anyone can verify them in a block explorer:

Facilitator Chain Settled tx
xpay Base mainnet 0x2dfeb24e…
cdp Base mainnet 0xce489629…
payai Base mainnet 0x554630c3…
cdp Polygon mainnet 0xd0a7261f…
payai Polygon mainnet 0x8fb20c95…
payai Avalanche C-Chain 0x46ab6c57…

The same six (facilitator, chain) cells were additionally validated across the other paid route families (/event-type/{slug}, /api/v1/events, /api/v1/events/{id}, /media/{id}) before this post was un-drafted.

What this means for buyers

If you're already on Base via xpay: nothing changes. Your old SDK still works, you still pay the base price, and you still get the same gas-sponsored settlement experience.

If you're on Polygon or Avalanche and previously had to swap into Base USDC to pay us: now you can sign directly on your native chain. Pick the accepts[] entry whose network matches your wallet's USDC balance, sign the EIP-3009 authorization for that exact payTo, and replay the request with X-PAYMENT: <base64>. The gate routes you to the matching facilitator on its own.

If you're integrating fresh today and your buyer SDK is single-offer-aware: keep picking accepts[0]; it's the xpay Base entry. If your SDK is multi-offer-aware (most modern ones are), iterate accepts[] and pick the cheapest entry your wallet can pay.

Roadmap