x402 Protocol

HTTP 402-based per-request payment using USDC on Base. No account and no API key are required — just a wallet.

Surplus Intelligence supports two x402 payment schemes for inference:

  • upto (preferred): buyer authorizes a maximum, then only the actual post-response cost is settled.
  • exact (fallback): buyer signs a fixed pre-charge and the full estimate is settled.

Flow

1. Agent sends a request with no auth header.

2. Server returns HTTP 402 with payment requirements in accepts[].

3. Agent signs one accepted payment option.

4. Agent retries the same request with PAYMENT-SIGNATURE.

5. Server verifies payment, routes to the cheapest available seller, runs inference, then settles payment.

6. Seller is paid by the SI operator; for x402 upto, CDP sponsors the buyer→operator settlement gas when available.

Response Headers

The 402 response includes payment requirements in two places:

  • PAYMENT-REQUIRED header — base64-encoded JSON (x402 v2 canonical, used by SDKs)
  • x-payment-required header — same data (legacy compatibility)
  • Response bodyaccepts[] array with the same data in readable JSON

The payment retry must include:

  • PAYMENT-SIGNATURE header — base64-encoded signed payment payload

Successful x402-paid responses include:

  • PAYMENT-RESPONSE header — base64-encoded settlement result with transaction hash

Scheme: upto (Preferred)

upto is designed for variable-cost resources like LLM inference.
  • Buyer signs a Permit2 witness authorization for a maximum USDC amount.
  • The max is based on estimated input + max_tokens + x402 flat fee + buffer.
  • After inference succeeds, SI computes actual usage and settles only the actual buyer cost.
  • If actual usage is below the max, the buyer keeps the difference.
  • If actual usage is $0, no x402 settlement tx is needed.
  • Requires one-time USDC approval to Permit2 (0x000000000022D473030F116dDEE9F6B43aC78BA3).

Current production setup:

  • Network: Base mainnet (eip155:8453)
  • Asset: USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913)
  • x402 Upto Permit2 Proxy: 0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002
  • payTo: SI operator/treasury wallet, resolved dynamically — read it from the 402 PAYMENT-REQUIRED challenge (or GET /x402/info); do not hardcode it
  • Facilitator: external HTTP facilitator when available (CDP preferred), with self-facilitation fallback

Example upto accept entry:

{

"scheme": "upto",

"network": "eip155:8453",

"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",

"amount": "5502",

"payTo": "",

"maxTimeoutSeconds": 120,

"extra": {

"name": "USD Coin",

"version": "2",

"facilitatorAddress": "0x..."

}

}

amount is the maximum authorized amount in USDC micro-units (6 decimals). For example, 5502 means 0.005502 USDC.

Scheme: exact (Fallback)

exact uses EIP-3009 TransferWithAuthorization.
  • Buyer signs a fixed USDC transfer for the estimated max amount.
  • The facilitator settles the full fixed amount before inference proceeds.
  • This is widely supported by current x402 tooling and remains available for compatibility.
  • It may overcharge relative to actual token usage, so upto is preferred when the client supports it.

Facilitators and Gas Sponsorship

exact and upto can both be gasless for the buyer.
  • exact: CDP facilitator submits the USDC transferWithAuthorization tx and pays gas.
  • upto: when CDP advertises Base mainnet upto, CDP submits the x402UptoPermit2Proxy.settle() tx and pays gas.
  • Fallback: if no external upto facilitator is available, SI can self-facilitate using the operator wallet.

Configuration knobs:

  • X402_FACILITATOR_URL — default HTTP facilitator (production uses CDP)
  • UPTO_FACILITATOR_MODE=auto|cdp|http|self — default auto
  • UPTO_FACILITATOR_URL — optional explicit upto facilitator URL
  • UPTO_FACILITATOR_ADDRESS — optional explicit facilitator signer address
  • X402_FLAT_FEE_MICRO — x402-only flat fee in USDC micro-units

Pricing

Pricing is model-specific and market-based. SI routes to the cheapest available seller for the requested model. Prices are often below direct provider rates, but the final amount is always returned in the 402 challenge.

For x402 payments, SI adds an x402-only flat convenience fee (X402_FLAT_FEE_MICRO) to cover facilitation/settlement overhead. This fee does not apply to the normal SIWE + one-time USDC approval path.

Dual 402 Response

When no auth is provided, the server returns a 402 that advertises:

  • x402 upto (preferred)
  • x402 exact (fallback)
  • MPP / Tempo, when available

Agents choose the payment rail they support.

Client Libraries

  • JavaScript/TypeScript: @x402/core + @x402/evm, or higher-level x402 fetch wrappers
  • Python: x402 package where available

Agent Happy Path: Buy Chat Inference with upto

x402 inference is native on the /v1 API surface — pay per request with the PAYMENT-SIGNATURE header, no separate wrapper path needed:

POST https://api.surplusintelligence.ai/v1/chat/completions

Minimal request body:

{

"model": "llama-3.3-70b",

"messages": [{ "role": "user", "content": "Say exactly: pong" }],

"max_tokens": 8

}

Algorithm for agents:

1. POST the request body without Authorization.

2. Decode the PAYMENT-REQUIRED header as base64 JSON.

3. Select accepts.find((a) => a.scheme === "upto") when present.

4. Sign that payment requirement.

5. Retry the same request body with PAYMENT-SIGNATURE: base64(JSON.stringify(paymentPayload)).

6. Success is HTTP 200 plus a PAYMENT-RESPONSE header and an OpenAI-compatible response body.

TypeScript example (@x402/evm + viem)

import { createPublicClient, createWalletClient, http, publicActions } from 'viem'

import { base } from 'viem/chains'

import { privateKeyToAccount } from 'viem/accounts'

import { UptoEvmScheme } from '@x402/evm'

const endpoint = 'https://api.surplusintelligence.ai/v1/chat/completions'

const account = privateKeyToAccount(process.env.PRIVATE_KEY as 0x${string})

const publicClient = createPublicClient({ chain: base, transport: http() })

const walletClient = createWalletClient({ account, chain: base, transport: http() }).extend(publicActions)

// Important: UptoEvmScheme needs a signer with an explicit address.

// Do not rely on walletClient.address if it is undefined.

const signer = {

address: account.address,

signTypedData: (msg: any) => account.signTypedData(msg),

readContract: publicClient.readContract.bind(publicClient),

getTransactionCount: publicClient.getTransactionCount.bind(publicClient),

estimateFeesPerGas: publicClient.estimateFeesPerGas.bind(publicClient),

}

const body = {

model: 'llama-3.3-70b',

messages: [{ role: 'user', content: 'Say exactly: pong' }],

max_tokens: 8,

}

const challenge = await fetch(endpoint, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify(body),

})

const paymentRequired = JSON.parse(

Buffer.from(challenge.headers.get('PAYMENT-REQUIRED')!, 'base64').toString(),

)

const upto = paymentRequired.accepts.find((a: any) => a.scheme === 'upto')

const paymentPayload = await new UptoEvmScheme(signer).createPaymentPayload(2, upto)

const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString('base64')

const paid = await fetch(endpoint, {

method: 'POST',

headers: {

'Content-Type': 'application/json',

'PAYMENT-SIGNATURE': paymentHeader,

},

body: JSON.stringify(body),

})

console.log(paid.status) // 200

console.log(paid.headers.get('PAYMENT-RESPONSE')) // settlement metadata

console.log(await paid.json()) // OpenAI-compatible completion

See /docs/getting-started/agent-quickstart.md for more examples.