x402 Facilitator
x402 is now available. Contact help@arkhia.io to request access.
x402 Facilitator via Arkhia
April 2026
Arkhia now supports x402 — an open standard for machine-to-machine HTTP payments using tokens like USDC on various networks like Ethereum. Route your facilitation requests through Arkhia's authenticated proxy for usage tracking, rate limiting, and credit-based billing.
What is x402?
x402 is an HTTP-native payment protocol where the server responds with a 402 Payment Required status and payment instructions. The client creates an ERC-3009 TransferWithAuthorization signature and submits it to a facilitator, which executes the on-chain token transfer.
Arkhia acts as an authenticated proxy between agents and the x402 payment protocol — your API key is part of the URL, enabling per-project usage tracking.
🌐 URL Format
https://starter.arkhia.io/x402/{chain}/{network}/{api-key}/{endpoint}
| Segment | Values | Description |
|---|---|---|
chain | ethereum, polygon, base, solana | Target blockchain |
network | mainnet, testnet | Target network environment |
api-key | Your project API key | From the Arkhia dashboard |
endpoint | supported, verify, settle | Facilitator endpoint |
Example:
https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY/supported
Prerequisites
- An Arkhia account with x402 access enabled
- A project created in the Arkhia Dashboard — the network will show x402 Facilitator
- Your project API key from the Project Details → Endpoints section
- A network-appropriate wallet ready to receive token payments
Endpoints
NOTE: Ethereum Testnet (Sepolia) with the USDC token are used in the examples.
GET /supported
Returns the payment schemes and networks the facilitator supports.
curl "https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY/supported"
Response:
{
"kinds": [
{ "x402Version": 1, "scheme": "exact", "network": "sepolia" },
{ "x402Version": 2, "scheme": "exact", "network": "eip155:11155111" }
]
}
GET /health
Check that the facilitator service is running and ready to receive requests.
curl "https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY/health"
Response:
{ "status": "ok" }
POST /verify
Verifies an x402 payment against the provided payment requirements.
Code Example:
curl -X POST "https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY/verify" \
-H "Content-Type: application/json" \
-d '{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "eip155:11155111",
"payload": {
"authorization": {
"from": "0xPAYER_ADDRESS",
"to": "0xFACILITATOR_ADDRESS",
"value": "1000000",
"validAfter": 1700000000,
"validBefore": 1700003600,
"nonce": "0xNONCE"
},
"signature": "0xSIGNED_ERC3009_SIGNATURE"
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:11155111",
"maxAmountRequired": "1000000",
"asset": "0xUSDC_CONTRACT_ADDRESS",
"payTo": "0xFACILITATOR_ADDRESS"
}
}'
Request:
Supports both x402 v1 and v2 formats.
- v1: requirements provided separately
- v2: requirements embedded in
acceptedfor deterministic validation
V1 (EIP-3009 / Permit2)
{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "eip155:11155111",
"payload": {
"authorization": {
"from": "0xPAYER_ADDRESS",
"to": "0xFACILITATOR_ADDRESS",
"value": "1000000",
"validAfter": 1700000000,
"validBefore": 1700003600,
"nonce": "0xNONCE"
},
"signature": "0xSIGNED_ERC3009_SIGNATURE"
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:11155111",
"maxAmountRequired": "1000000",
"asset": "0xUSDC_CONTRACT_ADDRESS",
"payTo": "0xFACILITATOR_ADDRESS"
}
}
V2
{
"x402Version": 2,
"paymentPayload": {
"x402Version": 2,
"accepted": {
"scheme": "exact",
"network": "eip155:11155111",
"amount": "1000000",
"asset": "0xUSDC_CONTRACT_ADDRESS",
"payTo": "0xFACILITATOR_ADDRESS"
},
"payload": {
"authorization": {
"from": "0xPAYER_ADDRESS",
"to": "0xFACILITATOR_ADDRESS",
"value": "1000000",
"validAfter": 1700000000,
"validBefore": 1700003600,
"nonce": "0xNONCE"
},
"signature": "0xSIGNED_ERC3009_SIGNATURE"
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:11155111",
"amount": "1000000",
"asset": "0xUSDC_CONTRACT_ADDRESS",
"payTo": "0xFACILITATOR_ADDRESS"
}
}
Response:
{
"isValid": true,
"payer": "0x...",
"invalidReason": null,
"details": {}
}
POST /settle
Submit a signed ERC-3009 authorization for on-chain settlement. This is the core endpoint of the x402 flow.
Code Example:
curl -X POST "https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY/settle" \
-H "Content-Type: application/json" \
-d '{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "eip155:11155111",
"payload": {
"authorization": {
"from": "0xPAYER_ADDRESS",
"to": "0xFACILITATOR_ADDRESS",
"value": "1000000",
"validAfter": 1700000000,
"validBefore": 1700003600,
"nonce": "0xNONCE"
},
"signature": "0xSIGNED_ERC3009_SIGNATURE"
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:11155111",
"maxAmountRequired": "1000000",
"asset": "0xUSDC_CONTRACT_ADDRESS",
"payTo": "0xFACILITATOR_ADDRESS"
}
}'
Request:
Supports both x402 v1 and v2 formats.
- v1: requirements provided separately
- v2: requirements embedded in
acceptedfor deterministic validation
V1 (EIP-3009 / Permit2)
{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "eip155:11155111",
"payload": {
"authorization": {
"from": "0xPAYER_ADDRESS",
"to": "0xFACILITATOR_ADDRESS",
"value": "1000000",
"validAfter": 1700000000,
"validBefore": 1700003600,
"nonce": "0xNONCE"
},
"signature": "0xSIGNED_ERC3009_SIGNATURE"
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:11155111",
"maxAmountRequired": "1000000",
"asset": "0xUSDC_CONTRACT_ADDRESS",
"payTo": "0xFACILITATOR_ADDRESS"
}
}
V2
{
"x402Version": 2,
"paymentPayload": {
"x402Version": 2,
"accepted": {
"scheme": "exact",
"network": "eip155:11155111",
"amount": "1000000",
"asset": "0xUSDC_CONTRACT_ADDRESS",
"payTo": "0xFACILITATOR_ADDRESS"
},
"payload": {
"authorization": {
"from": "0xPAYER_ADDRESS",
"to": "0xFACILITATOR_ADDRESS",
"value": "1000000",
"validAfter": 1700000000,
"validBefore": 1700003600,
"nonce": "0xNONCE"
},
"signature": "0xSIGNED_ERC3009_SIGNATURE"
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:11155111",
"amount": "1000000",
"asset": "0xUSDC_CONTRACT_ADDRESS",
"payTo": "0xFACILITATOR_ADDRESS"
}
}
Response:
{
"success": true,
"transaction": "0x...",
"payer": "0x...",
"network": "eip155:11155111",
"errorReason": null
}
Settlement Flow
Your App → GET /supported → Get the facilitator's supported networks and payment schemes
Your App → POST /verify → Validate the the user's attempted payment matches your requirements
Your App → POST /settle → Facilitator processes the payment on-chain and returns a confirmation
Code Examples
- cURL
- Python
- JavaScript
# 1. Check supported schemes
curl "https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY/supported"
# 2. Submit a settlement (v1 format)
curl -X POST "https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY/settle" \
-H "Content-Type: application/json" \
-d '{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "sepolia",
"payload": {
"authorization": {
"from": "0xPAYER_ADDRESS",
"to": "0xFACILITATOR_ADDRESS",
"value": "1000000",
"validAfter": 1700000000,
"validBefore": 1700003600,
"nonce": "0xRANDOM_NONCE_32_BYTES"
},
"signature": "0xSIGNED_ERC3009_SIGNATURE"
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "sepolia",
"maxAmountRequired": "1000000",
"asset": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
"payTo": "0xFACILITATOR_ADDRESS"
}
}'
import os
import time
import secrets
import asyncio
import httpx
from eth_account import Account
from eth_account.messages import encode_typed_data
from web3 import Web3, AsyncWeb3
FACILITATOR_URL = "https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY"
RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"
PAYER_PRIVATE_KEY = os.getenv("PAYER_PRIVATE_KEY")
USDC_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
FACILITATOR_ADDRESS = "0x5F0ddADAdb6938C96c6D0C4Fa3708D06306e5141"
MINIMAL_USDC_ABI = [
{"inputs": [], "name": "name", "outputs": [{"type": "string"}], "stateMutability": "view", "type": "function"},
{"inputs": [], "name": "DOMAIN_SEPARATOR", "outputs": [{"type": "bytes32"}], "stateMutability": "view", "type": "function"},
]
async def sign_erc3009(w3, token_address, private_key, authorization):
account = Account.from_key(private_key)
contract = w3.eth.contract(address=Web3.to_checksum_address(token_address), abi=MINIMAL_USDC_ABI)
token_name = await contract.functions.name().call()
chain_id = await w3.eth.chain_id
signable = encode_typed_data(
domain_data={"name": token_name, "version": "2", "chainId": chain_id, "verifyingContract": Web3.to_checksum_address(token_address)},
message_types={"TransferWithAuthorization": [
{"name": "from", "type": "address"}, {"name": "to", "type": "address"},
{"name": "value", "type": "uint256"}, {"name": "validAfter", "type": "uint256"},
{"name": "validBefore", "type": "uint256"}, {"name": "nonce", "type": "bytes32"},
]},
message_data={
"from": Web3.to_checksum_address(authorization["from"]),
"to": Web3.to_checksum_address(authorization["to"]),
"value": int(authorization["value"]),
"validAfter": int(authorization["validAfter"]),
"validBefore": int(authorization["validBefore"]),
"nonce": Web3.to_bytes(hexstr=authorization["nonce"]),
},
)
return account.sign_message(signable).signature.hex()
async def settle():
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(RPC_URL))
account = Account.from_key(PAYER_PRIVATE_KEY)
now = int(time.time())
authorization = {
"from": account.address,
"to": FACILITATOR_ADDRESS,
"value": str(1 * 10**6), # 1 USDC
"validAfter": now - 60,
"validBefore": now + 3600,
"nonce": "0x" + secrets.token_hex(32),
}
signature = await sign_erc3009(w3, USDC_ADDRESS, PAYER_PRIVATE_KEY, authorization)
payload = {
"x402Version": 1,
"paymentPayload": {
"x402Version": 1, "scheme": "exact", "network": "sepolia",
"payload": {"authorization": authorization, "signature": signature},
},
"paymentRequirements": {
"scheme": "exact", "network": "sepolia",
"maxAmountRequired": authorization["value"],
"asset": USDC_ADDRESS, "payTo": FACILITATOR_ADDRESS,
},
}
async with httpx.AsyncClient(timeout=60.0) as client:
resp = await client.post(f"{FACILITATOR_URL}/settle", json=payload)
result = resp.json()
if result.get("success"):
print(f"Settlement successful! TX: {result.get('transaction')}")
else:
print(f"Settlement failed: {result.get('errorReason')}")
asyncio.run(settle())
import axios from 'axios';
import { ethers } from 'ethers';
const FACILITATOR_URL = 'https://starter.arkhia.io/x402/ethereum/testnet/YOUR_API_KEY';
const RPC_URL = 'https://ethereum-sepolia-rpc.publicnode.com';
const USDC_ADDRESS = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238';
const FACILITATOR_ADDRESS = '0x5F0ddADAdb6938C96c6D0C4Fa3708D06306e5141';
const USDC_ABI = ['function name() view returns (string)'];
async function settle(privateKey) {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(privateKey, provider);
const usdc = new ethers.Contract(USDC_ADDRESS, USDC_ABI, provider);
const { chainId } = await provider.getNetwork();
const tokenName = await usdc.name();
const now = Math.floor(Date.now() / 1000);
const nonce = ethers.hexlify(ethers.randomBytes(32));
const domain = { name: tokenName, version: '2', chainId, verifyingContract: USDC_ADDRESS };
const types = {
TransferWithAuthorization: [
{ name: 'from', type: 'address' }, { name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' }, { name: 'validAfter', type: 'uint256' },
{ name: 'validBefore', type: 'uint256' }, { name: 'nonce', type: 'bytes32' },
],
};
const value = { from: wallet.address, to: FACILITATOR_ADDRESS, value: 1_000_000n, validAfter: now - 60, validBefore: now + 3600, nonce };
const signature = await wallet.signTypedData(domain, types, value);
const payload = {
x402Version: 1,
paymentPayload: {
x402Version: 1, scheme: 'exact', network: 'sepolia',
payload: { authorization: { ...value, value: value.value.toString() }, signature },
},
paymentRequirements: {
scheme: 'exact', network: 'sepolia',
maxAmountRequired: '1000000', asset: USDC_ADDRESS, payTo: FACILITATOR_ADDRESS,
},
};
const { data } = await axios.post(`${FACILITATOR_URL}/settle`, payload);
if (data.success) {
console.log('Settlement successful! TX:', data.transaction);
} else {
console.error('Settlement failed:', data.errorReason);
}
}
settle(process.env.PAYER_PRIVATE_KEY);
Credit Tracking
All x402 requests are tracked in the Arkhia credit system:
- Each request to the x402 proxy consumes your credits
- Usage is visible in the Analytics section of your Arkhia dashboard per project
- x402 requests appear as API type
x402in the usage breakdown
Supported Networks
| Chain | Network | URL Segment | Protocol Identifier | Environment |
|---|---|---|---|---|
| Ethereum | Mainnet | ethereum/mainnet | eip155:1 | Mainnet |
| Ethereum | Sepolia | ethereum/testnet | eip155:11155111 | Testnet |
| Polygon | Mainnet | polygon/mainnet | eip155:137 | Mainnet |
| Polygon | Amoy | polygon/testnet | eip155:80002 | Testnet |
| Base | Mainnet | base/mainnet | eip155:8453 | Mainnet |
| Base | Sepolia | base/testnet | eip155:84532 | Testnet |
| Solana | Mainnet | solana/mainnet | solana-mainnet | Mainnet |
| Solana | Testnet | solana/testnet | solana-testnet | Testnet |
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
Unauthorized access to x402 facilitator service | Project not enabled for x402 | Contact support to enable x402 on your plan |
ERC20: transfer amount exceeds balance | Payer wallet has insufficient tokens | Fund the wallet |
x402 request body is required | POST body is empty or missing | Ensure you send a valid JSON payload |
x402 Payment request failed | Upstream facilitator error | Check your network selection and payment payload structure |