2026-09-04
技能生成、上游可用性、超时点名的模型若供应商未配置,返回 503 而不是偷换
破坏性改了什么:某家供应商的 Key 未配置或被临时停用时,此前会把占位内容当作回答返回。
现在:你指定具体模型(如 glm-4-flash)而它不可用 →
503 / model_unavailable,绝不换成别的模型扣你的钱;
你用模式(tokin-balanced 等)或 tokin-auto → 自动切换到可用的供应商,响应头照常标明实际模型。
你要做什么:指定具体模型的接入方请处理 503,或改用 tokin-auto。
供应商临时故障的转移行为不变(仍带 X-Tokin-Failover)。
上游调用有了超时
修复此前上游卡住时请求会永久挂起。现在非流式 120 秒、流式首字节 60 秒未响应即转移到下一家供应商, 超时的供应商会被自动避开几分钟。
技能:从文档生成、多步执行、按模型调用工具
新增POST /v1/skills/draft 上传 .docx / .pdf / .pptx / .md / .txt / .html,
异步生成一份可执行的多步技能定义(含描述、步骤、角色、规则、阶段),并附对照报告:文档里有几问、提炼了几步、忽略了哪些章节。
图片型 PDF 会用视觉模型逐页转写。同一份文档第二次不重跑;regenerate: true 强制重新生成。
POST /v1/skills/:id/run 异步执行,GET /v1/skills/runs/:runId 轮询进度;
评审意见可通过 /note 注入下一步。
对话接口新增 enable_tools: true:模型可自行调用联网搜索、企业信息查询,服务端完成循环并逐次计费;
GET /v1/tools 给出工具声明。
技能:对话也能变技能,跑完可以用一句话改
新增材料形态不再受限:除了 Word / PDF / PPT / Markdown,直接把一段对话记录交给
POST /v1/skills/draft 也能生成技能。系统先认这份材料在用哪套写法
(问题N、##、Step N、第N步、1.1、【…】),认不出就按语义切;
编号被写在句子中间(「对,第三步做财务判断」)会被自动找回。
生成报告会给改进建议:告诉你识别到了什么结构、哪几步没认出来,以及在文档里怎么改下次会更准。
用一句话改技能:POST /v1/skills/:id/refine
接受「第三步要给出三个候选方案」这样的话,只改被点到的地方并逐条告诉你动了什么;
带上 runId 时会参考上一次运行的实际产出。改错了用
/undo 退回。
技能商店:提交上架、平台定价、按次收费
新增作者 POST /v1/skills/:id/submit 提交上架;平台审核时定价(按次,可为 0)与归类;
GET /v1/skills/store 列出已上架技能(名称、简介、角色、定价,不含定义正文)。
付费技能运行成功后才收使用费,失败一分不收;费用与该次运行的模型用量共用一个 runId,
账单标签 skill.fee.<id>。作者运行自己的技能不收费。
2026-09-02
代码审计修复计价修正:三处会多收或少收的缺陷
计费企业查询按关键词调用时的计价:请求里带 server
但走关键词路径时,实际执行的是普通企业查询(¥0.101),却按未知工具的兜底档收了 ¥0.505 —— 5 倍超收。
现在一律按真正执行的那个服务计价,与 GET /v1/company/tools 公示价一致。
缓存命中价被抹掉:后台每改一次模型价,缓存输入价会被清空并回落到未命中价 (如 0.64 → 3.2)。命中缓存的 token 被按 5 倍收。已修正为改价时保留原缓存价。
分档模型的后台改价此前不生效:页面显示新价、扣费仍按旧价。现在分档按新价同比例缩放。
要不要做什么:不需要。如果你在 8 月使用过企业查询的
server 参数并对账单有疑问,把请求时间发到反馈通道,我们逐笔核。
实时转写:开头几百毫秒不再丢字幕,也不再计费
计费连上之后到上游识别任务真正就绪之间有几百毫秒。此前这段音频被直接丢弃、时长却照算 —— 花了钱,开头那句话却没有字幕。现在这段音频会先缓存、任务就绪后补发; 上游确实起不来时则不计费。
另外:被代付的用户开实时会话不会再在 60 秒后被误判为「余额不足」而中断 (此前只看自己钱包,没算机构额度)。
子密钥:每分钟调用上限现在真的生效
破坏性控制台一直能设 rpmLimit、也一直展示它,但这个限制从未被检查过。
现在超限返回 429 / key_rate_limit。
你要做什么:如果你给某个子密钥设过每分钟上限、而实际用量高于它, 它现在会开始被限。请到控制台确认这个数值是你真正想要的。
同时:告警地址填了内网地址不再静默丢弃(此前保存成功但永远收不到告警),改为明确报错;
而 fcm.googleapis.com 这类公网域名此前被误判成内网,现已修正。
应用标识接口:多字节 app_secret 不再触发 500
修复凭据比对按字符数而非字节数取长度,传入中文等多字节内容会让
/v1/apps/* 五个接口直接 500(而不是干净地返回 401)。已修正。
轮换 secret 的宽限期也修了:此前第二次轮换会把仍在使用的上一把 secret 顶掉, 恰好造成宽限期本要避免的断档。
上架审核收紧:只有 https 的网页应用自动通过,
安装包类一律转人工审核。
2026-08-22
安全加固与计费修正OAuth 回调地址:绑定一次后即锁定
破坏性为什么改:此前「首次使用自动绑定」对任何登录用户开放。攻击者拿到你的
app_id(它公开出现在授权页 URL 里),自己授权一次把回调绑成
https://evil.com,再把链接发给你的用户——用户看到的是你的应用名,
同意之后授权码却落到攻击者手里。PKCE 挡不住这个,因为 challenge 是攻击者自己的。
现在的规则:本机地址(127.0.0.1 / localhost)始终放行;
其余地址第一次授权时绑定,之后只认它。能完成这次绑定的只有应用持有者——
控制台创建的应用认「创建它的账号」,匿名注册的应用认 app_secret。
你要做什么:确认 redirect_uri 是固定常量,
不要从 request.base_url 推导——
同一台服务器上 http、https、不同端口各算一个地址,绑上第一个之后其余全部会被拒。
换地址请见下方「解绑」。
免注册获取正式 app_id
新增此前拿 app_ 要先注册 Tokin 账号;不想注册的只能用
dev_ 标识,而它不保证唯一——别人用同一个字符串就可能抢先绑走你的回调。现在两者兼得:
curl -X POST https://api.tokincloud.com/v1/apps/register \
-H "Content-Type: application/json" -H "Idempotency-Key: 你生成的唯一串" -d '{"name":"你的应用名"}'
# → 201 { "app_id": "app_xxxxxxxxxxxxxxxx", "app_secret": "appsecret_..." }
# 注意是 201 不是 200。带同一个 Idempotency-Key 重放 → 200 + idempotent_replay:true,
# 返回同一个 app。这个头**可选**——不带也能注册(201);
# 但本接口发出去的标识收不回,做重试的话强烈建议带上。
# name 必填;请求体不合法一律 400,不会默默发号。
免鉴权、不用账号、不用实名。
app_secret 只返回这一次,是改回调、以及将来把应用认领进账号
(POST /v1/apps/claim)的唯一凭据;
app_id 是公开标识,无需保密。
绑定回调是部署时的一次性动作,在你的服务器上完成——
授权页是浏览器在调 /oauth/code,
而 app_secret 绝不能出现在浏览器里:
# 绑定回调:部署时在**你的服务器上**调一次(app_secret 不能进浏览器)
POST /v1/apps/bind-redirect { "app_id":"...", "app_secret":"...", "redirect_uri":"https://..." }
# 换域名 / 换端口:先解绑,再重新 bind-redirect
POST /v1/apps/unbind-redirect { "app_id":"...", "app_secret":"..." }
# secret 泄露了?用旧 secret 直接轮换,不需要账号:
POST /v1/apps/rotate-secret { "app_id":"...", "app_secret":"..." }
# 改名?持 app_secret 随时可改(有人用过后会向用户提示"曾改名"):
POST /v1/apps/rename { "app_id":"...", "app_secret":"...", "name":"新名字" }
# 领错了 / 探针误建?未认领、无授权、无用量的可以删掉:
POST /v1/apps/delete { "app_id":"...", "app_secret":"..." }
# 想进控制台管理(上架、看统计):
POST /v1/apps/claim { "app_id":"...", "app_secret":"..." }
部署前自检:GET /oauth/appinfo?app_id=… 返回
redirectBound(是否已锁定)与 redirectSource
(none / auto / registered)。
带上 X-App-Secret 头还会返回 redirectUris 明细——
上线前先跑一次,别等授权失败才发现绑错了地址。
换 app_id 会影响什么、不影响什么: 不影响用户钱包余额——余额挂在用户账号上,与 app_id 无关; 不影响已产生的用量与账单,历史记录仍在原标识名下可查。 但授权是「用户 × 应用」两者绑定的,所以已授权的用户需要重新授权一次。 这是换标识的全部代价。
相比 dev_ 的好处:标识独占、别人抢不走;只有持 secret 的人能改回调;
用户在授权页看到的是你的应用名而不是裸标识。代码改动只有换个 app_id 加一次带 secret 的绑定,其余不动。
联网搜索:另收一笔检索费
计费/v1/chat/completions 里用 enable_search 时,
除 token 外另收一笔检索费(与 POST /v1/search 同价,见
GET /v1/services),只在真的联网并拿到结果时收,
金额在响应头 X-Tokin-Search-Billing 里。
为什么:检索由搜索引擎按次收费、不含在模型 token 里。此前这笔由平台承担,不可持续。不使用 enable_search 则不受影响。
企业查询:查无结果改为计费
计费POST /v1/company 查无结果时,此前不收费,现在按同价计费,
响应里带 billing.noMatch=true。
为什么:数据源按次消耗额度,与是否查到无关。免费返回等于平台替用户出钱,且随机关键词可空刷。 上游调用失败仍然不收费——那是我们的问题,不该由你承担。
实时转写:一次性连接票
新增WebSocket 握手带不了 Authorization 头,凭证只能进 query,
而 query 会原样落进服务器访问日志——等于把 sk-* 写进日志文件。现在可以换票:
POST /v1/realtime/ticket # 带 Authorization
# → { "ticket": "rt_...", "expires_in": 60 }
wss://api.tokincloud.com/v1/realtime/transcriptions?ticket=rt_...
票一次性、60 秒有效。旧的 ?token= 仍兼容,不急着改,但线上建议用票。
2026-08-16
音频能力语音转写:文件 + 实时
新增POST /v1/audio/transcriptions(OpenAI 兼容)——句级 + 字级时间戳、说话人分离,
输出 json / verbose_json / srt / vtt / text。长录音走流式上传 + async=true 轮询。
wss://api.tokincloud.com/v1/realtime/transcriptions——约 1 秒出首字,
中间稿持续刷新、句末给终稿。每满 60 秒结算一次并推 billing 消息。
详见开发者文档的音频章节。
回调相关错误码
按 code 分支处理,不必解析中文文案
POST /oauth/code 失败时返回
{"error":{"message","type","code"}};
GET /oauth/appinfo 把同样的码放在
redirectCode 里。
把这些故障统一呈现成「授权完跳不回来」,排查成本会非常高。
| code | 含义 | 怎么处理 |
|---|---|---|
| redirect_locked | 该应用已绑定别的回调,不接受当前这个 | 确认配置里的地址;要换请先 /v1/apps/unbind-redirect 解绑 |
| redirect_bind_forbidden | 还没绑定,但你无权完成首次绑定 | 带上 app_secret,或用创建该应用的账号登录 |
| redirect_scheme_unsupported | 协议不是 http/https | 检查是否误传了 deeplink 或相对地址 |
| redirect_invalid | 不是合法的绝对 URL | 检查拼接逻辑(常见于从请求推导时少了协议或域名) |
| redirect_https_required | 平台要求线上回调用 https(默认未开启) | 配域名+证书;本机调试用 http://127.0.0.1 |
| redirect_bind_failed | 绑定写入失败(少见) | 重试;仍失败请反馈 |
| rename_refused | (已放开,不再返回)改名不再受限 | 持 app_secret 随时可改;已被使用过的应用改名会向用户提示 |
| delete_refused | 应用已被认领、或已产生用量,不能删 | 已认领的到控制台管理;有用量的保留(否则账单会指向不存在的应用) |
| redirect_has_fragment | 地址带了 # 片段 | 去掉片段部分 |
| redirect_missing | 没传 redirect_uri | 只有桌面端「手动复制凭证」模式才可省略 |
| app_not_found | app_id 不存在 | 确认没写错、或该应用已被删除 |
| invalid_app_secret | app_secret 不正确 | 确认没轮换过——轮换后旧 secret 立即失效 |
| invalid_request | 缺 code_challenge / code_verifier 等必填参数 | PKCE 参数没传全;challenge 至少 32 字符 |
| invalid_grant | 授权码或 refresh_token 无效/已用过/已吊销 | 引导用户重新授权(这是唯一需要你处理的凭证异常) |
| unsupported_grant_type | grant_type 不是 authorization_code / refresh_token | 检查换码请求的参数 |
| temporarily_unavailable | 刷新暂时不可用(503) | 退避重试即可,不要当成需重新授权 |
| invalid_body | 注册请求体不合法或缺 name | 检查 Content-Type 与 JSON 结构 |