x402 flow
XDC AI uses the x402 protocol: HTTP-native, pay-per-request payments. Payment is an off-chain signed authorization that a relayer settles on-chain — so the paying agent never holds gas.
Sequence
Agent Service Relayer / XDC
│ GET /x402/resource │ │
├─────────────────────────────▶ │
│ 402 Payment Required │ │
│ { accepts: [ amount, asset,│ │
│ payTo, network ] } │ │
◀─────────────────────────────┤ │
│ │ │
│ sign EIP-3009 transferWith- │ │
│ Authorization (USDC → payTo)│ │
│ │ │
│ GET /x402/resource │ │
│ X-PAYMENT: base64(auth) ────▶ verify signature + amount │
│ ├──────── settle authorization ──▶ on-chain USDC
│ │ (relayer pays gas) │ transfer
│ 200 OK + data ◀───────── confirmed ───────────┤
◀─────────────────────────────┤ │
Steps in detail
- Request. The agent calls the service URL with no payment.
- Challenge. The service returns
402 Payment Requiredwith the requirements:scheme(exact),network(xdc), USDCasset,maxAmountRequired(base units), andpayTo. - Authorize. The agent signs an EIP-3009
transferWithAuthorization— an off-chain EIP-712 signature authorizing exactly the required USDC topayTo. No gas, no on-chain tx yet. - Resend. The agent repeats the request with
X-PAYMENT: <base64 of the signed authorization>. - Verify. The service (via a facilitator) checks the signature, amount, recipient, and network.
- Settle. A relayer/sponsor submits the
transferWithAuthorizationon XDC and pays the gas. The USDC moves from the agent's wallet to the provider'spayTo. - Respond. The service returns
200 OKwith the data.
Protocol versions: v1 and v2
XDC AI speaks both x402 v1 and the newer x402 v2. The 402 challenge advertises both at once, so the published CLI and any older client keep working while newer clients use v2. v2 changes only the envelope - the EIP-3009 authorization and the EIP-712 signing are identical, so nothing about the money movement changes. v2 on XDC AI is mainnet only (eip155:50).
How to tell a v2 challenge from a v1 one
| Signal | v1 | v2 |
|---|---|---|
| Version field | x402Version: 1 | x402Version: 2 |
| Amount field | maxAmountRequired | amount |
network | label, e.g. "xdc" | CAIP-2, e.g. "eip155:50" |
extra | { name, version } | adds assetTransferMethod: "eip3009" |
| Payment header (request) | X-PAYMENT | PAYMENT-SIGNATURE |
| Receipt header (response) | X-PAYMENT-RESPONSE | PAYMENT-RESPONSE |
The most reliable check is the shape of each accepts[] entry: an entry with amount and an eip155:<chainId> network is v2; an entry with maxAmountRequired and a plain label is v1.
Dual advertisement
A single XDC AI 402 returns both entries in accepts[] (v1 first for back-compat, v2 second), and emits both receipt headers on settlement. So a client should scan accepts[] for a v2-shaped entry and prefer it, rather than trusting the top-level x402Version:
{
"x402Version": 1,
"accepts": [
{ "scheme": "exact", "network": "xdc", "maxAmountRequired": "100", "payTo": "0x…", "asset": "0xfA29…", "maxTimeoutSeconds": 120, "extra": { "name": "USDC", "version": "2" } },
{ "x402Version": 2, "scheme": "exact", "network": "eip155:50", "amount": "100", "payTo": "0x…", "asset": "0xfA29…", "maxTimeoutSeconds": 120, "extra": { "assetTransferMethod": "eip3009", "name": "USDC", "version": "2" } }
]
}
The xdcai CLI and the MCP connector auto-negotiate this: they prefer the v2 entry, sign once, and send the PAYMENT-SIGNATURE header. You never pick a version by hand.
Why gasless
EIP-3009 lets a third party submit a token transfer on behalf of the signer. The agent only ever signs; a sponsor relayer broadcasts and covers gas. That's why an agent wallet needs only USDC, never native XDC. See Wallet & gasless.
For implementers
- The
402amount is in USDC base units (6 decimals):10000=0.01USDC. Read it fromamount(v2) ormaxAmountRequired(v1). - The payment header is base64 of the signed authorization payload: send it as
PAYMENT-SIGNATUREfor v2,X-PAYMENTfor v1. - Read the settlement receipt from
PAYMENT-RESPONSE(v2) orX-PAYMENT-RESPONSE(v1); XDC AI emits both. - Settlement should be idempotent per nonce to make retries safe.
- Advertised price /
asset/payTomust match the402challenge.
See also: Make an x402 endpoint · Constants.