x402 integration
x402 is an HTTP-native payment protocol for paid APIs and paid content.
A protected endpoint returns 402 Payment Required with payment requirements.
The client signs payment data and retries with a PAYMENT-SIGNATURE header.
A facilitator verifies and settles the payment on Radius.
Radius is a strong fit for this model because fees are low and predictable.
What you can build with x402
- Per-request API billing: charge per call with immediate settlement
- Pay-per-visit content: charge for a single article, feed, or download
- Streaming payments: combine x402 with recurring payment loops for compute and inference workloads
Request lifecycle
- Client requests a protected resource.
- Server responds with
402 Payment Requiredand aPAYMENT-REQUIREDheader listing the payments it accepts. - Client signs a payment for one offer and retries with a
PAYMENT-SIGNATUREheader. - Server sends the payment to a facilitator, which verifies it and settles it on Radius.
- Server returns the resource with a
PAYMENT-RESPONSEheader that carries the transaction hash.
Add x402 payments to your app
- Accept payments: charge for routes in a Hono app or Cloudflare Worker with
radius-sdk. - Make payments: pay from an app or agent with
radius-sdk, or from a terminal withradius-cli.
Both speak standard x402 v2 with Radius defaults: SBC, Permit2 with gas sponsoring, and the Radius facilitator. The rest of this page explains what goes over the wire, which facilitators exist, and why SBC payments use Permit2. For every SDK option, see the radius-sdk reference.
The Radius payment challenge
The PAYMENT-REQUIRED header holds a Base64-encoded JSON PaymentRequired object; the response body carries no protocol data. This is the decoded challenge radius-sdk sends for a 0.1 SBC route on mainnet, with the extension's JSON Schema omitted:
{
"x402Version": 2,
"error": "Payment required",
"resource": {
"url": "https:
"description": "Premium report",
"mimeType": ""
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:723487",
"amount": "100000",
"asset": "0x33ad9e4BD16B69B5BFdED37D8B5D9fF9aba014Fb",
"payTo": "0x{{MERCHANT_ADDRESS}}",
"maxTimeoutSeconds": 300,
"extra": {
"assetTransferMethod": "permit2",
"name": "Stable Coin",
"version": "1",
"paymentFlow": "upfront"
}
}
],
"extensions": {
"eip2612GasSponsoring": {
"info": { "description": "…", "version": "1" },
"schema": {}
}
}
}| Field | Meaning |
|---|---|
network | CAIP-2 chain: eip155:723487 (mainnet) or eip155:72344 (testnet) |
amount | Base units; SBC has six decimals, so "100000" is 0.1 SBC |
extra.assetTransferMethod | permit2: the payer signs a Permit2 transfer, not a token-specific authorization |
extra.name, extra.version | The SBC EIP-712 domain, used to sign the gas-sponsoring permit |
extra.paymentFlow | Set by radius-sdk: upfront settles before the handler runs; clients echo it back unchanged |
extensions.eip2612GasSponsoring | The facilitator accepts an EIP-2612 permit for Permit2, so a first-time payer needs no approval transaction |
The client answers with a PAYMENT-SIGNATURE header: a Base64-encoded PaymentPayload that echoes the chosen offer in accepted and carries a Permit2 permitWitnessTransferFrom signature. The spender is the canonical x402ExactPermit2Proxy (0x402085c248EeA27D92E8b30b2C58ed07f9E20001), which only transfers to the payTo address. When the payer has not approved Permit2 yet, the payload also carries the EIP-2612 permit under extensions.eip2612GasSponsoring. See the x402 exact EVM scheme spec for every field, and the x402 facilitator API for /verify and /settle.
Choose a facilitator
Radius (recommended)
| Detail | Value |
|---|---|
| URL (mainnet) | https://facilitator.radiustech.xyz |
| URL (testnet) | https://facilitator.testnet.radiustech.xyz |
| Networks | Radius mainnet (eip155:723487), testnet (eip155:72344) |
| Token | SBC via Permit2 with EIP-2612 gas sponsoring |
| Protocol | x402 v2 |
| Operator | Radius (first-party) |
The Radius facilitator settles each payment in one atomic call through the Permit2 proxy and pays all gas. It exposes /supported, /verify, /settle, and /health; it is the default in radius-sdk. Integrators do not need to know its settlement wallet addresses. Query the endpoint you plan to use before deployment:
curl https://facilitator.radiustech.xyz/supported
curl https://facilitator.testnet.radiustech.xyz/supportedOther facilitators
| Facilitator | Networks | Transfer method |
|---|---|---|
| Stablecoin.xyz | Mainnet and testnet | erc2612 (permit + transferFrom) |
| Middlebit | Mainnet | Routes through Stablecoin.xyz |
erc2612 is Stablecoin.xyz's own transfer method, not part of the x402 exact EVM scheme, which defines eip3009 and permit2. radius-sdk, radius-cli, and other standard x402 clients cannot pay it; payers need a client that supports Stablecoin.xyz's format. See the Stablecoin.xyz x402 documentation to integrate with it.
To use your own facilitator with radiusPayments, pass facilitator: { url, apiKey } for one that serves the x402 facilitator API, or a FacilitatorClient from @x402/core/server.
SBC and transfer methods
The token determines which x402 transfer methods are available:
| Standard | USDC (FiatTokenV2_2) | SBC (Radius native) | Integration impact |
|---|---|---|---|
EIP-2612 (permit) | ✅ | ✅ | Gasless approvals, used for Permit2 gas sponsoring |
EIP-3009 (transferWithAuthorization) | ✅ | ❌ | The eip3009 transfer method is unavailable for SBC |
| EIP-1271 (contract-wallet signature validation) | ✅ | ❌ | Smart-account compatibility is reduced for signed payments |
Because SBC has no EIP-3009, Radius payments use Permit2. The payer grants Permit2 an allowance once, through a gas-sponsored EIP-2612 permit or an approval transaction. After that, each payment is a separate Permit2 signature capped to its amount, settled in one transaction.
Check settlement and delivery
radius-sdk performs these checks for you. If you verify payments yourself, check the payment against your own requirements before settling:
scheme,network,asset, andpayTomatch what you offeredamountis at least the price, in six-decimal base units- the signature is valid and the validity window has not expired
After settlement, store the transaction hash with an idempotency key so a retried request does not charge twice. The PAYMENT-RESPONSE header carries the hash; to reconcile a payment on-chain, use getSettlement(txHash) from radius-sdk/client.
With settle-before-handler (the radius-sdk default), a payment can succeed while the resource fails to deliver. Log the transaction hash with the failure so you can refund or serve the resource on retry.
Troubleshooting
"No available wallets in pool"
This error from the facilitator /settle endpoint means the facilitator's internal pool of settlement wallets is temporarily exhausted. This is a facilitator operational issue, not a client-side or payer-wallet problem.
Retry after a brief delay (1-2 seconds). If persistent, contact the facilitator operator or switch to an alternate facilitator.
502 from facilitator
The facilitator service is unreachable. This does not mean the payment is invalid. Retry or fail over to an alternate facilitator URL.
"Permit expired"
The validBefore or deadline timestamp in the payment authorization has passed. The paying client needs to re-sign with a fresh deadline.
Client refuses to pay
radius-sdk and radius-cli refuse offers before signing when the network, asset, or transfer method does not match, or the price is above the cap. The SDK's RadiusPaymentError.code names the reason, for example network_mismatch, asset_mismatch, unsupported_transfer_method, or price_above_limit.