> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sunra.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 共享预算池

> 用一份临时的 USD 预算，为多把预算子密钥和外部操作统一授权。

预算池让多把预算子密钥共享同一份临时授权。每把密钥仍是独立的计费 ticket，拥有各自的调用和最终 receipt。创建预算池或签发密钥不会转移资金，也不会产生 organization 扣费。每个 LLM 请求仍照常执行 organization 计费和模型访问校验。

该公开功能初期仅对部分 organization 开放。如需启用请联系 Sunra。功能未启用时，预算池请求返回 `503 budget_pools_disabled`；不要改成不走预算池的请求重试。

## 金额与责任

预算池金额为十进制 USD 字符串，输入最多 8 位小数，输出固定为 8 位小数。JSON number、负数输入、指数表示法、null 和超出精度的值均无效。最大准入金额为 `90071992.54740991` USD。授权可以为零；credit、debit 和可选的子密钥上限必须为正数。

预算池暴露四个金额：

| 字段                  | 含义                           |
| ------------------- | ---------------------------- |
| `authorized_usd`（A） | 授予该预算池的累计授权。                 |
| `committed_usd`（C）  | 已落账的 LLM 已付支出，加上成功的外部 debit。 |
| `in_flight_usd`（F）  | 为财务结果尚未结算的调用所做的 reservation。 |
| `available_usd`（R）  | A 减 C 再减 F。                  |

授权**不是您 wallet 剩余余额的镜像**。credit 只会增加 A，绝不会减少 C。例如：授权 `10`、debit `3`，再 credit `1`，则 A 为 `11`，C 为 `3`，R 为 `8`。把 A 设成 wallet 剩余的 `8` 会把同一笔消耗扣减两次。

C 和 F 由 Sunra 维护，您无法覆盖它们。把 A 设为低于 C 加 F 会立即暂停预算池，并可能使 R 为负。此前已准入的工作仍按其实际记录的金额结算。增加授权不会自动恢复已暂停的预算池。

LLM 调用在下发之前预留其估算的最大售价。一次原子决策同时检查共享预算池和子密钥的可选上限。外部 debit 与这些 reservation 竞争同一份额度，并立即落账。使可用额度恰好为零的决策是允许的。被拒绝的 debit 不会部分消耗授权，也绝不会产生 LLM 记录或子密钥 receipt。

## 鉴权与管理

在 `https://api.sunra.ai/v1` 上使用有效的普通 API key 管理预算池和密钥。LLM 调用则在 `https://api-llm.sunra.ai/v1` 上使用签发的子密钥 secret。预算子密钥和用户 session 不能管理预算池。

`management` 不可修改，默认为 `org`：

* `org`：同一 organization 内有效的普通密钥都可以管理该预算池。
* `parent_only`：签发父密钥在保持 active 期间管理该预算池。如果该父密钥确定处于 inactive、已撤销、已停用或不存在，同组织内另一把有效的普通密钥可以恢复、读取、暂停和关闭它。父密钥查询失败不授予恢复权限。

只有原始的 active 父密钥可以 mint 新的入池子密钥。子密钥继承预算池的父密钥和管理模式。更换密钥时应当先结清旧预算池，再创建新的一代。其他 organization 的预算池 ID 返回 `404 budget_pool_not_found`。管理保护不是 organization 内部额外的保密边界；已有的组织级 completion 访问权限保持不变。

原始父密钥失效后，恢复密钥不能对该预算池执行 debit 或 resume。它仍可以确认历史结果、追加修复用的 credit、暂停和关闭。已确认的 operation 重放保留其原始结果。

## 创建与找回预算池

```http theme={null}
POST /v1/budget-pools
Authorization: Bearer <ordinary-api-key>
Content-Type: application/json

{
  "ref": "account-opaque-reference",
  "authorized_usd": "10.00000000",
  "expected_generation": "0",
  "management": "parent_only"
}
```

首次创建返回 `201` 和一个 `budget_pool` 资源。创建身份由 organization、`ref` 和 `expected_generation` 构成。以相同的归一化参数重复请求返回 `200`，并带回同一个预算池 ID 及其当前 snapshot。参数不同则返回 `409 budget_pool_conflict`。`ref` 必须为 1–256 个字符。

