Spend governance & API keys
Wallet-issued keys with per-key budgets, enforcement, and audit trails. Govern an agent fleet without trusting it.
For production agents, spending uses a spend-capable prepaid credit key. Fund credits once, give each agent its own key and cap, and every inference request becomes one HTTP call. Direct x402 remains available for one-off calls. A normal API key only attributes requests and reads usage; it cannot spend.
Wallet-issued keys
Keys are minted self-service from the developers surface of the web app (the API tab) or via createApiKey in the SDK. Your wallet is the identity: one fixed EIP-712 signature is the management credential for creating, listing, re-budgeting, and revoking keys. The bearer key value is shown exactly once at creation; the router stores only a SHA-256 hash, so a leaked database leaks no keys. A spend-capable key rides requests as an x-credit-key header; a normal key rides as x-api-key for attribution.
The crucial asymmetry is explicit capability. Ordinary keys cannot spend. A spend-capable key can draw only from prepaid credits, and only within the credit balance and any per-key budget. Keep it server-side.
One-call prepaid spending
# create the key with can_spend_credits: true, then deposit once
omnious credits deposit 5
curl -N "$ROUTER_URL/v1/chat/completions" \
-H "x-credit-key: $OMNIOUS_CREDIT_KEY" \
-H "idempotency-key: agent-req-123" \
-H "content-type: application/json" \
-d '{"model":"auto","messages":[{"role":"user","content":"hello"}]}'The router reserves the worst case before dispatch, captures the metered actual at stream completion, and releases the unused remainder. The deposit is the only payment handshake; ordinary asks do not return a 402 or require wallet signing.
One wallet balance, separate keys
Prepaid funds belong to the wallet, not to an individual key. Every spend-capable key minted by that wallet draws from the same unified API balance, while retaining its own label, rolling cap, request history, and revocation state. This lets you create a separate key for each app, agent, or environment without fragmenting funds or stranding money when one key is revoked.
Per-key budgets and enforcement
A key may carry a budget: a USDC cap over a rolling daily, weekly, or monthly window. Once the key's attributed spend reaches the cap, requests carrying that key are refused with 403 key_budget_exhausted, and every keyed response reports x-key-budget-remainingso an agent can see its own runway. Requests without the key are untouched; the wallet can always spend. The gate contains a runaway agent that was given the key, not an adversary holding the wallet's signer. Enforcement happens at admission on billed actuals, so one in-flight request can overshoot by at most its own charge before the gate closes.
The audit trail
Every request carrying a key is attributed to it individually, and GET /v1/keys/:id/receiptspages through that key's history, newest first. Each row resolves through /v1/receipts/:id to the signed usage record, hashed into the hourly on-chain anchor. Per-agent accounting is not a report you assemble later; it is the settlement layer read back.
Client-side spend caps in the SDK
new OmniousClient({
spendCaps: { perQueryUsd: 0.05, perHourUsd: 1, perDayUsd: 5 },
});These bind before any money moves: the cap is checked against the 402's worst case before anything is signed, and a query that would blow a window is refused with an OmniousError of code spend_cap, no payment made. The router additionally enforces routing.max_price_usdc per request server-side, in-auction. Three layers, then: a client refusal before signing, a server refusal before quoting above your cap, and the key budget on attributed actuals.
Cumulative CLI loop caps
# The paid local loop defaults to a $1 cumulative allowance.
omnious ask "research this" --tools --web --max-spend 0.50
# Removing the cumulative client bound is deliberately explicit.
omnious chat --allow-unbounded-spend--max-spend covers every paid model/tool round and every child agent in that CLI process. It is separate from --max-price, which caps one auction. Model-supplied spawn_agent budgets are untrusted input: the CLI clamps them to the remaining parent/role allowance, and parallel children reserve disjoint shares before they start.
Why this matters for agent fleets
An autonomous agent with a wallet is an unbounded liability; an agent with a budgeted key is a bounded one. Give each agent its own key and cap, and it can spend only what its key allows, while every unit it does spend lands in a signed, anchored record you can audit per agent. You govern the fleet without trusting any member of it.
- router/src/accounts/apiKeys.ts key minting, budgets, and the attribution overlay
- router/src/money/billing.ts the billing formula budgets are enforced against