变更记录

接口、计费口径与安全规则的变动都记在这里。已经接入的请只看红色和黄色标记那几条—— 其余是新增能力,不影响现有代码。

破坏性 · 需改代码 计费口径变化 新增能力 · 不影响现有代码

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(是否已锁定)与 redirectSourcenone / 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_foundapp_id 不存在确认没写错、或该应用已被删除
invalid_app_secretapp_secret 不正确确认没轮换过——轮换后旧 secret 立即失效
invalid_request缺 code_challenge / code_verifier 等必填参数PKCE 参数没传全;challenge 至少 32 字符
invalid_grant授权码或 refresh_token 无效/已用过/已吊销引导用户重新授权(这是唯一需要你处理的凭证异常)
unsupported_grant_typegrant_type 不是 authorization_code / refresh_token检查换码请求的参数
temporarily_unavailable刷新暂时不可用(503)退避重试即可,不要当成需重新授权
invalid_body注册请求体不合法或缺 name检查 Content-Type 与 JSON 结构
发现文档与实际行为对不上? 直接通过反馈通道告诉我们—— 文档里承诺了不存在的东西,比没写更糟。