第一个预算池使用 `expected_generation: "0"`。第 `"1"` 代关闭后，使用 `expected_generation: "1"` 创建第 `"2"` 代。新的一代拥有新的预算池 ID。旧的创建重试始终找回它原本的预算池，不能开启新的一代。每个 organization 与 ref 的组合只能存在一个当前代。

```http theme={null}
GET /v1/budget-pools/{pool_id}
GET /v1/budget-pools?ref=account-opaque-reference&state=active&limit=100
```

列表接受可选的精确 `ref` 和 `state`（`active`、`paused` 或 `closed`）筛选条件、1–100 的 `limit`（默认 100）以及不透明的 `after` 游标。它按创建时间从新到旧返回 `{data, has_more, next_cursor}`，因此同一个 ref 的代号是递减的。先应用权限再分页。跟随游标翻页时，请保持筛选条件和调用凭证不变。跨分页的列表不是财务 snapshot。

预算池资源包含 `id`、`organization`、`ref`、`generation`、`version`、`state`、四个金额、`management`、`parent_api_key_id`、`created_at`、`last_admitted_at`、`pause_reason`、`close_requested_at`、`closed_at`、`closed_by_api_key_id` 和 `open_children`。未设置的生命周期时间戳和归因为 null。generation 和 version 是十进制**字符串**。version 在实际状态变化时递增，恢复后可能跳变；它不是 wallet 事件的水位线。

## 签发 turn 密钥

```http theme={null}
POST /v1/budget-keys
Authorization: Bearer <ordinary-api-key>
Content-Type: application/json

{
  "pool_id": "<pool-id>",
  "pool_generation": "1",
  "ref": "turn-opaque-reference",
  "expires_at": "2026-09-20T12:00:00Z",
  "models": ["openai/gpt-5.5"]
}
```

到期时间必须在未来，且不得晚于预算池创建后的第七天。`ref` 为必填，用于标识预算池内的一张逻辑 ticket。已有的 `models`、`metadata` 和 call tag 限制仍然适用。可以额外提供正数的 `cap_usd`，作为针对该子密钥的第二重上限；省略时该密钥只受共享预算池约束。未设置时子密钥资源不返回 `cap_usd`，并包含 `pool_id` 和 `pool_generation`。

首次成功 mint 返回 `201` 和一个 secret。相同的 pool/ref 和相同参数返回 `200`，带回同一个子密钥且**不含 secret**。参数不同则返回 `409 budget_key_mint_conflict`。系统只存储 secret 的哈希值。如果在保存 secret 之前丢失了首次 response，请找回子密钥 ID，关闭该 ticket 并结清其账目。不要复用它的 ref 创建另一张 ticket，也不要指望找回 secret。

```http theme={null}
GET /v1/budget-keys?pool_id={pool_id}&ref={turn_ref}
GET /v1/budget-keys/{child_id}
GET /v1/budget-keys/{child_id}/calls
POST /v1/budget-keys/{child_id}/close
```

预算池筛选条件可以不带 ref 使用。已有的子密钥列表分页和权限规则适用。关闭 A 只会停止 A 的新调用，B 仍可继续使用该预算池。A 已接受的调用仍需承担各自的费用。入池子密钥过期后停止接受新调用，但不会自动关闭它的财务 ticket。请显式关闭它并等待 `status: "closed"`，再把它的 receipt 视为最终结果。

入池子密钥的 close 返回 `200`，状态为 `finalizing` 或 `closed`；请检查资源状态。Receipt 仍保持 version 1、调用标识、封存的已付金额和完整分页。请取回全部调用分页，并将其已付金额总和与子密钥的最终支出核对。外部 operation 不计入这些 receipt。把该 receipt 记入您的 wallet 时，不得再次对预算池执行 debit。

每张 ticket 只有一个 secret。本次发布不支持延长到期时间，也不支持替换既有 ticket 的 secret。

## 变更授权或暂停准入

```http theme={null}
PATCH /v1/budget-pools/{pool_id}
If-Match: "<version>"
Content-Type: application/json

{
  "authorized_usd": "8.00000000",
  "generation": "1",
  "mutation_id": "authorization-event-42"
}
```

