Agent Quickstart
AI agents can pay per-request with no SI account or API key, using x402 (USDC on Base). exact needs only a per-request signature; upto also needs a one-time Permit2 token approval.
How It Works
- Agent sends a request to
/v1/chat/completionswith no auth header - Server returns HTTP 402 with payment requirements for both protocols
- Agent picks a protocol, signs the payment, retries the request
- Server verifies payment, routes to cheapest seller, streams response
x402 (USDC on Base)
The agent needs a Base wallet with USDC. SI advertises x402 upto first and exact second:
upto(preferred): authorize a max amount for one request, then settle only actual usage after the response completes. Requires one-time USDC approval to Permit2 plus a per-request Permit2 payment signature; CDP sponsors x402 settlement gas when available.exact(fallback): sign a fixed EIP-3009 payment for the estimated amount. Widely compatible, but may overpay versus actual usage.
The Permit2 approval is only for this per-request agent rail. It authorizes payments, not a funded balance.
Call the API directly at https://api.surplusintelligence.ai (the canonical API host) so redirects never drop the x402 payment headers.
JavaScript / TypeScript (upto, @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 = '$SURPLUS_BASE_URL/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: pass an explicit address. Do not rely on walletClient.address. 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, } // 1. Get a 402 challenge. 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(), ) // 2. Prefer upto; fall back to exact if your client cannot sign Permit2 upto. 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') // 3. Retry the same request with payment attached. 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')) // present on success console.log(await paid.json()) // OpenAI-compatible response body
Python (x402 package):
from x402 import x402_fetch response = x402_fetch( '$SURPLUS_BASE_URL/chat/completions', wallet=wallet, method='POST', json={'model': 'claude-opus-4.6', 'messages': [{'role': 'user', 'content': 'Hello'}]} )
API Key Auth (SIWE)
Agents with wallets can self-issue API keys — no browser, no Privy:
# Get SIWE challenge curl .../v1/buyer/auth/challenge?address=0xAgentWallet # Sign and get buyer API key curl -X POST .../v1/buyer/auth/keys \ -H "Content-Type: application/json" \ -d '{"message": "<SIWE message>", "signature": "0x..."}' # Returns inf_xxx buyer key # Use like any OpenAI client curl .../v1/chat/completions \ -H "Authorization: Bearer inf_xxx" \ -d '{"model": "claude-opus-4.6", "messages": [{"role": "user", "content": "Hello"}]}'
Buyer keys are persistent (they survive restarts) and complement x402, which is per-request. Use a SIWE key for a long-running agent and fund it with deposited USDC or fiat credits; use x402 for ephemeral or serverless agents that should not hold a balance at all.
Manage Keys Programmatically
Anything you can do to a key in the dashboard, you can do over the API. Provision, list, read, reconfigure, rename, and revoke keys with no browser and no Privy. This is the same /v1/buyer/keys API the dashboard itself calls, so a long-running agent can run its whole key lifecycle in code.
Authenticate every call with an existing buyer key (inf_xxx, self-issued via SIWE above) or a session cookie; the tenant is resolved from your auth. The full lifecycle:
BASE=https://api.surplusintelligence.ai KEY=inf_xxx # an existing buyer key # 1. Create a key. Idempotency-Key is required on create. curl -X POST $BASE/v1/buyer/keys \ -H "Authorization: Bearer $KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"label": "worker-01"}' # → { # "id": "01JQ8G7YV3M4N5P6Q7R8S9T0AB", # "key": "inf_9f8c1e2b3a4d5e6f7a8b9c0d1e2f3a4b", # store now: shown only once # "key_prefix": "inf_9f8c1e2b", # "label": "worker-01", # "created_at": 1713312000000 # } # 2. List keys (redacted: the secret is never returned again). curl $BASE/v1/buyer/keys -H "Authorization: Bearer $KEY" # → { "items": [ { "id": "01JQ8G7YV3M4N5P6Q7R8S9T0AB", "key_prefix": "inf_9f8c1e2b", # "label": "worker-01", "created_at": 1713312000000, "last_used_at": 1713398400000 } ] } # 3. Read one key's metadata. ID=01JQ8G7YV3M4N5P6Q7R8S9T0AB curl $BASE/v1/buyer/keys/$ID -H "Authorization: Bearer $KEY" # 4. Configure per-key routing preferences. # allow_untrusted=true lets this key route to providers outside the trusted-domain allowlist; # null or omitted inherits the account default (trusted-only). limits is reserved: send {}. curl -X PUT $BASE/v1/buyer/keys/$ID/preferences \ -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \ -d '{"preferences": {"allow_untrusted": true}, "limits": {}}' curl $BASE/v1/buyer/keys/$ID/preferences -H "Authorization: Bearer $KEY" # → { "preferences": { "allow_untrusted": true }, "limits": {} } # 5. Rename a key's label. curl -X PATCH $BASE/v1/buyer/keys/$ID \ -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \ -d '{"label": "worker-01 (prod)"}' # 6. Revoke a key (irreversible; returns 204 No Content). curl -X DELETE $BASE/v1/buyer/keys/$ID -H "Authorization: Bearer $KEY"
Keys are capped at 25 active per wallet. In a team org, mutating ops (create, rename, configure, revoke) are capability-gated, so a low-privilege member cannot reconfigure or revoke another member's key; a personal wallet or plain buyer key is unrestricted.
Become a Seller
Agents can also sell inference: list your API capacity programmatically, with no browser. As with buyer keys, whatever the seller dashboard does to a key or an offer is an API call you can make yourself.
# 1. Bootstrap a seller key via SIWE (one-time, no browser). curl ".../v1/seller/auth/challenge?address=0xAgentWallet" curl -X POST .../v1/seller/auth/keys \ -H "Content-Type: application/json" \ -d '{"message": "<SIWE message>", "signature": "0x..."}' # Returns si_seller_xxx in the "key" field (shown once). # 2. List an offer. Idempotency-Key is required on create. curl -X POST .../v1/seller/offers \ -H "Authorization: Bearer si_seller_xxx" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"model": "claude-opus-4.6", "api_key": "your-provider-key", "seller_base_url": "https://api.venice.ai/api/v1", "pricing_mode": "cost_multiplier", "cost_multiplier": 0.50}'
Seller keys support create, list, and revoke (rename and per-key preferences are buyer-only, since a seller key has no routing config):
KEY=si_seller_xxx # Create another seller key (Idempotency-Key required; up to 25 active). curl -X POST .../v1/seller/keys \ -H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" -d '{"label": "batch-bot"}' # List keys (redacted; secrets are never returned again). curl .../v1/seller/keys -H "Authorization: Bearer $KEY" # Revoke a key (returns 204). curl -X DELETE .../v1/seller/keys/{id} -H "Authorization: Bearer $KEY"
Offers, health probes, earnings, and payouts are all API-first too. See the Seller API Reference for the full surface.
Pricing
Pricing is model-specific and market-based. SI routes to the cheapest available seller for the requested model, often below direct provider rates. The final x402 maximum is returned in the 402 challenge. With upto, the buyer signs a per-request Permit2 authorization for that max but only actual usage is settled; with exact, the full estimate is settled.