① 开通与层级(账户 → 子账号 → 门店 → 员工 → Key)

蓝色 = 商户可自助(INTEGRATION Key,限自有账户);橙色 = 仅平台(PLATFORM Key);绿色 = CALL Key 调用侧

1. 开户
POST /internal/v1/accounts
{name, remark} → accountId
provisionStatus: PENDING → READY(绑定网关令牌)
▶
2. 充值(必须有余额)
POST /internal/v1/gifts
{accountId, clientId, points, exclusiveWriterConfirmed:true, exclusivityEvidenceRef}
账户未分配余额 += points×10000 子单位
仅 phase=CONFIRMED 算完成
▶
3. 建子账号
POST /internal/v1/accounts/{accountId}/sub-accounts
{name, remark?} → subAccountId
新建桶余额 = 0
▶
4. 划拨余额(分配即冻结)
POST .../sub-accounts/{subAccountId}/transfers
{points, direction:"OUT"}(OUT=账户→子)
账户桶条件扣减 + 子桶条件累加,同事务
任一侧不足 → 409 整单拒绝,无部分转账
▶
5. 建门店 / 员工 / 额度
POST .../scopes
POST .../scopes/{scopeId}/quota
门店:{scopeType:"STORE", scopeKey, subAccountId?}(省略=直挂账户)
员工:{scopeType:"EMPLOYEE", scopeKey, parentScopeKey:门店}
额度:{storeScopeId, monthlyQuotaPoints|null}(0=零额度,null=显式不限)

② 签发 Key 并激活

6. 签发
POST .../accounts/{accountId}/credentials
{subAccountId? | scopeId?}(二选一,互斥)
→ credentialId + secret(仅此一次),status=PENDING(24h 内有效)
▶
7. 激活
POST /internal/v1/credentials/{credentialId}/activate
{replacesCredentialId?} → status=ACTIVE
轮换:旧 Key 宽限期 min(原值, now+24h)
▶
Key 四类(决定主体与扣费桶)
账户级 | 子账号级 | 门店级 | 员工级
账户级 = 平台/测试专用(商户自签 403 ACCOUNT_KEY_PLATFORM_ONLY)
商户路径:门店 Key(无子账号时直挂账户)或员工 Key(受月度额度约束)
▶
调用时不需要传主体
主体 = Key 绑定值;分账维度不可由请求参数覆盖
CALL Key 传 accountIds/子账号/门店维度 → 403 PERMISSION_DENIED

③ 一次模型调用的服务端内部流程(含 SSE 分支)

CALL Key请求
▶
POST /v1/chat/completions(OpenAI 兼容,可带 stream:true)或 POST /api/v1/chat/completions(原生)
Authorization: Bearer <CALL Key> X-Request-Id: ≥8 字符(/v1 可省,服务端生成)
1. 鉴权
▼
凭证存在 → 摘要恒定时间比较 → 状态/有效期 → client/account 授权
401 CREDENTIAL_PENDING/EXPIRED/REVOKED、AUTHENTICATION_FAILED
403 ACCOUNT_ACCESS_REVOKED(撤销优先于重放)
通过 → 下一步
2. 幂等命中?
▼
按 (accountId, clientId, requestId) 查原记录 + 比对指纹
同号同参 → 直接返回历史结果(200/202),不派发、不重复扣费
同号异参 → 409 FINGERPRINT_MISMATCH
无记录 → 新请求
3. 准入检查(只读比对)
▼
账户/子账号/门店/员工状态 + 余额 + 员工当月额度
403 ACCOUNT_DISABLED / SUB_ACCOUNT_DISABLED / SCOPE_DISABLED
409 ACCOUNT_BLOCKED(reason=INSUFFICIENT_BALANCE,可带 subAccountId)
429 EMPLOYEE_QUOTA_EXCEEDED(带 quotaMonth/limitPointUnits/usedPointUnits)
无 429 命中即放行
4. 登记 + 派发
▼
落 call 行(主体快照 subAccountId/scopeId/storeScopeId + quotaMonth = 登记月 Asia/Shanghai) → 调上游(NewAPI/直连),调用预算 150 秒
5. 结果与结算
▼
以网关日志 quota 为权威计量:consumedPointUnits = max(quota,0) × 146
已结算 → 200 / OpenAI 正常响应:billingStatus=SUCCESS, settled=true,同事务写 consumption + point_record(扣快照桶)+ 余额
日志延迟 → SETTLE_PENDING → 补偿重试 1/5/15/60 分钟 ×24(≈21.35h)
结果不明 → 202 + retryHint=QUERY_ORIGINAL_REQUEST,用原 requestId 查 GET /api/v1/calls/{requestId}
SSEstream:true 分支
▼
首个事件前失败 → 仍返回 OpenAI JSON 错误(400/401/403/409/429/5xx)
已发响应头后失败 → data: {"error": …} 并结束,不发 [DONE],含 X-Should-Retry:false
正常 → 增量 delta(role/content/reasoning_content)→ finish_reason → data: [DONE]
[DONE] 只表示生成成功,不代表已结算;账务与最终费用仍查 GET /api/v1/calls/{requestId}。同号重放且原执行仍 PENDING/PROCESSING → JSON 409(reason=ORIGINAL_REQUEST_PENDING),绝不重新派发。

