委托接口
用 OAuth 访问令牌代用户读取余额与授权额度、调用工具和模型服务;费用从用户余额扣除,受授权预算约束。
委托接口的基础地址是 https://tokentosea.com/api/v1/connect,每个请求都需要 Authorization: Bearer tts_at_…(OAuth 访问令牌),并且令牌所属授权必须包含接口要求的 scope。费用从授权用户的余额扣除,受该授权的预算约束。
前提:用户已通过授权流程授予所需 scope;付费调用需要用户设置了足够的累计预算与单次上限,且账户可用余额充足。
| 方法 | 路径 | 需要的 scope | 用途 |
|---|---|---|---|
GET | /grant | 任意 | 读取本授权的预算与剩余额度 |
POST | /grant/limits | grant:limits | 修改本授权的预算与单次上限 |
GET | /wallet | wallet:read | 用户的账户余额 |
GET | /tools | tools:read | 工具目录 |
GET | /tools/{id}/actions | tools:read | 工具操作与价格 |
POST | /tools/invoke | tools:invoke | 调用工具 |
GET | /tools/runs/{id} | tools:read | 本授权产生的工具调用 |
GET | /models | models:read | 委托模型目录 |
POST | /chat/completions | models:invoke | 调用委托模型 |
GET | /models/runs/{id} | models:read | 本授权产生的模型调用 |
鉴权错误#
| 状态码 | 响应 | 原因 |
|---|---|---|
401 | {"error":"invalid_token"} | 令牌缺失、格式错误、过期或已撤销;授权已失效;用户账号不可用或已离开工作区 |
401 | {"error":"insufficient_scope","scope":"tools:invoke"} | 授权不包含该接口需要的 scope |
访问令牌最长 15 分钟,收到 invalid_token 时先刷新再重试一次;刷新也失败时请用户重新授权。
授权额度#
GET /grant
{
"id": "cm3…",
"scopes": ["openid", "tools:read", "tools:invoke", "grant:limits"],
"budgetUsd": "20.000000",
"perCallUsd": "1.000000",
"spentUsd": "0.012000",
"reservedUsd": "0.001000",
"remainingUsd": "19.987000",
"expiresAt": "2026-11-20T08:00:00.000Z",
"limitsEditable": true,
"maxPerCallUsd": "100.000000",
"maxBudgetUsd": "1000.013000"
}| 字段 | 说明 |
|---|---|
budgetUsd / perCallUsd | 累计预算与单次上限 |
spentUsd / reservedUsd | 已结算金额与处理中的预留 |
remainingUsd | 剩余 = 预算 − 已花费 − 处理中 |
expiresAt | 授权到期时间(每次刷新令牌后顺延) |
limitsEditable | 是否包含 grant:limits |
maxPerCallUsd / maxBudgetUsd | 当前允许设置的最大值 |
修改额度#
POST /grant/limits
| 参数 | 类型 | 说明 |
|---|---|---|
perCallUsd | string | 单次上限,美元十进制字符串(最多 6 位小数),不超过 USD 100,且不超过 budgetUsd |
budgetUsd | string | 累计预算,不超过 maxBudgetUsd(已花费 + 处理中 + USD 1000) |
成功返回 200 与更新后的额度对象;不符合规则返回 400 invalid_budget。修改会写入审计记录,用户可以在控制台看到并随时调整或撤销。请只在用户明确操作时调用它。
余额#
GET /wallet
返回 { "balanceUsd", "reservedUsd", "availableUsd", "currency": "USD" },与工具 API 的余额相同。
工具#
GET /tools 与 GET /tools/{id}/actions 的参数和响应与工具 API 的 /catalog、/actions/{toolId} 完全相同。
调用工具#
POST /tools/invoke
请求体与工具 API 的 /invoke 相同:actionId、input、requestKey、priceVersion、maxCostUsd。区别:
- 调用前在本授权上预留操作价格:价格超过
perCallUsd,或超过剩余预算时返回409 oauth_budget_exceeded; requestKey在本授权内隔离,响应中的requestKey是平台转换后的值(以oauth:开头),请自行保存你生成的原始值;GET /tools/runs/{id}只能读取本授权产生的调用。
{
"statusCode": 409,
"error": "Conflict",
"message": "oauth_budget_exceeded",
"requiredUsd": "0.001000",
"perCallUsd": "0.000000",
"remainingUsd": "0.000000"
}收到这个错误时,提示用户在控制台提高本应用的额度,或(持有 grant:limits 时)引导用户在你的应用内确认新的额度。
模型#
GET /models
委托模型目录只包含模型市场中由卖家发布、经平台审核的对话服务,不包含 api.tokentosea.com 的官方直供型号。响应为 { "object": "list", "data": [ … ] },每项包含:
| 字段 | 说明 |
|---|---|
id | 服务 ID,调用时作为 model |
name / description / provider / model / seller | 服务名称、说明、上游服务商、上游型号与卖家 |
billingMode | request(按成功请求计费,价格为 priceUsd)或 token(按 token 计费,价格为 inputPriceUsdPerMillion / outputPriceUsdPerMillion) |
priceVersion | 价格版本,调用时原样传回 |
maxOutputTokens | 单次最大输出 |
streaming / toolCalling | 是否支持流式、函数调用(仅 token 计费的服务) |
reasoning | 支持思考的服务给出 { efforts, default, maxOutputTokens },否则为 null |
调用模型#
POST /chat/completions
| 请求头 | 必填 | 说明 |
|---|---|---|
Idempotency-Key | 必填 | 16–128 个字符;也可在请求体中用 requestKey 提供。键在本授权内隔离 |
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | string | 必填 | /models 返回的服务 id |
messages | array | 必填 | role 为 system、user、assistant、tool;content 为字符串(带 tool_calls 的 assistant 消息可为 null)。最多 80 条、200,000 字节(开启思考时 120 条、1,000,000 字节) |
max_tokens | integer | 可选 | 默认 1024,1–4096(开启思考时最多 65,536),不超过服务的 maxOutputTokens |
stream | boolean | 可选 | 仅 token 计费的服务支持 |
tools / tool_choice | array / string | 可选 | 仅支持函数调用的服务:最多 30 个 function 工具、合计 100,000 字节;tool_choice 为 auto、none、required |
reasoning_effort | string | 可选 | 仅 reasoning 不为空的服务:off、low、high、max(none 等同 off,medium 等同 high) |
priceVersion | string | 必填 | 服务当前的价格版本 |
maxCostUsd | string | 必填 | 本次最多支付的金额,最多 USD 100。request 计费时不能低于 priceUsd;token 计费时不能低于按输入长度与 max_tokens 估算的预留额,否则返回 409 model_cost_limit_too_low(含 requiredUsd) |
成功时返回 201:上游的 OpenAI 兼容响应体,model 为服务 ID,并附带:
{ "tokentosea": { "runId": "cm4…", "chargedUsd": "0.000420" } }调用失败(例如上游出错)同样返回 201,响应体为调用记录 { "id", "model", "status": "failed", "chargedUsd": "0.000000", "output": null, "errorCode", "createdAt" },不扣费。GET /models/runs/{id} 返回同样结构的调用记录。
流式响应为 SSE:数据块实时转发;最后一个数据块包含 usage 与 tokentosea;失败时最后一帧为 {"error":{"code":"…"},"tokentosea":{…}};结束于 data: [DONE]。开启思考的调用会收到 SSE 注释形式的保活行。
| 状态码 | message | 原因 |
|---|---|---|
400 | model_idempotency_key_required | 缺少 Idempotency-Key |
400 | model_invalid_messages、model_invalid_max_tokens、model_invalid_tools、model_invalid_tool_choice、model_invalid_reasoning_effort | 参数不合法 |
400 | model_output_limit、model_token_pricing_required、model_tool_calling_unavailable、model_reasoning_unsupported | 超出服务能力(输出上限、流式、函数调用、思考) |
409 | model_unavailable、model_price_changed | 服务已下架,或价格版本已变化 |
409 | model_cost_limit_too_low、model_insufficient_balance | maxCostUsd 过低,或用户余额不足(含 requiredUsd) |
409 | oauth_budget_exceeded | 超出授权预算或单次上限 |
409 | model_rate_limited | 每个账号每分钟最多 20 次、同时最多 2 个调用 |
409 | model_request_key_reused、model_request_not_replayable | 同一个键对应了不同请求;或流式重放的原调用未成功 |
403 | oauth_grant_invalid | 授权已失效 |
示例#
TOKEN="tts_at_…"
API=https://tokentosea.com/api/v1/connect
# 剩余额度
curl "$API/grant" -H "Authorization: Bearer $TOKEN"
# 调用免费的 UUID 工具
curl "$API/tools/invoke" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"actionId": "utility.uuid", "input": {}, "requestKey": "'"$(uuidgen)"'", "priceVersion": "public-20261010-v1", "maxCostUsd": "0"}'import uuid
import requests
API = "https://tokentosea.com/api/v1/connect"
def call_tool(access_token: str, tool_id: str, action_id: str, tool_input: dict) -> dict:
headers = {"Authorization": "Bearer " + access_token}
actions = requests.get(f"{API}/tools/{tool_id}/actions", headers=headers, timeout=30)
actions.raise_for_status()
action = next(a for a in actions.json() if a["id"] == action_id)
response = requests.post(
f"{API}/tools/invoke",
headers=headers,
json={
"actionId": action_id,
"input": tool_input,
"requestKey": str(uuid.uuid4()), # 保存它,网络重试时沿用
"priceVersion": action["priceVersion"],
"maxCostUsd": action["priceUsd"],
},
timeout=60,
)
if response.status_code == 409 and response.json().get("message") == "oauth_budget_exceeded":
raise RuntimeError("授权额度不足,请用户在 TokenToSea 控制台提高额度")
response.raise_for_status()
return response.json()
print(call_tool("tts_at_…", "public_hn", "hn.top", {"limit": 3}))import { randomUUID } from 'node:crypto';
const API = 'https://tokentosea.com/api/v1/connect';
export async function callTool(accessToken, toolId, actionId, input) {
const headers = { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' };
const actions = await (await fetch(`${API}/tools/${toolId}/actions`, { headers })).json();
const action = actions.find((a) => a.id === actionId);
const response = await fetch(`${API}/tools/invoke`, {
method: 'POST',
headers,
body: JSON.stringify({
actionId,
input,
requestKey: randomUUID(), // 保存它,网络重试时沿用
priceVersion: action.priceVersion,
maxCostUsd: action.priceUsd,
}),
});
const body = await response.json();
if (response.status === 409 && body.message === 'oauth_budget_exceeded') throw new Error('授权额度不足,请用户在 TokenToSea 控制台提高额度');
if (!response.ok) throw new Error(`HTTP ${response.status}: ${JSON.stringify(body)}`);
return body;
}
console.log(await callTool('tts_at_…', 'public_hn', 'hn.top', { limit: 3 }));