503 budget_pools_disabled; do not retry it as an unpooled request.
Amounts and responsibility
Pool amounts are decimal USD strings with at most eight fractional digits on input and exactly eight on output. JSON numbers, negative inputs, exponent notation, null, and extra precision are invalid. The maximum admission amount is90071992.54740991 USD. Authorization may be zero; credits, debits, and an optional child cap must be positive.
A pool exposes four amounts:
Authorization is not a mirror of your remaining wallet balance. A credit increases A; it never reduces C. For example, authorize
10, debit 3, then credit 1: A is 11, C is 3, and R is 8. Setting A to the wallet’s remaining 8 would deduct the same consumption twice.
Sunra maintains C and F. You cannot overwrite them. Setting A below C plus F pauses the pool immediately and may make R negative. Previously admitted work still settles at its actual recorded amount. Adding authorization does not resume a paused pool automatically.
An LLM call reserves its estimated maximum sell cost before dispatch. One atomic decision checks both the shared pool and the child’s optional cap. External debits compete with those same reservations and commit immediately. A decision that leaves exactly zero available is allowed. Rejected debits do not partially consume authorization and never produce LLM rows or child receipts.
Authentication and management
Manage pools and keys onhttps://api.sunra.ai/v1 with an active ordinary API key. Use the issued child secret on https://api-llm.sunra.ai/v1 for LLM calls. Budget children and user sessions cannot manage pools.
management is immutable and defaults to org:
org: active ordinary keys in the same organization can manage the pool.parent_only: the issuing parent manages the pool while it remains active. If that parent is definitively inactive, revoked, suspended, or missing, another active ordinary key in the same organization can recover, read, pause, and close it. A failed parent lookup does not grant recovery access.
404 budget_pool_not_found. Management protection is not a separate confidentiality boundary within an organization; existing organization-wide completion access is unchanged.
After the original parent becomes inactive, a recovery key cannot debit or resume that pool. It can still confirm historical results, add a repair credit, pause, and close. Confirmed operation replays preserve their original results.
Create and recover a pool
201 and a budget_pool resource. The creation identity is organization, ref, and expected_generation. Repeating the same normalized parameters returns 200 with the same pool ID and its current snapshot. Different parameters return 409 budget_pool_conflict. ref must contain 1–256 characters.
Use expected_generation: "0" for the first pool. After generation "1" is closed, use expected_generation: "1" to create generation "2". A new generation has a new pool ID. An old create retry always finds its original pool; it cannot open a new generation. Only one current generation can exist per organization and ref.
ref and state (active, paused, or closed) filters, limit from 1 through 100 (default 100), and an opaque after cursor. It returns {data, has_more, next_cursor} in newest-creation order, which gives descending generations for a ref. Authorization is applied before pagination. Keep filters and the calling credential unchanged when following a cursor. Lists are not financial snapshots across pages.
A pool resource includes id, organization, ref, generation, version, state, the four amounts, management, parent_api_key_id, created_at, last_admitted_at, pause_reason, close_requested_at, closed_at, closed_by_api_key_id, and open_children. Unset lifecycle timestamps and attribution are null. Generation and version are decimal strings. Versions increase on actual state changes and can jump after recovery; they are not wallet event watermarks.
Issue a turn key
ref is required and identifies one logical ticket within the pool. Existing models, metadata, and call-tag restrictions still apply. You may add a positive cap_usd as a second, child-specific bound; omitting it leaves the key subject to the shared pool alone. The child resource omits cap_usd when none was set and includes pool_id and pool_generation.
The first successful mint returns 201 with one secret. The same pool/ref and parameters return 200 with the same child and no secret. Different parameters return 409 budget_key_mint_conflict. Only the secret hash is stored. If the initial response was lost before you saved the secret, recover the child ID, close that ticket, and finish its accounting. Do not reuse its ref to create another ticket or expect to recover the secret.
status: "closed" before treating its receipt as final.
Pooled child close returns 200 with finalizing or closed; inspect the resource status. Receipts retain version 1, call identifiers, sealed paid amounts, and full pagination. Fetch all call pages and verify their paid sum against the child’s final spend. External operations are excluded from these receipts. Recording that receipt in your wallet must not debit the pool again.
Each ticket has one secret. This release does not support extending expiry or replacing the secret on an existing ticket.
Change authorization or pause admission
If-Match so a stale wallet update cannot overwrite a concurrent credit. A stale version returns 409 budget_pool_version_conflict.
If-Match. Reads, settlement, and credits remain available.
409 budget_mutation_conflict. Replays do not increment the version.
A pool pauses after 24 hours without a successful LLM admission or external debit, or seven days after creation. Reads, failed admissions, credits, and PATCH do not extend its activity window. Expiry never automatically closes the pool. An over-age pool must be finished and replaced with a new generation.
Debit, credit, and recover an operation
201; an applied replay returns 200. Persist your operation ID before making the request. Do not start external work until its debit is confirmed applied. The external work’s later success does not debit this pool again.
Use type: "credit" to grant more authorization after a confirmed top-up or refund. A credit may include related_operation_id pointing to an applied debit in the same pool. This link is for audit; Sunra does not implement your order or refund state machine. reason is optional, at most 256 characters, and has no authorization effect. Operation IDs are opaque strings of 1–128 characters.
status (pending, applied, or rejected), timestamps, result_version, and the decision’s pool snapshot. Applied and rejected results are immutable. A replay returns the original decision snapshot, even if later operations changed the pool. Amounts compare after normalization; type, generation, reason, and related ID also participate in conflict detection.
A store timeout with an unresolved decision can return 202 pending. Query or retry the same ID. A 404 only means no durable intent was found; it does not authorize choosing a new ID. A rejected debit remains rejected after later credit; a genuinely new attempt needs a new persisted operation ID. A changed request under an existing ID returns 409 budget_operation_conflict.
Operation lists use ascending creation order, an opaque pool-bound cursor, and the same 1–100 page size. Poll pending IDs separately because a paginated scan is not a stream of status changes.
Close a pool
409 budget_pool_children_open if any child or mint membership is still open. It has no closing side effect in that case. force: true first stops pool admission, then closes children through their existing finalization path, including children whose mint response is still being recovered.
A completed close returns 200 closed. If accepted responsibilities or membership outcomes remain unresolved, it returns 202 paused with close_requested_at set. Retry the same close operation. A close request is irreversible: new mints, resume, PATCH, debits, and credits are blocked, but accepted work can settle. Closure does not refund R because no funds were transferred into the pool.
Closed pools never reopen. Their snapshots, operations, child records, and receipts remain available for audit. Old IDs and generations cannot mutate a new generation. Confirmed historical operations still replay after closure.
Poolable models and ceiling translation
Pooled children use an explicit model allowlist: only models whose billed output is verified to stay within the requested output ceiling are admitted. Unlisted models return400 model_not_poolable, including the five reasoning Grok models grok-4.20, grok-4.3, grok-4.5, grok-4.6, and grok-build-0.1. Ordinary keys and unpooled children keep their existing behavior.
The following eight models require Chat Completions translation:
- Ark:
doubao-seed-2.1-pro,doubao-seed-2.1-turbo,doubao-seed-evolving,glm-5.2. - DashScope:
qwen3.7-flash,qwen3.7-plus,qwen3.7-max,qwen3.8-max.
max_completion_tokens and removes max_tokens from the upstream copy. This ceiling includes reasoning output. The client body, reasoning intent, and reservation formula are unchanged. Pooled Responses and Messages requests on these eight models return 400 invalid_input because the translation is not verified for those formats.
The 75 native-ceiling model IDs are (83 poolable chat models including the eight translated IDs):
claude-fable-5claude-fable-5-1claude-opus-4-7claude-opus-4-8claude-opus-5claude-sonnet-5gpt-5.4-proclaude-haiku-4-5-awsclaude-opus-4-6-awsclaude-opus-4-7-awsclaude-opus-4-8-awsclaude-sonnet-4-6-awsclaude-haiku-4-5-reverseclaude-opus-4-6-reverseclaude-sonnet-4-6-reverseclaude-haiku-4-5-relayclaude-opus-4-6-relayclaude-sonnet-4-6-relayqwen3.5-plusclaude-haiku-4-5claude-haiku-4-5-openrouterclaude-opus-4-6claude-opus-4-6-openrouterclaude-sonnet-4-6claude-sonnet-4-6-openrouterdeepseek-flashdeepseek-v4-flashdeepseek-v4-flash-vision-expdeepseek-v4-progemini-2.5-flashgemini-2.5-flash-gcpgemini-2.5-flash-openroutergemini-2.5-progemini-2.5-pro-gcpgemini-2.5-pro-openroutergemini-3.1-flash-lite-previewgemini-3.1-pro-previewgemini-3.1-pro-preview-gcpgemini-3.1-pro-preview-openroutergemini-3.5-flashgemini-3.5-flash-gcpgemini-3.5-flash-litegemini-3.6-flashgemini-3.7-flashgemini-3.8-flashglm-5glm-5-turboglm-5.3glm-5.3-flashgpt-4o-minigpt-5-minigpt-5-nanogpt-5.3-codexgpt-5.4gpt-5.5gpt-5.6-lunagpt-5.6-solgpt-5.6-terragpt-6-astragpt-oss-120bgrok-4.20-non-reasoningkimi-k2.6kimi-k2.7-codekimi-k2.7-code-highspeedkimi-k3longcat-2.0minimax-m2-herminimax-m2.1minimax-m2.5muse-spark-1.1muse-spark-1.2o4-miniseed-2.0-liteseed-2.0-minio3
-reverse and -relay aliases are admitted because they serve the same Claude models on Anthropic-format upstreams. Legacy -aws aliases can degrade to the unpinned default routes. Only upstream routes qualified under the same ceiling rule may execute; a request that resolves to an unqualified route fails closed with 400 model_not_poolable.
Text /v1/embeddings requests, including gemini-embedding-2, retain prompt-only reservation and need no completion allowlist entry. Native/media embeddings remain forbidden for child keys. minimax-m2.7 is delisted.
These are exact released model names after public-model resolution, not family or prefix permissions. New models and aliases require a billed-output ceiling acceptance check before being added. grok-4.20-non-reasoning is a separate measured native entry; it does not authorize any reasoning Grok sibling.
For Chat Completions, provide a positive integer max_tokens or max_completion_tokens. Supplying both with different values is invalid. Native Responses and Messages retain the existing output-limit fallback where available. Pooled children reject any previous_response_id with 400 invalid_input; hidden prior context cannot be included in the admission estimate. The reasoning-budget validation for budget keys runs before pool reservation, using the original output ceiling before any reservation overhead. Existing child output-parameter validation, single-completion restriction, model restrictions, and thinking-off rules remain in force.
Errors
Errors use the existing v2error envelope. Inspect error.details.reason and retryable. HTTP 402 uses coarse code INSUFFICIENT_CREDIT, HTTP 503 uses INTERNAL_ERROR, and the other budget errors use HTTP_EXCEPTION.
budget_exhausted details include the same atomic decision’s authorized_usd, committed_usd, in_flight_usd, and estimate_usd, plus scope: pool|child. A child-bound refusal also includes spent_usd, reserved_usd, and cap_usd. blocking: in_flight means outstanding reservations are the blocker and retryable is true. blocking: committed means waiting for reservations alone will not make the request fit. Operation refusals include operation_id.
Recovery and limits
Pool execution is temporary: idle timeout is 24 hours and maximum age is seven days. It is not a permanent wallet. Your application coordinates wallet updates, generation changes, external work, and which wallet events are already included in a new pool’s initial authorization. Sunra does not implement funding buckets, subscriptions, order state, or wallet shortfall compensation. Wallet compensation does not automatically grant new pool authorization. The supported-model contract targets an output ceiling that includes all billed output, including reasoning where declared. Admission still uses the existing conservative prompt-byte estimate and reserves the full output ceiling. Each recognized image, audio, video, or document part contributes 8,192 input tokens: a dedicated media price is used when present, otherwise the allowance is folded into prompt pricing. These input conventions can reject requests that would have cost less. A small positive available amount does not guarantee admission; lower a suitable output ceiling or wait for in-flight work to settle. Actual paid amounts are retained rather than truncated to authorization. A deliberate downward authorization adjustment can leave previously accepted responsibility above the new authorization. Strict actual-cost coverage requires acceptance verification of provider behavior and input/media accounting. The initial acceptance record forqwen3.5-plus contains 66 billed output tokens at a requested ceiling of 64. By the September 19 admission decision, pool reservations for this model price the admitted ceiling plus two completion tokens (64 reserves 66). The ceiling sent upstream and the client body remain unchanged. Other models have zero completion overhead. Local tests verify this reservation behavior, not a new live-provider measurement.
Redis state loss stops new spending. Recovery reconstructs paid spend, applied external operations, and open completion reservations from durable records, then leaves the pool paused for an explicit resume. Open journal entries retain their financial responsibility even after the request lease expires. Only an expired reservation with no completion row can be reclaimed as an undispatched orphan.
If Redis disappears while an external operation or lifecycle decision is still awaiting durable confirmation, its exact decision may be unknowable. The operation remains pending and the pool fails closed until that evidence can be resolved; it is never blindly debited or credited again. A timeout or a finalizing close is not proof of zero cost. Contact Sunra with the pool and operation IDs if recovery remains pending.
Client disconnects use the existing cancellation and persisted settlement-evidence path. Receipts describe durable Sunra billing records; they do not invent usage that was never recorded. New provider routes require renewed boundary verification, and the measured model matrix is not a guarantee that future provider implementations will preserve today’s behavior.
Operators can disable new pool admission independently of reads, settlement, credit repair, and close. All auth and gateway instances must understand pooled credentials before enabling the feature. Rollback must first stop admission and finish existing financial responsibility; it must preserve readers for retained receipts and operations.
The deployment gates are BUDGET_POOLS_ENABLED (default false), BUDGET_POOLS_ORG_ALLOWLIST (comma-separated organization IDs; empty grants none), and BUDGET_POOLS_ADMISSION_ENABLED (set to false to stop new admissions; otherwise enabled subject to the first two gates). Configure the same gates on auth and apiv1. Existing unpooled credentials retain their existing contract when these gates are off.