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 signs a per-request Permit2 authorization for 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) plus the per-request Permit2 payment signature.
  • This Permit2 approval is separate from /buy / API-key marketplace SettlementV2 approval. It does not satisfy SettlementV2 allowance checks, and SettlementV2 approval does not satisfy x402 upto.

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:

json
{
  "scheme": "upto",
  "network": "eip155:8453",
  "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "amount": "5502",
  "payTo": "<operator address from the 402 challenge>",
  "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 + API-key SettlementV2 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. Do not treat x402 Permit2 approval as a drop-in spender swap for /buy: a simple /buy spender change to Permit2 would still be a standing allowance. Only a future redesign that carries per-request x402/Permit2 payment proofs through /buy would remove that standing-allowance risk.

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:

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

Minimal request body:

json
{
  "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 (for upto, a per-request Permit2 payment authorization).
  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)

ts
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.