Payments (x402)
Two methods cover every x402 flow:
| Method | Use it when |
|---|---|
client.x402.pay(input) | You want a single call that signs and settles on-chain. Most apps. |
client.x402.sign(input) | You want the signed payload so you can forward it elsewhere (another facilitator, a queue, a different settle path). |
Both produce a canonical x402 v2 payload (EIP-3009 TransferWithAuthorization against the token’s own domain) so any x402-compatible facilitator can settle it.
Supported tokens
Section titled “Supported tokens”| Symbol | What it is | Peg | Chains | Cap accounting |
|---|---|---|---|---|
USDC | Circle USD Coin (default) | $1 | Avalanche, Fuji | 1 USDC = $1 against your USD caps |
XSGD | StraitsX Singapore-dollar stablecoin | 1 SGD (~US$0.77) | Avalanche, Fuji | Converted at the live Chainlink SGD/USD rate — 10 XSGD counts as ≈ $7.75, not $10 |
Pass the symbol via tokenSymbol. The agent’s policy must allow it (allowedTokens) — tier ceilings include both USDC and XSGD, but agents created before XSGD support keep their stored ['USDC']-only policy; update it once with policy.setAgent to add 'XSGD'.
// Pay 10 XSGD (6 decimals, like USDC)await client.x402.pay({ agentId: 'trading-bot-1', to: '0xMerchant...', value: '10000000', tokenSymbol: 'XSGD',});The full current list, including each token’s settlement mode and signing domain, is served live at GET /tokens on the facilitator.
x402.pay(input)
Section titled “x402.pay(input)”Sign and settle in one call. This is what 90% of apps want.
const result = await client.x402.pay({ agentId: 'trading-bot-1', // required to: '0xMerchant...', // required — recipient EOA value: '500000', // required — atomic units (USDC/XSGD have 6 decimals) tokenSymbol: 'USDC', // optional — 'USDC' (default) or 'XSGD' chain: 'avalanche', // optional — default = agent's stored chain validBefore: 1781000000, // optional — default = now + 5 minutes (max 1 hour) nonce: '0x...', // optional — auto-generated 32-byte random settle: true, // optional — default true. Set false to skip the on-chain submit.});Returns
Section titled “Returns”{ "agentId": "trading-bot-1", "signed": { "payerAddress": "0xb0e6ec46cd2d97de006f9318ac5c7994b7aadd2b", "paymentPayload": { "token": "0x5425890298aed601595a70AB815c96711a31Bc65", "payload": { "authorization": { "from": "0xb0e6ec46cd2d97de006f9318ac5c7994b7aadd2b", "to": "0xMerchant...", "value": "500000", "validAfter": 1781000040, "validBefore": 1781000340, "nonce": "0x..." }, "signature": "0x..." } }, "paymentRequirements": { "network": "avalanche-testnet", "chainId": 43113 }, "policy": { "perTxCapUSD": 5, "perDayCapUSD": 50, "spentTodayUSD": "0.500000", "remainingTodayUSD": "49.500000" } }, "settle": { "success": true, "transaction": "0x5972437a3695b2641855416da6b7f936ceda300c7536189a5dc237e1cc21a8e7", "network": "avalanche-fuji", "payer": "0xb0e6ec46cd2d97de006f9318ac5c7994b7aadd2b", "blockNumber": 56206516 }}Inspect result.settle?.transaction for the on-chain hash. Inspect result.signed.policy to see your remaining daily quota.
x402.sign(input)
Section titled “x402.sign(input)”Produces a signed payload without submitting it on-chain. Useful when:
- You want to forward the payload to a different facilitator.
- You’re queueing payments for batch settlement.
- The merchant wants to settle the payload themselves.
const signed = await client.x402.sign({ agentId: 'trading-bot-1', to: '0xMerchant...', value: '500000',});
// Forward signed.paymentPayload + signed.paymentRequirements wherever you want.Same return shape as the signed block in pay(). The signature is valid and on-chain settle-able for the lifetime of validBefore.
Validity window
Section titled “Validity window”Every authorization carries a validAfter and validBefore (unix seconds). Defaults:
| Field | Default | Notes |
|---|---|---|
validAfter | now − 60s | Small clock-skew tolerance for the recipient. |
validBefore | now + 300s (5 min) | Tight by design — limits exposure if a signature is leaked or an agent is revoked. |
The platform caps validBefore at now + 1 hour. Anything beyond that is a 400 validBefore window cannot exceed 3600s.
If you need long-lived payment instructions, sign multiple short-lived authorizations rather than one long one.
Policy enforcement (before any signature)
Section titled “Policy enforcement (before any signature)”Before the platform signs, it checks all of:
| Check | Failure |
|---|---|
Agent exists and is active | 403 Agent ... is revoked — cannot sign |
chain is in policy.allowedChains | 403 Chain ... not in agent policy |
tokenSymbol is in policy.allowedTokens | 403 Token ... not in agent policy |
value ≤ policy.perTxCapUSD | 403 per_tx_cap_exceeded |
Today’s spend + value ≤ policy.perDayCapUSD | 403 per_day_cap_exceeded |
| Validity window valid and within max | 400 |
Every check happens server-side. Failed checks consume no gas (no signature is produced).
Calling the facilitator directly
Section titled “Calling the facilitator directly”If you have a signed payload from somewhere else (another platform, a self-hosted agent), you can settle it via:
const result = await client.facilitator.settle( signed.paymentPayload, signed.paymentRequirements,);Or verify without spending gas:
const v = await client.facilitator.verify( signed.paymentPayload, signed.paymentRequirements,);console.log(v.isValid); // true/falseSee x402 Protocol → Facilitator API for the underlying REST contract.
Patterns
Section titled “Patterns”Pay-per-call with daily budget
Section titled “Pay-per-call with daily budget”The platform tracks per-day spend automatically. You don’t need a counter in your code:
try { await client.x402.pay({ agentId, to, value: '100000' }); // 0.1 USDC} catch (err) { if (err.code === 'per_day_cap_exceeded') { // Agent is done for today — notify ops, or fall back to a different agent. }}Sign, queue, settle later
Section titled “Sign, queue, settle later”// In your producerconst { paymentPayload, paymentRequirements } = await client.x402.sign({ agentId, to, value: '500000', validBefore: Math.floor(Date.now() / 1000) + 3000, // 50 min ahead — within the 1-hour cap});queue.push({ paymentPayload, paymentRequirements });
// In your worker, sometime later (but before validBefore)const queued = queue.pop();await client.facilitator.settle(queued.paymentPayload, queued.paymentRequirements);Multiple agents on different chains
Section titled “Multiple agents on different chains”const usAgent = await client.agents.create({ agentId: 'us-bot', chain: 'avalanche' });const baseBot = await client.agents.create({ agentId: 'base-bot', chain: 'base' });
await client.x402.pay({ agentId: 'us-bot', to: '0x...', value: '1000000' }); // settles on Avalancheawait client.x402.pay({ agentId: 'base-bot', to: '0x...', value: '1000000' }); // settles on BaseSee also
Section titled “See also”- Agents — create wallets to spend from.
- Policy — change caps and allowed tokens.
- Errors — full error reference including
per_tx_cap_exceededand friends. - x402 Protocol → Facilitator API — the REST contract
pay()andsign()build on.