PATCH 以绝对值设置累计的 A。它要求在 `If-Match` 中带上先前 snapshot 的 version，这样过期的 wallet 更新就不会覆盖并发的 credit。version 过期返回 `409 budget_pool_version_conflict`。

```http theme={null}
POST /v1/budget-pools/{pool_id}/pause

{"generation":"1","mutation_id":"pause-42"}
```

Pause 立即停止新的 LLM 准入、debit 和 mint。它不需要 `If-Match`。读取、结算和 credit 仍然可用。

```http theme={null}
POST /v1/budget-pools/{pool_id}/resume
If-Match: "<version>"

{"generation":"1","mutation_id":"resume-42"}
```

Resume 要求账目状态已知、授权足以覆盖既有责任、没有关闭请求，且存续时间不足七天。它不会恢复已经花掉的授权。成功 resume 会重新开始一个空闲窗口。PATCH 和生命周期变更使用 1–128 个字符的 mutation ID：相同 ID 加相同参数会重放已存储的决策；参数变化则返回 `409 budget_mutation_conflict`。重放不会递增 version。

预算池在连续 24 小时没有成功的 LLM 准入或外部 debit 后暂停，创建满七天后也会暂停。读取、失败的准入、credit 和 PATCH 都不会延长它的活跃窗口。到期永远不会自动关闭预算池。超龄的预算池必须结清，并用新的一代替换。

## Debit、credit 与找回 operation

```http theme={null}
POST /v1/budget-pools/{pool_id}/operations
Content-Type: application/json

{
  "operation_id": "generation-order-42",
  "type": "debit",
  "amount_usd": "0.75000000",
  "generation": "1",
  "reason": "External generation authorization"
}
```

首次成功应用的 operation 返回 `201`；重放已应用的 operation 返回 `200`。请在发起请求之前先持久化您的 operation ID。在对应的 debit 确认 applied 之前，不要启动外部工作。该外部工作后续成功不会再次对本预算池执行 debit。

在确认充值或退款后，使用 `type: "credit"` 授予更多授权。credit 可以带上 `related_operation_id`，指向同一预算池中已应用的 debit。该关联仅用于审计；Sunra 不实现您的订单或退款状态机。`reason` 为可选，最多 256 个字符，对授权没有影响。operation ID 是 1–128 个字符的不透明字符串。

```http theme={null}
GET /v1/budget-pools/{pool_id}/operations/{operation_id}
GET /v1/budget-pools/{pool_id}/operations?limit=100&after={cursor}
```

operation 资源包含提交的参数、`status`（`pending`、`applied` 或 `rejected`）、时间戳、`result_version` 以及该决策对应的预算池 snapshot。applied 和 rejected 的结果不可变。即使后续 operation 改变了预算池，重放仍返回原始决策的 snapshot。金额在归一化后比较；type、generation、reason 和关联 ID 也参与冲突检测。

存储超时且决策未确定时可能返回 `202 pending`。请查询或重试**同一个 ID**。`404` 只表示没有找到持久化的意图，并不意味着可以换用新 ID。被拒绝的 debit 在后续 credit 之后仍然是 rejected；真正的新尝试需要一个新的、已持久化的 operation ID。同一 ID 下提交不同请求返回 `409 budget_operation_conflict`。

operation 列表按创建时间正序排列，使用绑定该预算池的不透明游标，分页大小同样为 1–100。请单独轮询 pending 的 ID，因为分页扫描不是状态变化的推送流。

## 关闭预算池

```http theme={null}
POST /v1/budget-pools/{pool_id}/close
Content-Type: application/json

{"generation":"1","mutation_id":"close-42","force":false}
```

不带 force 的 close：只要还有子密钥或 mint 成员处于 open 状态，就以 `409 budget_pool_children_open` 拒绝，此时不产生任何关闭副作用。`force: true` 会先停止预算池准入，再通过子密钥既有的 finalization 路径关闭它们，包括 mint response 仍在找回中的子密钥。