④ 是否满足 OpenAI 协议(按代码实测口径)

类别字段 / 行为结果
支持(严格校验) model(可省)、messages(system/user/assistant,developer 映射为 system;content 为字符串 1–1MiB,1–50 条)、stream(严格布尔)、stream_options.include_usage、temperature 0–2、top_p 0–1、max_tokens/max_completion_tokens 1–1e7、presence_penalty/frequency_penalty −2–2、seed 0–2³¹−1、stop(≤4 条,每条 1–64 字符) ✔ 透传到上游
忽略(不报错) n、tools/tool_choice、response_format、logit_bias、user、metadata 等一切白名单外字段 ⚠ 静默丢弃,不会生效,也不会 400
拒绝(400) messages[].content 数组形式(多模态)、stream 非布尔、stream_options 中出现 include_usage 以外的键、非流式请求带非 null 的 stream_options、stop 超 4 条或单条超 64 字符 ✘ invalid_request_error
响应形态 id=chatcmpl-<requestId>、object=chat.completion/chat.completion.chunk、created、model、choices[{index, message|delta, finish_reason}]、usage{prompt_tokens, completion_tokens, total_tokens},另加扩展块 mei1{requestId, callId, billingStatus, consumedPoints}(官方 SDK 会忽略未知字段) ✔ SDK 可直接解析
错误形态 {error:{message, type, code, param}};余额不足与员工超额 → 429 insufficient_quota;限流 → 429 rate_limit_error;执行未出正文 → 500/503 api_error;/v1/* 的入参校验失败也走 OpenAI 形态 ✔ 与 OpenAI 同构
必须知道的差异 1. 单模型白名单:model 必须等于服务端配置值,否则 400(不是「模型不存在」而是参数非法)
2. 无幂等头的重试会重复计费:/v1 省略 X-Request-Id 时服务端每次生成新号,请自行带业务请求号并关闭自动重试
3. 仅字符串 content:多模态、工具调用、JSON mode 均不可用
4. 计费不看 usage:扣费以网关日志 quota 为准,usage 仅为展示;include_usage 的尾部用量事件可能缺失
5. 429 insufficient_quota 有两种含义(账户/子账号余额不足 vs 员工月度额度超限),区分看 error.mei1.reason
6. 错误 code 为自有错误码(如 ACCOUNT_BLOCKED),message 可能带 : reason 后缀
⚠ 兼容但非等价
入口 POST /v1/chat/completions(非流式 + SSE)、GET /v1/models(单模型,仅供探活/枚举) 迁移只需改 base_url 与 Key
依据:app/account_openai.py(请求模型、响应与错误构造)、app/account_main.py(路由与异常处理)、docs/external-api-reference.md §3.2 与 §7.2。未做真实网关联调的部分(上游 SSE 契约、目标环境反代)未包含在内。
展开 SSE 时序
导出 Postman collection
导出单文件 HTML