Sessions & agent loops
One authorization funds a whole loop. The budget draws down per turn and the remainder refunds at close.
An agent loop is expensive in a specific way: chat models are stateless, so every turn resends the whole growing conversation as input. Provider prefix caches make those re-reads cheap, but only at the provider that served the previous turn. A market that ignored this would either migrate your loop to whoever quotes lowest while you pay full re-prefill every turn, or pin you to one provider who could then reprice at will. Sessions give you the third option: sticky by default, contested every turn, and never repriced mid-loop.
Prepay once, or draw from prepaid credits
A per-turn 402, sign, settle round trip is pure overhead inside a loop that may run 200 turns. Budgeted sessions use direct x402: clients sign one session authorization for the cap (say $2), then every turn draws down against that budget. Hosted services can instead open a sticky per-turn session with a prepaid credit key: every turn atomically reserves its worst case, captures metered usage locally, and releases the rest without an x402 or chain settlement. In both paths, only metered usage is charged.
The locked tariff
Turn 1 is a normal symmetric auction, and the winner's second-score cleared rates, including a cheaper rate for cached input tokens, lock for the session's TTL. Every later turn re-runs the auction on effective cost: the incumbent is scored at its locked tariff with your prefix at the cached rate, every challenger at spot paying full re-prefill in both price and latency. If the incumbent wins, it bills exactly its locked rates. A continuation turn is a contestability check, never a repricing event: nothing the incumbent bids mid-session changes what it is paid, so holding your cache buys it no leverage over you. If a challenger is cheap enough to win despite the re-prefill, the session migrates and its cleared rates become the new lock.
How the router knows two requests are one session
Explicitly: send routing.session_id (the SDK and web app set one per conversation automatically); the first request with a new id opens the session and the response confirms the locked rates in x-session-id and x-session-ratesheaders. Implicitly: the router keeps a salted rolling hash chain over each session's messages, so a request that extends a known prefix is recognized as the next turn even from agent frameworks you cannot modify. The hash chain, not your content, is what gets stored, and it expires with the session.
Receipts stay per-turn
A session is an analytics grouping plus a budget, not a new settlement primitive. Every turn still produces its own signed usage record, now carrying the session_id, so reconciliation and epoch anchoring are untouched and you can audit any single turn the same way you audit a standalone request. On top of the per-turn records, GET /v1/analytics/sessions/:id publishes the per-session transaction-cost analysis: the turn-by-turn tape and the cache discount you captured against a cache-blind counterfactual that re-auctioned every turn at spot. The Activity blotter in the web app renders exactly this: cache captured versus cache-blind, per session.
In the SDK it is three lines: omnious.session({ budgetUsd: 1 }), then ask per turn, then close() to trigger the refund. With creditKey, omit budgetUsdto keep the same sticky routing while capturing each turn from prepaid credits. Direct one-off clients also honor the router's machine-readablesession_recommended hint: they request the reusable session quote before asking the wallet to sign. The provider-side view of the same mechanism is on Winning sessions, and the full mechanism analysis on Sessions: auctions for agent loops.
- router/src/api/sessions.ts session registry, locked tariffs, budget draw-down