关于 subrouter
SubRouter 是一项引导 Agent 完成账户授权并配置客户端使用其 API 网关的技能。它强调凭据安全,并要求在持久修改配置前取得确认。
主要功能
- 01通过设备流程授权 API 密钥
- 02配置兼容 OpenAI 和 Anthropic 的客户端
- 03经由 SubRouter 路由模型请求
- 04提交并轮询异步图像与视频任务
- 05落实凭据处理与计费安全规则
适用场景
- 01为 Agent 授权 SubRouter 账户
- 02将指定 SDK 或应用接入 SubRouter
- 03通过异步任务生成图像或视频
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ abingyyds/ subrouter-skills --skill subrouter安装后在 Claude Code 中输入/subrouter确认已加载。
附带文件references/provider-models.md
详细介绍
SubRouter Agent 接入指南
SubRouter 是一个 AI API 网关:一个 API 密钥,兼容 OpenAI 和 Anthropic 的端点,跨提供商按请求智能路由,并按 token 透明计费。本技能将指导 Agent 端到端地帮助用户接入 SubRouter。
基础 URL:使用用户指定的网站,或默认的 https://subrouter.ai。以下示例均使用 $BASE。
安全规则(不可妥协)
- 仅将 API 密钥发送到用户选择的 SubRouter 主机。绝不发送到任何其他域名。
- 始终使用
--max-redirs 0调用,确保重定向绝不会将密钥带到其他位置。 - 除了用户要求配置的客户端配置文件外,绝不将完整密钥输出到日志或文件中。确认时最多显示
sk-xxxx…前缀。 - **绝不让凭据出现在 stdout 中。**授权响应会完整包含
device_code、key和access_token;直接运行curl会将它们输出到终端和 Agent 记录中。务必将这些响应写入权限为0600的文件,并从中读取各个字段(步骤 1 展示了具体方法)。只报告状态、user_code、验证 URL 和sk-前缀。 - 付款始终由用户在浏览器中完成。绝不处理银行卡号、加密货币种子短语或付款凭据。
步骤 0 — 先确认即将进行的更改
获取密钥和重新配置某人的编码 Agent 是两件不同的事。绝不要因为用户要求获取密钥,就推断他们也想进行第二项操作。在步骤 1 之前,说明你对需求的理解并取得用户确认:
- 目标 — 用户想要以下哪一种?
- 仅账户操作:授权并显示密钥,不更改磁盘上的任何内容。
- 单次调用:在本次会话中通过环境变量使用密钥;不会写入任何配置,但步骤 1 创建的凭据文件会保留(参见步骤 1),除非用户要求删除。
- 配置 SDK/应用:写入用户指定的项目。
- 切换编码 Agent 的模型提供商:将 Claude Code / Codex / Cursor 改为使用 SubRouter。
- 目标位置 — 明确列出要修改的文件及其绝对路径,并说明更改仅限本次会话还是会持久保留。
- 现有配置 — 先读取目标文件,再说明其中已有的内容。如果文件中已有凭据或提供商配置,务必告知用户并在修改前征求同意。
默认采用满足请求所需的最小侵入性方案。如有疑问,停留在仅账户操作,并向用户询问。
**特别说明 Codex:**大多数 Codex 用户使用的是 ChatGPT 订阅,而不是 API 密钥。不要写入 ~/.codex/config.toml,不要覆盖 ~/.codex/auth.json,也不要替换现有的 model_provider,除非用户明确要求将 Codex 切换到 SubRouter。展示你准备添加的配置,并让用户批准。
绝不要覆盖现有配置文件。应追加内容或创建新的配置文件,并告知用户如何恢复。
步骤 1 — 设备授权(获取 API 密钥)
开始新流程前,请先查找已保存的凭据(~/.subrouter/credentials.json 或之前运行时保存凭据的其他文件/环境变量)。如果使用该 sk- 密钥调用 GET $BASE/v1/models 返回 200,请复用该密钥并跳到步骤 2;只有在没有已保存的密钥或密钥已失效时,才重新授权。
启动设备授权。channel 用于标识你的 Agent 类型(claude、cursor、codex 等)。使用相同的 channel 重新授权时,会返回账户中现有的已启用 Agent 授权令牌 (<channel>),而不是再创建一个令牌,因此反复登录不会累积密钥——但密钥仍只会通过此流程交付,所以获取后请妥善保存:
/api 下的每个端点都会将结果封装在一个信封中。 负载始终位于 data 下,并与 success 一同返回——{"success":true,"message":"","data":{…}}。有些端点还会添加同级字段(/api/pricing 也会返回 vendors、usable_group、group_ratio;/api/marketplace/models 会返回 total),因此请明确读取 .data,不要假定整个响应体就是负载。失败时,success 为 false,message 中包含原因。/v1 下的端点采用原始 OpenAI/Anthropic 格式,不会封装。
将响应写入私密文件,不要让它直接打印出来:
umask 077
AUTH_TMP="$(mktemp -t subrouter-auth)" # 0600 via umask
curl -s --max-redirs 0 -X POST "$BASE/api/device_auth" \
-H 'Content-Type: application/json' \
-d '{"channel":"claude"}' -o "$AUTH_TMP"
.data 中包含:
{
"user_code": "ABCD-2345",
"device_code": "<64-char secret — never print this>",
"verification_url": "https://subrouter.ai/device?code=ABCD-2345",
"expires_in": 600,
"interval": 3
}
从文件中读取 .data.user_code 和 .data.verification_url,并且只展示这两项。device_code 是由机器持有的机密:任何看到它的人都可以在你之前领取密钥,因此绝不能回显。
告知用户在浏览器中打开该 URL,登录(或注册),确认页面显示的 user_code 一致,然后进行确认。验证码将在 10 分钟后过期。
然后每隔 .data.interval 秒轮询一次(绝不能更快),并再次将响应写入私密文件:
curl -s --max-redirs 0 -X POST "$BASE/api/device_auth/poll" \
-H 'Content-Type: application/json' \
-d "{\"device_code\":\"$DEVICE_CODE\"}" -o "$AUTH_TMP"
根据 .data.status 进行处理:
authorization_pending— 继续等待。slow_down— 轮询过快;延长等待时间。approved—.data.key(sk-…)、.data.access_token、.data.user_id和.data.base_url均已填入。仅交付一次:如果丢失,就必须重新执行整个流程。从文件中保存这些值,绝不要通过回显来获取。success:false且提示为 "invalid or expired" — 流程已过期或已被使用;从步骤 1 重新开始。
请保留凭据。将已批准的响应从临时路径移至下次会话能够找到的稳定私密文件,例如
install -m 600 "$AUTH_TMP" ~/.subrouter/credentials.json(使用 mkdir -p -m 700 ~/.subrouter 创建目录)。任务结束时不要删除该文件:
令牌不会过期,删除它会导致用户下次必须再次通过浏览器确认授权。只有在用户要求时才删除文件,并告知用户删除了哪个文件。
你会获得三个权限各不相同的值:
key(sk-…)— 中继令牌:用于模型调用、/v1/models、账单查询。将其填入客户端配置。access_token— 账户管理凭据:拥有用户账户下完整的管理 API 访问权限。绝不要将其填入客户端配置;只能将其用于下面的管理调用,并且要比sk密钥更加谨慎地保护它。user_id— 数字账户 ID。它不是机密,但缺少它将导致每个管理调用失败(见下文)。请将其与访问令牌一同保存。
第 2 步 — 配置客户端
同一个密钥适用于这两种协议接口。
Claude Code(Anthropic 协议):
export ANTHROPIC_BASE_URL="$BASE"
export ANTHROPIC_API_KEY="sk-…"
OpenAI SDK / Cursor / Codex(OpenAI 协议):
export OPENAI_BASE_URL="$BASE/v1"
export OPENAI_API_KEY="sk-…"
核心端点:POST /v1/chat/completions(OpenAI)、POST /v1/messages(Anthropic)、GET /v1/models。
sk 密钥也可用于完整的多模态接口(是否可用取决于模型 — 请查看 /api/pricing):
- 图像和视频:请参阅下方的“第 2b 步” — 使用异步任务 API,不要使用同步端点
- 音频:
POST /v1/audio/transcriptions、/v1/audio/speech、/v1/audio/translations - 其他:
POST /v1/embeddings、/v1/rerank、/v1/moderations、/v1/responses、GET /v1/realtime(WebSocket)
第 2b 步 — 图像和视频:使用异步任务 API
图像和视频生成需要几十秒到几分钟。同步调用会在整个过程中占用一个 HTTP 连接,因此会导致失败:网关前面的代理和 CDN 会超时并返回 504,同时并行运行多个任务会耗尽连接并产生 502。默认使用异步方式。
| 情况 | 使用方式 |
|---|---|
| 任何模型的视频 | 异步 — 始终使用。切勿同步调用视频接口。 |
| 一次生成多张图像 | 异步 |
| 批量或无人值守生成图像 | 异步 |
| 生成单张图像,且用户正在交互等待 | 可以使用同步方式 |
计费规则(生成前请阅读)
每次成功生成都会计费,包括重试。 以下规则不可变通:
- 每个用户请求只提交一次。 不要擅自生成多个变体、尺寸或“改进版”。如果用户要求生成一张图像,就提交一次。
- 重试会产生新的费用。 在失败、超时或用户不喜欢输出后重新提交之前,必须明确说明“这将再次产生生成费用”,并征得用户同意。唯一例外是计费前就失败的提交(返回 4xx 错误且没有任务 ID)。
- 绝不丢弃已计费的内容。 每个已完成任务的输出都必须先写入磁盘,再继续后续操作,包括用户最终不喜欢的中间尝试。后续尝试覆盖文件导致已付费图像丢失,会造成用户的实际损失。请为每个任务 ID 使用不同的文件名;切勿在不同尝试中重复使用同一文件路径。
- 及时下载已完成的结果。 上游结果 URL 会过期。
status为completed后立即获取并保存;不要让已完成的任务闲置,也不要先去做其他事。 - 提交前验证,而不是提交后:先根据
/api/pricing检查size/ 宽高比、时长和模型功能。计费后参数被拒绝,费用将无法挽回。 - 最后汇报:提交的生成任务数量、模型、尺寸 / 时长、总费用,以及每个已保存文件的绝对路径。
如果连接在流程中断开,不要重新提交。任务已在服务端创建 — 请通过 GET /api/task/self(下方的“列出过往任务”)找回并下载结果。这正是优先使用异步方式的主要原因:同步调用中断会导致已付费的图像丢失。
提交
图像 — 在常规路径后追加 /async:
curl -s --max-redirs 0 -X POST "$BASE/v1/images/generations/async" \
-H "Authorization: Bearer sk-…" -H 'Content-Type: application/json' \
-d '{"model":"<image-model>","prompt":"…","n":1,"size":"1024x1024"}'
POST /v1/images/edits/async 的工作方式相同。
请求会返回 202 Accepted,并带有 Location: /v1/tasks/{task_id} 标头和 media.task 响应体:
{"id":"…","object":"media.task","type":"image","action":"generate",
"status":"pending","progress":0,"model":"…","created_at":1756400000,
"result":{"images":[],"videos":[]},"usage":{"quota":1234},"error":null}
即使上游服务商只支持同步调用,/async 门面也始终会生成一个任务 — 网关会在后台为你运行任务。
视频 — POST /v1/video/generations 本身就是异步的;请求体遵循模型自身的架构。响应采用平台原生的提交格式,但其中的任务 ID 属于 SubRouter,因此请以相同方式轮询。POST /v1/videos 是兼容 OpenAI 的别名。
轮询
一个端点涵盖图像和视频任务:
curl -s --max-redirs 0 "$BASE/v1/tasks/<task_id>" -H "Authorization: Bearer sk-…"
status 的值为 pending、processing、completed 或 failed。轮询间隔不要短于 2–3 秒,长视频任务则应逐步延长间隔。状态为 completed 时,图像任务读取 result.images[].url / result.images[].b64_json,视频任务读取 result.videos[].url;状态为 failed 时,读取 error.message。result 始终包含两个数组——与任务类型不匹配的数组会保持为空,而不是为 null。
也有原生的分平台轮询路径(GET /v1/images/generations/{task_id}、GET /v1/video/generations/{task_id}、GET /v1/videos/{task_id})——优先使用 /v1/tasks/{task_id},其结构在所有模型间都相同。
下载视频
curl -fL "$BASE/v1/videos/<task_id>/content" -H "Authorization: Bearer sk-…" -o out.mp4
上游视频 URL 往往有效期很短或受地域限制;此端点会通过网关使用 sk 密钥传输文件。请使用此端点,而不要直接获取 result.videos[].url。
列出过往任务
GET /api/task/self(使用访问令牌)会列出用户的媒体任务——可用于找回客户端丢失的任务 ID。
第 3 步 — 选择模型(也可选择提供商)
价格和提供商市场公开可查,无需身份验证:
curl -s --max-redirs 0 "$BASE/api/pricing" # models + prices
curl -s --max-redirs 0 "$BASE/api/marketplace/models" # marketplace models with provider offers
curl -s --max-redirs 0 "$BASE/api/marketplace/providers" # provider (商家) list
curl -s --max-redirs 0 "$BASE/api/marketplace/providers/<slug>" # one provider's detail
curl -s --max-redirs 0 "$BASE/api/marketplace/reviews" # provider reviews
根据用户明确表达的偏好(价格最低、指定供应商/提供商、功能)进行选择。向用户展示排名靠前的候选项及其价格,让用户选择;不要擅自猜测。
第 4 步 — 充值(由用户操作,而非你)
在作出任何假设前,先检查余额。 新注册的账户余额为零,但重新授权现有账户的老用户可能已有余额。只有在确认余额为零/不足或收到 402 后,才引导用户访问 $BASE/console/topup。充值始终在用户的浏览器中进行。
curl -s --max-redirs 0 "$BASE/v1/dashboard/billing/subscription" -H "Authorization: Bearer sk-…"
curl -s --max-redirs 0 "$BASE/v1/dashboard/billing/usage" -H "Authorization: Bearer sk-…"
账户管理(使用 access_token)
管理端点需要两个请求头,而不是一个:
Authorization: Bearer <access_token>(Bearer前缀可省略)New-Api-User: <user_id>— 第 1 步中的数字账户 ID
两者都必须提供。仅发送第一个请求头会返回 401 无权进行此操作,未提供 New-Api-User,而与访问令牌所有者不匹配的 user_id 会返回 401 New-Api-User 与登录用户不匹配——因此无法猜测。以下所有 /api 管理端点均适用此规则,包括提供商(商家)相关端点。
AUTH='Authorization: Bearer <access_token>'
AUTH_USER='New-Api-User: <user_id>'
# Account info & balance
curl -s --max-redirs 0 "$BASE/api/user/self" -H "$AUTH" -H "$AUTH_USER"
# Usage reconciliation ("where did my money go")
curl -s --max-redirs 0 "$BASE/api/log/self?cursor=first&page_size=20" -H "$AUTH" -H "$AUTH_USER" # per-call logs, newest first; if has_more, pass next_cursor as cursor
curl -s --max-redirs 0 "$BASE/api/log/self/stat" -H "$AUTH" -H "$AUTH_USER" # totals (today by default)
# Log list/stat/export default to today 00:00 → now and refuse ranges over 31 days ("时间跨度不能超过 31 天"): split longer questions into ≤31-day windows.
curl -s --max-redirs 0 "$BASE/api/data/self" -H "$AUTH" -H "$AUTH_USER" # per-day quota usage
curl -s --max-redirs 0 "$BASE/api/task/self" -H "$AUTH" -H "$AUTH_USER" # async media tasks
# Redeem a voucher code the user already has (this is NOT payment)
curl -s --max-redirs 0 -X POST "$BASE/api/user/self/topup" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"key":"<redemption-code>"}'
# Referral: the user's invite code and earnings
curl -s --max-redirs 0 "$BASE/api/user/self/aff" -H "$AUTH" -H "$AUTH_USER" # invite code → share $BASE/register?aff=<code>
curl -s --max-redirs 0 "$BASE/api/user/self/aff_dashboard" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/user/self/aff_earnings" -H "$AUTH" -H "$AUTH_USER"
# Move referral earnings into the usable balance (confirm amount with the user first)
# NOTE: quota is in internal units, 500000 = $1 — never pass a dollar number directly
curl -s --max-redirs 0 -X POST "$BASE/api/user/self/aff_transfer" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"quota":<amount-in-quota-units>}'
# List / create API tokens
curl -s --max-redirs 0 "$BASE/api/token/" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 -X POST "$BASE/api/token/" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"name":"my-project","expired_time":-1,"unlimited_quota":true}'
限定模型或提供商的密钥
令牌可以限制为特定模型、每个模型对应的特定提供商(商家),以及价格上限。当用户说“只用某商家”或“只允许这个模型”时,请使用此功能:
curl -s --max-redirs 0 -X POST "$BASE/api/token/" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{
"name": "sonnet-via-acme",
"expired_time": -1,
"unlimited_quota": true,
"model_limits_enabled": true,
"model_limits": "claude-sonnet-4-6,claude-haiku-4-5",
"subrouter_model_providers": "{\"claude-sonnet-4-6\":[\"acme\"]}"
}'
model_limits— 以逗号分隔的模型白名单(并设置model_limits_enabled: true)。subrouter_model_providers— 一个 JSON 字符串,将模型名称映射到允许的提供商 slug;slug 来自GET /api/marketplace/providers。未列出的模型仍使用常规路由。subrouter_model_price_limits— 可选的逐模型输入/输出价格上限(同样为 JSON 字符串)。
创建密钥前,请将确切的限制复述给用户。
成为供应商(商家)并上架模型
用户的账户可以申请成为供应商,然后管理自己的渠道和模型 — 全程使用相同的 access_token 和 New-Api-User 标头。
对于添加或更新模型、更改价格、批量导入或主备渠道,请先阅读供应商模型管理。其中介绍了定价策略、缓存价格的 null/zero 语义、不会修改数据的价格预览、延迟调价、分页,以及带版本控制的路由列表替换。这些是商家上架 API,不是下方的分销商 API,也不是官方 Key 市场。
# Apply to become a provider (platform review may apply)
curl -s --max-redirs 0 -X POST "$BASE/api/provider/register" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{...}'
# After approval, provider management lives under /api/provider/*
curl -s --max-redirs 0 "$BASE/api/provider/self" -H "$AUTH" -H "$AUTH_USER" # provider profile
curl -s --max-redirs 0 "$BASE/api/provider/models?page=1&page_size=50" -H "$AUTH" -H "$AUTH_USER" # data is one page; total is at the envelope root
# POST /api/provider/models creates; PUT /api/provider/models/<id> updates.
# Omit all pricing fields for metadata-only edits. Partial price edits merge
# into the saved/pending quote, but the pricing strategy can derive other prices.
# POST /api/provider/models/price-preview calculates the proposal without saving.
# GET/PUT /api/provider/models/<id>/routes manages ordered primary/backup targets.
# Two optional markdown doc fields ride along on the same model body, and they
# publish very differently: params_doc (the request-parameter table users read
# in the docs page) goes live on save, while integration_guide (the whole
# calling guide) is queued for AI review and stays invisible for about an hour.
# Put a parameter table in params_doc, not in integration_guide. Omitting either
# field leaves it untouched. params_doc clears with ""; integration_guide does
# NOT — "" is refused on a custom endpoint and queues an empty draft for review
# on a standard one, so clear it with use_standard_integration_guide: true.
# Channels: /api/provider/channels (GET/POST/PUT/DELETE)
# Bulk model maintenance (e.g. syncing upstream price changes)
# POST /api/provider/models/batch — create many models at once
# POST /api/provider/models/batch-status — enable/disable in bulk
# POST /api/provider/models/batch-delete — remove in bulk
# Channel debugging — <id> is a channel id from GET /api/provider/channels
curl -s --max-redirs 0 "$BASE/api/provider/channels/<id>/test" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/provider/channels/<id>/fetch_models" -H "$AUTH" -H "$AUTH_USER"
# Earnings (read-only)
curl -s --max-redirs 0 "$BASE/api/provider/earnings/summary" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/provider/earnings" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/provider/payouts" -H "$AUTH" -H "$AUTH_USER" # payout history
提现(POST /api/provider/payout)属于资金操作:请勿自行调用 — 请引导用户在浏览器中前往供应商控制台。
在提交供应商申请,或更改任何会影响上架内容、价格或资金的事项之前,请明确说明即将提交的内容,并取得用户的明确确认。对于批量操作,请先展示完整的变更列表(模型名称、价格、状态)。
运营分销站(分站站长)
分销商是运营自己的 SubRouter 分站的用户。分站管理接口位于 /api/distributor/* 下,使用与上述账户调用相同的 access_token + New-Api-User 组合;账户必须已拥有分销商角色。成为分销商,以及续费或购买分站折扣,均需在浏览器中通过 $BASE/console/distributor 付费 — 切勿调用这些接口(/api/distributor/register/pay、/subscription/renew、/discount/buy)。
始终调用主站。 此处的 $BASE 指 SubRouter 主站地址(除非用户指定其他主站,否则为 https://subrouter.ai),绝不是分站自己的域名:分站域名会拒绝除 /api/dist/* 以外的所有 /api 路径,并返回 403 该接口不能通过分站域名访问。第 1 步也要对主站执行。
更改任何内容之前先读取分站,并在每次写入前重新读取 — 有几个 PUT 接口会替换整个对象:
curl -s --max-redirs 0 "$BASE/api/distributor/self" -H "$AUTH" -H "$AUTH_USER" # name, domain, display_mode, enable_all_models, global_markup, registration_mode, …
curl -s --max-redirs 0 "$BASE/api/distributor/dashboard" -H "$AUTH" -H "$AUTH_USER" # traffic, revenue, customer counts
确认分站的上架模式
/self 上的 enable_all_models 决定模型如何上架,两种模式使用不同的接口。先检查该值,再使用相应的接口组。
enable_all_models | 模型上架方式 | 接口 |
|---|---|---|
true(全站上架) | 自动上架所有者订阅的每家供应商的所有模型;所有者只能排除模型或调整价格 | /models/global* |
false(手动) | 所有者逐个上架模型 | /models、/providers/:id/models |
两种模式都需要满足同一前提:所有者自己的账户必须先在主市场订阅供应商,才能上架该供应商的任何模型,否则 API 会返回 该商家不在当前分站订阅范围内。GET /api/distributor/providers?keyword=&page=1&page_size=50 仅列出已订阅的供应商。要添加供应商,请调用 POST /api/marketplace/subscribe,并传入 {"provider_id":<id>}(id 来自 GET /api/marketplace/providers);调用 DELETE /api/marketplace/subscribe/<id> 可取消订阅。订阅本身不收费。
这些接口返回的 provider_id 值可能是代表共享方案而非供应商的虚拟 id。请原样传递;切勿自行构造。
全站上架开启时的上架 / 下架
curl -s --max-redirs 0 "$BASE/api/distributor/models/global" -H "$AUTH" -H "$AUTH_USER"
# items: provider_id, provider_slug, provider_name, model_name, display_name, enabled,
# input_price, output_price, fixed_price, price_currency, has_custom_price, custom_*
# 下架 one model (adds an exclusion); "enabled": true restores it
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/models/global/status" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"provider_id":12,"model_name":"gpt-5.4","enabled":false}'
# a whole provider at once
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/models/global/provider-status" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"provider_id":12,"enabled":false}'
# override the price of one model; omit a field to leave it alone
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/models/global/price" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"provider_id":12,"model_name":"gpt-5.4","custom_input_price":2.5,"custom_output_price":10}'
# alternatives: "custom_fixed_price" (per-call models), "custom_price_multiplier";
# {"provider_id":12,"model_name":"gpt-5.4","clear_custom_pricing":true} returns to provider price + markup
手动模式下的上架 / 下架
curl -s --max-redirs 0 "$BASE/api/distributor/models" -H "$AUTH" -H "$AUTH_USER" # what is listed now (id, provider_id, model_name, display_name, markup_percent, custom_*_price, enabled, sort_order)
curl -s --max-redirs 0 "$BASE/api/distributor/providers/12/models" -H "$AUTH" -H "$AUTH_USER" # a provider's catalogue, marked with what the station already lists
# 上架 several models from one provider
curl -s --max-redirs 0 -X POST "$BASE/api/distributor/providers/12/models" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"models":[{"model_name":"gpt-5.4","display_name":""},{"model_name":"gpt-5.4-mini","display_name":""}]}'
# 上架 one model with its own markup
curl -s --max-redirs 0 -X POST "$BASE/api/distributor/models" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"provider_id":12,"model_name":"gpt-5.4","display_name":"","markup_percent":20,"enabled":true,"sort_order":0}'
# edit: send the full record from GET /models with your changes. display_name, markup_percent,
# custom_input_price, custom_output_price and sort_order are overwritten by whatever you send;
# "enabled" only changes when present.
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/models/<id>" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{...full record..., "enabled": false}'
# 下架 for good: remove the record. PUT enabled:false hides it but keeps the configuration.
curl -s --max-redirs 0 -X DELETE "$BASE/api/distributor/models/<id>" -H "$AUTH" -H "$AUTH_USER"
定价规则
global_markup(通过PUT /self设置)是一个百分比 —30表示加价 30% — 适用于所有未设置单独价格的上架模型。- 每个模型可以设置
markup_percent(0= 使用全局值),或设置明确的custom_input_price/custom_output_price。自定义价格的单位是每百万个 token,使用上架信息中报告的price_currency货币;设置数值前请先读取该字段。 - 向用户展示的变更列表必须包含加价后的实际价格,而不只是百分比。
套餐 (packages)
curl -s --max-redirs 0 "$BASE/api/distributor/packages" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 -X POST "$BASE/api/distributor/packages" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"name":"入门包","description":"","type":"quota","price":99,"original_price":129,
"quota_amount":50000000,"duration":30,"quota_reset_period":"never","enabled":true,"sort_order":0}'
服务端规则:name 必填,最多 128 个字符;type 为 quota、daily、weekly 或 monthly;price 以 CNY 计价,必须 > 0;original_price ≥ 0(划线价);quota_amount > 0,单位为配额单位,500000 = $1——一个「$100 套餐」即为 50000000;duration 以天为单位,默认为 30;quota_reset_period 为 never、daily、weekly 或 monthly。
PUT /api/distributor/packages/<id> 会替换所有字段——请从 GET 获取完整对象,应用修改后发送。DELETE /api/distributor/packages/<id> 会将其删除。
已售套餐:GET /api/distributor/package-subscriptions 会列出这些套餐;POST /package-subscriptions/<id>/reset-usage 和 POST /package-subscriptions/<id>/invalidate 会改变付费客户获得的内容——请说明客户姓名和操作影响,然后征得同意。
站点设置
PUT /api/distributor/self 是部分更新:只有你发送的字段会发生变化。站点所有者通常会提出修改的字段包括:name、domain、logo、favicon、announcement、display_mode(simple 或 full)、registration_mode(open、invite、closed)、global_markup、enable_all_models、enable_topup、hide_provider_info、currency_display。读取 GET /self,逐项展示每个键的旧值 → 新值,然后再写入。
切勿设置支付、邮件或 OAuth 凭据字段(epay_*、stripe_*、creem_*、smtp_*、*_oauth_client_id、*_oauth_client_secret、theme_password)——这些应由站点所有者在控制台中填写。
路由来源和加价设置位于同一端点,并决定站点的收入,因此修改前后都要读取这些设置:
| 字段 | 默认值 | 影响 |
|---|---|---|
include_official_channels | true | 站点密钥是否可以路由到平台官方渠道 |
include_shared_subscriptions | true | 站点密钥是否可以路由到订阅共享线路 |
include_provider_self | true | 站点密钥是否可以路由到供应商自己的列表 |
include_cache_hit_rate | true | 排序候选项时是否考虑缓存命中率 |
subrouter_route_preference | 平台默认值 | 候选项排序方式,例如 price_first、stability_first |
shared_subscription_markup | 0 | 应用于订阅共享路由的加价,0-10000(百分比) |
除 shared_subscription_markup 外,上表中的每一项只有在 display_mode 为 simple 时才会生效。在 full 模式下,站点设置值会被忽略,每个密钥各自保存一份,因此写入站点设置会返回成功,但不会改变路由。
关闭某个来源会移除所有仅由该来源提供的模型——这些调用会开始对客户失败,而不是变得更有利可图。写入前请说明哪些模型会消失。
客户、密钥组、订阅共享、官方渠道
# Customers (the station's end users)
curl -s --max-redirs 0 "$BASE/api/distributor/customers?page=1&page_size=20&keyword=" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/distributor/customers/<userId>/dashboard" -H "$AUTH" -H "$AUTH_USER"
# status 1 = enabled, 2 = banned — confirm with the owner first
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/customers/<userId>/status" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"status":2}'
# Key groups (bundles of listed models sold under one label)
curl -s --max-redirs 0 "$BASE/api/distributor/key-groups" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/distributor/key-groups/models" -H "$AUTH" -H "$AUTH_USER" # models a group may include
# POST /key-groups and PUT /key-groups/<id> take the group record: name, vendor_category, description,
# model_limits (comma-separated, only models from /key-groups/models), subrouter_providers (comma-separated slugs),
# price_discount (0.8 = 20% off), is_private, is_recommended, enabled, sort_order.
# Members of a private group: GET/POST /key-groups/<id>/users, DELETE /key-groups/<id>/users/<userId>
# Shared subscriptions (进货 a shared plan, then list it)
curl -s --max-redirs 0 "$BASE/api/distributor/shared-subscriptions" -H "$AUTH" -H "$AUTH_USER"
# PUT /shared-subscriptions/<id> {"purchased":true,"enabled":true,"markup_percent":0,"sort_order":0}
# "purchased": true records the procurement and activates the plan on the owner's account — confirm first;
# "enabled" cannot be true unless "purchased" is.
# PUT /shared-subscriptions/<id>/models {"model_name":"…","enabled":true,"markup_percent":0,"sort_order":0}
# (also custom_input_price / custom_output_price / custom_fixed_price / custom_price_multiplier)
# PUT /shared-subscriptions/<id>/models/bulk-status — enable or disable many at once
# Official channels offered by the platform
curl -s --max-redirs 0 "$BASE/api/distributor/official-channels" -H "$AUTH" -H "$AUTH_USER"
# PUT /official-channels/<id> — GET first to learn the record shape
用于回答「站点运营情况如何」等问题的只读端点:GET /logs(cursor=first,然后使用上一次的 next_cursor、page_size、type、start_timestamp、end_timestamp——最多 31 天,默认为今天——、model_name、username、token_name)、/logs/stat(时间范围规则相同)、/earnings、/earnings/summary、/earnings/payouts、/topups、/redemptions、/invoices、/sub-distributors、/package-fund。
绝不能调用的 Station 端点
这些操作会转移资金或处理凭据。请说明这一点,并引导所有者前往 $BASE/console/distributor:
POST /earnings/withdraw、POST /package-fund/topup、POST /package-fund/withdraw、PUT /package-fund/settingsPOST /redemptions— 创建兑换码会发放余额PUT /customers/<userId>/password、PUT /customers/<userId>/commission、PUT /customers/<userId>/commission-application/saas-activation-token(GET、rotate、DELETE)、POST /smtp/test、PUT /invoices/<id>
在 /api/distributor 下执行任何其他写入操作前,都要准确列出所有将发生的变更——包括模型名称及生效价格、套餐字段、设置项键的旧值 → 新值、客户 ID,并获得明确同意。批量上架或下架时,必须先列出完整的模型清单,不能只给出数量。
更多功能(此处未详细介绍)
该平台还提供面向终端用户的共享订阅、官方密钥市场、发票、2FA/通行密钥管理和付款发起功能。这些功能涉及凭据、购买或账户安全——不要通过 API 操作。用户询问时,请引导他们前往 Web 控制台($BASE/console)。
故障排查
- 401 Unauthorized — 密钥缺失或错误,或
sk-前缀被去掉。重新检查你写入的配置。 - 402 / quota messages — 账户余额不足;用户需要充值(第 4 步)。
- 429 — 请求受限;请等待一段时间后,以指数级递增的延迟重试。
- Model not found — 模型名称必须与
GET /api/pricing完全一致;不要自行编造名称。
破坏性操作
删除令牌、更改账户设置、/console 管理 API 下的任何操作,以及 /api/distributor 下的所有写入操作:始终先征求用户确认。