关闭完成返回 `200 closed`。如果已接受的责任或成员归属结果仍未确定，则返回 `202 paused` 并设置 `close_requested_at`。请重试同一个 close 操作。关闭请求不可逆：新的 mint、resume、PATCH、debit 和 credit 都会被阻止，但已接受的工作仍可继续结算。关闭不会退还 R，因为从未有资金转入该预算池。

已关闭的预算池永不重开。它们的 snapshot、operation、子密钥记录和 receipt 仍可供审计。旧 ID 和旧代不能修改新的一代。已确认的历史 operation 在关闭后仍可重放。

## 可入池模型与上限转换

入池子密钥使用显式的模型 allowlist：只有计费输出经验证不会超出所请求输出上限的模型才会准入。未列入的模型返回 `400 model_not_poolable`，其中包括五个推理型 Grok 模型 `grok-4.20`、`grok-4.3`、`grok-4.5`、`grok-4.6` 和 `grok-build-0.1`。普通密钥和不入池的子密钥保持原有行为。

以下八个模型需要 Chat Completions 转换：

* 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`。

对这些模型，Sunra 会把准入的上限作为 `max_completion_tokens` 发送，并从上游副本中移除 `max_tokens`。该上限包含推理输出。客户端 body、推理意图和 reservation 公式保持不变。这八个模型上的入池 Responses 和 Messages 请求返回 `400 invalid_input`，因为该转换未在这两种格式上验证过。

原生上限的 75 个模型 ID 如下（连同上述八个需转换的 ID，共 83 个可入池 chat 模型）：

* `claude-fable-5`
* `claude-fable-5-1`
* `claude-opus-4-7`
* `claude-opus-4-8`
* `claude-opus-5`
* `claude-sonnet-5`
* `gpt-5.4-pro`
* `claude-haiku-4-5-aws`
* `claude-opus-4-6-aws`
* `claude-opus-4-7-aws`
* `claude-opus-4-8-aws`
* `claude-sonnet-4-6-aws`
* `claude-haiku-4-5-reverse`
* `claude-opus-4-6-reverse`
* `claude-sonnet-4-6-reverse`
* `claude-haiku-4-5-relay`
* `claude-opus-4-6-relay`
* `claude-sonnet-4-6-relay`
* `qwen3.5-plus`
* `claude-haiku-4-5`
* `claude-haiku-4-5-openrouter`
* `claude-opus-4-6`
* `claude-opus-4-6-openrouter`
* `claude-sonnet-4-6`
* `claude-sonnet-4-6-openrouter`
* `deepseek-flash`
* `deepseek-v4-flash`
* `deepseek-v4-flash-vision-exp`
* `deepseek-v4-pro`
* `gemini-2.5-flash`
* `gemini-2.5-flash-gcp`
* `gemini-2.5-flash-openrouter`
* `gemini-2.5-pro`
* `gemini-2.5-pro-gcp`
* `gemini-2.5-pro-openrouter`
* `gemini-3.1-flash-lite-preview`
* `gemini-3.1-pro-preview`
* `gemini-3.1-pro-preview-gcp`
* `gemini-3.1-pro-preview-openrouter`
* `gemini-3.5-flash`
* `gemini-3.5-flash-gcp`
* `gemini-3.5-flash-lite`
* `gemini-3.6-flash`
* `gemini-3.7-flash`
* `gemini-3.8-flash`
* `glm-5`
* `glm-5-turbo`
* `glm-5.3`
* `glm-5.3-flash`
* `gpt-4o-mini`
* `gpt-5-mini`
* `gpt-5-nano`
* `gpt-5.3-codex`
* `gpt-5.4`
* `gpt-5.5`
* `gpt-5.6-luna`
* `gpt-5.6-sol`
* `gpt-5.6-terra`
* `gpt-6-astra`
* `gpt-oss-120b`
* `grok-4.20-non-reasoning`
* `kimi-k2.6`
* `kimi-k2.7-code`
* `kimi-k2.7-code-highspeed`
* `kimi-k3`
* `longcat-2.0`
* `minimax-m2-her`
* `minimax-m2.1`
* `minimax-m2.5`
* `muse-spark-1.1`
* `muse-spark-1.2`
* `o4-mini`
* `seed-2.0-lite`
* `seed-2.0-mini`
* `o3`

Claude 的 `-reverse` 和 `-relay` 别名准入，因为它们在 Anthropic 格式的上游上提供相同的 Claude 模型。旧的 `-aws` 别名可能退化到未 pin 的默认路由。只有遵循相同上限规则的合格上游路由才能执行；解析到不合格路由的请求会 fail closed，返回 `400 model_not_poolable`。

文本 `/v1/embeddings` 请求（包括 `gemini-embedding-2`）保持仅按 prompt 预留，不需要 completion allowlist 条目。子密钥仍禁止 native/media embeddings。`minimax-m2.7` 已下架。

这些是公共模型解析之后的精确 released 模型名，不是按家族或前缀授权。新增模型和别名必须先通过计费输出上限验收检查才能加入。`grok-4.20-non-reasoning` 是单独实测的原生条目；它不代表任何推理型 Grok 同族模型获得授权。

使用 Chat Completions 时，请提供正整数的 `max_tokens` 或 `max_completion_tokens`。两者同时提供且取值不同时无效。原生 Responses 和 Messages 在可用时保留已有的输出上限回退。入池子密钥对任何 `previous_response_id` 返回 `400 invalid_input`；隐藏的历史上下文无法计入准入估算。预算子密钥的[推理预算校验](/zh-Hans/platform/budget-keys#推理预算必须低于输出上限)在预算池 reservation 之前执行，使用的是叠加 reservation 开销之前的原始输出上限。已有的子密钥输出参数校验、单 completion 限制、模型限制和关闭 thinking 的规则继续生效。

## 错误

错误沿用已有的 v2 `error` envelope。请检查 `error.details.reason` 和 `retryable`。HTTP 402 使用粗粒度 code `INSUFFICIENT_CREDIT`，HTTP 503 使用 `INTERNAL_ERROR`，其他预算错误使用 `HTTP_EXCEPTION`。

| HTTP | Reason                                                                        | 含义                                               |
| ---- | ----------------------------------------------------------------------------- | ------------------------------------------------ |
| 400  | `invalid_input`                                                               | 字段、金额、到期时间、cursor、不支持的转换格式或隐藏的响应历史无效。未知请求字段会被拒绝。 |
| 400  | `max_tokens_required`                                                         | 缺少必需的输出上限。                                       |
| 400  | `model_not_poolable`                                                          | 解析出的模型不在预算池 allowlist 内。                         |
| 401  | `budget_key_expired`, `budget_key_revoked`                                    | 该子密钥无法接受新请求。                                     |
| 403  | `budget_key_route_forbidden`                                                  | 子密钥或 session 试图执行管理操作。                           |
| 403  | `budget_pool_management_forbidden`, `budget_key_management_forbidden`         | 调用方不在目标资源管理策略的授权范围内。                             |
| 404  | `budget_pool_not_found`, `budget_operation_not_found`, `budget_key_not_found` | 指定范围内的资源不存在，或属于其他 organization。                  |
| 402  | `budget_exhausted`                                                            | 预算池或可选的子密钥上限不足以覆盖本次请求。                           |
| 402  | `budget_pool_paused`                                                          | 准入已暂停；details 中包含 `pause_reason`。                |
| 409  | `budget_pool_conflict`, `budget_key_mint_conflict`                            | 创建或 mint 身份被以不同参数复用。                             |
| 409  | `budget_operation_conflict`, `budget_mutation_conflict`                       | operation ID 或 mutation ID 被以不同参数复用。             |
| 409  | `budget_pool_generation_mismatch`                                             | 写操作指向了错误的代。                                      |
| 409  | `budget_pool_version_conflict`                                                | `If-Match` 已过期。                                  |
| 409  | `budget_pool_children_open`                                                   | 关闭要求先关闭子密钥，或使用 `force: true`。                    |
| 409  | `budget_pool_closing`, `budget_pool_closed`                                   | 该预算池不再接受所请求的变更。                                  |
| 409  | `budget_pool_not_resumable`                                                   | 既有责任超过授权，或预算池存续过久。                               |
| 503  | `budget_store_unavailable`, `budget_state_unknown`                            | 依赖故障或恢复流程无法确定可靠的账目。请使用相同身份重试。                    |
| 503  | `budget_pricing_unavailable`                                                  | 必需的准入定价不可用。                                      |
| 503  | `budget_pools_disabled`                                                       | 该 organization 未启用新的预算池准入；自动重试或降级都不合适。           |

`budget_exhausted` 的 details 包含同一次原子决策的 `authorized_usd`、`committed_usd`、`in_flight_usd` 和 `estimate_usd`，以及 `scope: pool|child`。因子密钥上限被拒时还包含 `spent_usd`、`reserved_usd` 和 `cap_usd`。`blocking: in_flight` 表示未释放的 reservation 是阻塞原因，此时 `retryable` 为 true。`blocking: committed` 表示仅等待 reservation 释放无法让该请求通过。operation 被拒时包含 `operation_id`。

## 恢复与限制

预算池的执行是临时的：空闲超时为 24 小时，最长存续七天。它不是永久 wallet。您的应用负责协调 wallet 更新、换代、外部工作，以及哪些 wallet 事件已经计入新预算池的初始授权。Sunra 不实现资金桶、订阅、订单状态或 wallet 欠款补偿。wallet 侧的补偿不会自动授予新的预算池授权。

受支持模型的契约所针对的输出上限涵盖全部计费输出，包括已声明的推理部分。准入仍使用已有的保守 prompt 字节估算，并预留完整的输出上限。每个识别到的图片、音频、视频或文档 part 计入 8,192 个输入 token：存在专门的媒体价格时使用该价格，否则把该额度折入 prompt 价格。这些输入约定可能拒绝实际成本更低的请求。可用金额为很小的正数并不保证准入；请调低合适的输出上限，或等待在途工作结算。实际付费金额会如实保留，不会截断到授权额度。有意调低授权可能使此前已接受的责任高于新的授权额度。

严格覆盖实际成本需要对 provider 行为和输入/媒体计费口径做验收验证。`qwen3.5-plus` 的初始验收记录显示：请求上限为 64 时计费输出为 66 个 token。按 9 月 19 日的准入决定，该模型的预算池 reservation 按准入上限加两个 completion token 计价（64 预留 66）。发往上游的上限和客户端 body 保持不变。其他模型的 completion 开销为零。本地测试验证的是这一 reservation 行为，而不是新的线上 provider 实测。

Redis 状态丢失会停止新的支出。恢复流程会从持久化记录中重建已付支出、已应用的外部 operation 和未释放的 completion reservation，然后让预算池保持 paused，等待显式 resume。未结的账目条目即使在请求 lease 过期后仍保留其财务责任。只有已过期且没有对应 completion 记录的 reservation，才能作为未下发的孤儿回收。

如果在外部 operation 或生命周期决策仍等待持久化确认时 Redis 消失，其确切决策可能无从得知。在该证据能够确认之前，该 operation 保持 pending，预算池 fail closed；绝不会盲目地再次 debit 或 credit。超时或处于 finalizing 的 close 都不能证明费用为零。如果恢复长期处于 pending，请带上预算池 ID 和 operation ID 联系 Sunra。

客户端断连沿用已有的取消和持久化结算证据路径。Receipt 描述的是持久化的 Sunra 计费记录，不会臆造从未记录过的用量。新的 provider 路由需要重新做边界验证；已实测的模型矩阵并不保证 provider 未来的实现会保持今天的行为。

运维可以单独停用新的预算池准入，而不影响读取、结算、credit 修复和关闭。启用该功能之前，所有 auth 和网关实例都必须能够识别入池凭证。回滚必须先停止准入并结清既有的财务责任；同时必须保留用于读取留存 receipt 和 operation 的读接口。

部署开关为 `BUDGET_POOLS_ENABLED`（默认 `false`）、`BUDGET_POOLS_ORG_ALLOWLIST`（逗号分隔的 organization ID；为空表示不授予任何组织）和 `BUDGET_POOLS_ADMISSION_ENABLED`（设为 `false` 可停止新的准入；否则在前两个开关允许的前提下启用）。auth 和 apiv1 需要配置相同的开关。这些开关关闭时，已有的不入池凭证保持原有契约。
