Download OpenAPI specification:
一个 OpenAI 兼容的模型网关,外加子钥签发与对账接口
完整的说明性文档在 www.jamerly.ai/docs/tare(中英双语)。
⚠️ 2026-08-22.4:.3 曾以缺两段的形式发布过(chat 端点与
Generation.upstream_inference_cost 关于「转售不返回上游成本」的说明在发布流程中
丢失)。行为本身从 0.1.13 起就是这样,丢的只是文字。拿过 .3 的话请重新拉一份。
这份文件只描述 接口形状,不重复文档里的那些判断依据(为什么额度用尽是 403、为什么流式末帧 两种方言不一致等等)——接入前请两份一起读。
全部接口都用 Authorization: Bearer <key>。三类 key 权限互不重叠,用错类型返回明确的
拒绝,而不是含糊的 401:
| key | 能做什么 |
|---|---|
| 推理 key | 调模型(/v1/chat/completions、/v1/embeddings、/v1/models、/v1/generation) |
| 母钥 | 管子钥(/v1/keys/*),不能调模型 |
| 对账 key | 读账(/v1/integration/*),不能调模型 |
除了 Authorization(和流式的 Accept: text/event-stream),不要求任何请求头。
⚠️ 未识别的请求头一律忽略,不会因此拒绝请求,也不会转发给上游。 发往上游的请求头
由我们重建(凭据、Content-Type、方言要求的那几个),你带的头不会出现在上游那一侧。
OpenRouter 的归因头 HTTP-Referer / X-Title 属于这一类:带上无害,但不会到达任何
地方,也不影响路由、计费和限流。
归因请改用 usage_label(请求体内,原样落进用量记录)和子钥本身。
所有时间戳都是 ISO 8601。⚠️ 不带偏移量的那些(2026-08-21T17:30:00)按
Asia/Shanghai(UTC+8)解释 —— 它们是服务端本地时间。账期也按这个时区切,
/v1/integration/whoami 会返回当时使用的时区,以该字段为准,不要硬编码。
| 接口 | 形状 | 失败时 |
|---|---|---|
/v1/chat/completions、/v1/embeddings、/v1/keys/*、/v1/models、/v1/generation |
OpenAI:{"error":{...}} |
HTTP 非 2xx |
/v1/integration/* |
ApiResponse:{"code","message","data","success"} |
HTTP 非 2xx,body 仍是 ApiResponse(不是 200 + success:false) |
请求体原样透传:未识别的字段照发给上游,这是 cache_control、reasoning、
plugins 这类字段能工作的原因。我们只改动四处:模型名换成上游认的、stream
固定为本次尝试的模式、流式强制 stream_options.include_usage=true、剥除归因字段。
⚠️ usage.cost 是「花了你多少」。 走平台渠道(token 的钱由我们垫)时,回包中
不含 usage.cost_details —— 那一格是上游向我们收取的金额。BYOK 下保留:
那是你自己上游账单上的钱,也正是成本归因需要的数据。
(2026-08-22 之前两种模式都返回了;已修。)
算不出来时 cost 整格不出现,而不是 0 —— 「该模型尚未定价」也算算不出来。
⚠️ 两种上游方言的流式末帧形状不一致(OpenAI 兼容是 choices: [],Anthropic 是
choices 有一项而 delta 为空)。按帧渲染时判据写成「choices 为空 或 delta
无内容 → 跳过渲染,只取 usage」。
| model required | string 平台侧模型名,取自 |
required | Array of objects |
| stream | boolean Default: false |
| max_tokens | integer |
| temperature | number |
| usage_label | string 你自己的归因标签,会原样落进用量记录,基数不设上限 |
| property name* additional property | any |
{- "model": "GLM 5.1",
- "messages": [
- {
- "role": "system",
- "content": null
}
], - "stream": false,
- "max_tokens": 0,
- "temperature": 0,
- "usage_label": "string"
}{- "id": "string",
- "object": "chat.completion",
- "created": 0,
- "model": "string",
- "provider": "string",
- "choices": [
- null
], - "usage": {
- "prompt_tokens": 0,
- "completion_tokens": 0,
- "total_tokens": 0,
- "cost": 0,
- "is_byok": true
}
}请求体原样透传,回包是上游原文加一格 usage.cost。
⚠️ 模型名不做映射。 chat 那边写的是我们这边的模型名(取自 GET /models),
这里写的是上游自己的名字(text-embedding-3-small 这一类)——
embeddings 不走模型路由表,所以没有那层映射,回包里的模型名同样不改写。
⚠️ 打到哪条渠道:你自己的渠道优先(显式配的 > 按方言推出来的), 都没有才走平台那条被指定的渠道。 两者的计费不同(自己的渠道只记账,平台渠道按售价扣余额),所以不会在两者 之间自动故障转移 —— 一次静默的回退等于在你没同意的情况下开始花钱。
没有流式。gen- 编号走响应头,用法和 chat 一致。
| model required | string 上游的模型名(不是 |
required | string or Array of strings 字符串或字符串数组 |
| dimensions | integer |
| encoding_format | string Enum: "float" "base64" |
| usage_label | string 你自己的归因标签,会原样落进用量记录 |
| property name* additional property | any |
{- "model": "text-embedding-3-small",
- "input": "string",
- "dimensions": 0,
- "encoding_format": "float",
- "usage_label": "string"
}{- "object": "list",
- "model": "string",
- "data": [
- {
- "object": "embedding",
- "index": 0,
- "embedding": [
- 0
]
}
], - "usage": {
- "prompt_tokens": 0,
- "total_tokens": 0,
- "cost": 0
}
}完整明文只在创建时返回一次。带 external_id 时同一个编号重复创建是幂等的:
返回已存在的那把,HTTP 状态码是 200 而不是 201。
⚠️ 幂等重放只在创建后 5 分钟内能拿回明文。超过窗口 key 是 null,
并附 key_unavailable_reason(枚举值)和 key_unavailable_message(文案会变)。
| name required | string 用途说明,由你填写;原样进入账单归因 |
| limit | number or null 累计消费上限(美元)。不传 = 不限额 |
| external_id | string 你自己的编号,用来做创建幂等;唯一性是 (账号, external_id) |
| include_byok_in_limit | boolean Default: true BYOK 渠道的消耗算不算进这把 key 的上限 |
| expires_at | string 形如 |
{- "name": "string",
- "limit": 0,
- "external_id": "string",
- "include_byok_in_limit": true,
- "expires_at": "string"
}{- "key": "string",
- "key_unavailable_reason": "plaintext_window_expired",
- "key_unavailable_message": "string",
- "data": {
- "hash": "string",
- "name": "string",
- "label": "string",
- "limit": 0,
- "limit_remaining": 0,
- "usage": 0,
- "disabled": true,
- "include_byok_in_limit": true,
- "external_id": "string",
- "byok_usage": 0,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z"
}
}游标翻页。⚠️ 游标是 id 不是 offset:翻页途中若有新 key 创建,offset 会让某一把被 跳过或重复出现。账号下的子钥总数不设上限,单次返回的行数有上限。
| limit | integer <= 200 Default: 100 单页行数。超过 200 不报错,会被压到 200 |
| after | integer <int64> 上一页返回的 |
{- "data": [
- {
- "hash": "string",
- "name": "string",
- "label": "string",
- "limit": 0,
- "limit_remaining": 0,
- "usage": 0,
- "disabled": true,
- "include_byok_in_limit": true,
- "external_id": "string",
- "byok_usage": 0,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z"
}
], - "next_cursor": 0
}| hash required | string 创建时返回的 |
{- "data": {
- "hash": "string",
- "name": "string",
- "label": "string",
- "limit": 0,
- "limit_remaining": 0,
- "usage": 0,
- "disabled": true,
- "include_byok_in_limit": true,
- "external_id": "string",
- "byok_usage": 0,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z"
}
}⚠️ 解封用 {"disabled": false},不要用同一个 external_id 再开一把:
external_id 的唯一性包含已停用的 key,一个编号永久对应一把 key。
| hash required | string 创建时返回的 |
| limit | number or null 改累计上限; |
| name | string |
| disabled | boolean
|
| include_byok_in_limit | boolean |
| expires_at | string or null <date-time> 改到期时间;显式 |
{- "limit": 0,
- "name": "string",
- "disabled": true,
- "include_byok_in_limit": true,
- "expires_at": "2019-08-24T14:15:22Z"
}{- "data": {
- "hash": "string",
- "name": "string",
- "label": "string",
- "limit": 0,
- "limit_remaining": 0,
- "usage": 0,
- "disabled": true,
- "include_byok_in_limit": true,
- "external_id": "string",
- "byok_usage": 0,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z"
}
}有了它就不必在你那边维护一张 external_id → hash 的映射表。
| externalId required | string 你自己的编号 |
{- "data": {
- "hash": "string",
- "name": "string",
- "label": "string",
- "limit": 0,
- "limit_remaining": 0,
- "usage": 0,
- "disabled": true,
- "include_byok_in_limit": true,
- "external_id": "string",
- "byok_usage": 0,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z"
}
}| externalId required | string 你自己的编号 |
| limit | number or null 改累计上限; |
| name | string |
| disabled | boolean
|
| include_byok_in_limit | boolean |
| expires_at | string or null <date-time> 改到期时间;显式 |
{- "limit": 0,
- "name": "string",
- "disabled": true,
- "include_byok_in_limit": true,
- "expires_at": "2019-08-24T14:15:22Z"
}{- "data": {
- "hash": "string",
- "name": "string",
- "label": "string",
- "limit": 0,
- "limit_remaining": 0,
- "usage": 0,
- "disabled": true,
- "include_byok_in_limit": true,
- "external_id": "string",
- "byok_usage": 0,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z"
}
}带上推理 key,目录里就包含你自己配的模型:你在自己渠道上配的那些 ∪ 平台的那些, 同名时以你自己那条为准 —— 和真正调用时的路由口径完全一致。
凭据是可选的:不带任何请求头时返回平台目录(客户端确实这么用,这条路一直保留)。
但带了就必须是真的 —— 无效或过期的 key 返回 401,不会静默给你一份
「少了你自己模型」的目录。
推理 key 和母钥都可以。母钥没有绑 Router,它拿到的是账号默认那套路由的目录;
要看某一把绑了别的 Router 的子钥能用什么,就拿那把子钥来调。
对账 key 返回 403(它碰不到模型这一片)。
⚠️ 你自己渠道上的模型,pricing.prompt / completion 是 0:那些 token 你直接付给
上游,不经我们的价目表。真实成本读回包的 usage.cost。
{- "data": [
- {
- "id": "string",
- "name": "string",
- "description": "string",
- "context_length": 0,
- "architecture": {
- "modality": "string",
- "input_modalities": [
- "string"
], - "output_modalities": [
- "string"
]
}, - "reasoning": {
- "mandatory": true,
- "default_enabled": true,
- "supported_efforts": [
- "string"
], - "default_effort": "string"
}, - "pricing": {
- "prompt": "string",
- "completion": "string",
- "input_cache_read": "string",
- "input_cache_write": "string",
- "request": "string",
- "image": "string"
}
}
]
}⚠️ 404 是正常路径,不是错误:用量记录在响应之后才写入。非流式通常 1 秒内可查, 流式以流结束为准;中途断开的流需事后向上游回查,可能到分钟级。 请把 404 当作「稍后再试」,退避几秒后重试。
| id required | string 响应头 |
{- "data": {
- "id": "string",
- "model": "string",
- "provider_name": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "tokens_prompt": 0,
- "tokens_completion": 0,
- "native_tokens_prompt": 0,
- "native_tokens_completion": 0,
- "total_cost": 0,
- "usage": 0,
- "upstream_inference_cost": 0,
- "is_byok": true,
- "cancelled": true
}
}这一组是留存开关本身(控制台上叫 Call Traces)。默认不存:关闭时
request_content / response_content 两列为空 —— 是没有存,不是存了不给看。
| productLine | string 指定产品线;不传即账号级。 ⚠️ 只能放在 GET 的 query 上,写操作一律放在 body:前置网关会丢弃 POST/PUT 的 query string,写在 query 上不报错也不生效。 |
{- "code": 0,
- "message": "string",
- "success": true,
- "data": {
- "enabled": true,
- "retentionDays": 0,
- "productLine": "string",
- "operatorAccess": {
- "granted": true,
- "expiresAt": "2019-08-24T14:15:22Z",
- "reason": "string"
}
}
}| enabled | boolean |
| retentionDays | integer [ 1 .. 365 ] 默认 30;超出 1–365 返回 400 |
| productLine | string |
{- "enabled": true,
- "retentionDays": 1,
- "productLine": "string"
}{- "code": 0,
- "message": "string",
- "success": true,
- "data": {
- "enabled": true,
- "retentionDays": 0,
- "productLine": "string",
- "operatorAccess": {
- "granted": true,
- "expiresAt": "2019-08-24T14:15:22Z",
- "reason": "string"
}
}
}⚠️ 没有「永久授权」这个选项,时长 1 小时 – 30 天:无时限的授权会在排查结束后 一直留存。授权期内每次读取都会记录审计。
| hours | integer [ 1 .. 720 ] Default: 24 |
| reason | string 给审计看的理由 |
| productLine | string |
{- "hours": 24,
- "reason": "string",
- "productLine": "string"
}{- "code": 0,
- "message": "string",
- "success": true,
- "data": {
- "granted": true,
- "expiresAt": "2019-08-24T14:15:22Z",
- "reason": "string"
}
}到期的授权自动失效,无需处理。
| productLine | string |
{- "productLine": "string"
}{- "code": 0,
- "message": "string",
- "success": true,
- "data": {
- "granted": true,
- "expiresAt": "2019-08-24T14:15:22Z",
- "reason": "string"
}
}读取夜间汇总表,当天部分为实时计算。⚠️ 单次查询窗口上限 100 天,超出返回 明确的错误,不做静默截断。
| from | string <date> 含,默认本期第一天 |
| to | string <date> 含,默认今天 |
| apiKeyId | integer <int64> 只看某一把 key |
{- "code": 0,
- "message": "string",
- "success": true,
- "data": null
}⚠️ 账号级:同一账号下所有 key 共享一个钱包。要限制单把 key 的消费用母钥的
limit,那是另一套机制。
{- "code": 0,
- "message": "string",
- "success": true,
- "data": {
- "currency": "USD",
- "balance": 0,
- "creditLimit": 0,
- "available": 0,
- "lifetimeCharged": 0,
- "lifetimeCredited": 0,
- "periods": [
- {
- "period": "2026-07",
- "charged": 0,
- "credited": 0
}
]
}
}{- "code": 0,
- "message": "string",
- "success": true,
- "data": {
- "content": [
- {
- "id": 0,
- "at": "2019-08-24T14:15:22Z",
- "category": "string",
- "type": "string",
- "direction": "IN",
- "amount": 0
}
], - "total": 0,
- "page": 0,
- "size": 0
}
}