Authentication
所有受保护 endpoint 都需要 Authorization: Bearer sk-xa-{env}-{base62}。key 由后台生成或 Telegram 客服直接发,数据库里存的是 HMAC 摘要(不是明文)。每个 key 带四个属性:允许的模型列表、IP 白名单、月预算上限、归属租户 — 调用前会逐项检查,不符合直接拒。
Common request headers
| 字段 | 类型 | 说明 | |
|---|---|---|---|
| Authorization | string | 必填 | Bearer sk-xa-{env}-{base62}。env 取值 dev/stg/prod。 |
| Content-Type | string | 必填 | POST 必须 application/json,/v1/files 上传为 multipart/form-data。 |
| X-Request-Id | string | 客户端自带的 trace id。未提供时 SACTL 会生成 UUIDv7 并回填到响应头。 | |
| anthropic-beta | string | 仅对 /v1/messages / Files / Batches 起作用,原样透传给 Anthropic。 |
|
| anthropic-version | string | SACTL 在出站请求处统一锁定 2023-06-01(客户端传值会被覆盖),保持与 Anthropic SDK / Claude Code 默认值一致。 |
Response headers
所有 2xx 响应都会携带 SACTL 计量 header。前端可以直接读 X-SACTL-Usage-Cost-USD 做前端账本,不需要再调用 /v1/usage。
| Header | 类型 | 何时 | 说明 |
|---|---|---|---|
| X-SACTL-Usage-Prompt-Tokens | int | 2xx | 本次 prompt tokens 数(按 upstream 报告回来的值计)。 |
| X-SACTL-Usage-Completion-Tokens | int | 2xx | 本次 completion tokens 数。 |
| X-SACTL-Usage-Cost-USD | decimal | 2xx | 本次消耗美元,按 SACTL 折扣表(Claude 全系 3 折;GPT 低至官方 1/30 已支持;Gemini 即将支持)结算,保留 6 位小数。 |
| X-SACTL-Budget-Remaining-USD | decimal | 有 cap | VK 配置了月度 cap 时出现,表示当月剩余额度。 |
| Retry-After | int | 429 | GCRA 限流命中时返回,单位秒,按 bucket 恢复速率计算。 |
Error envelope
所有 4xx/5xx 统一为下面的信封结构。不透传上游原文 error body —— 上游凭证、内部 URL、trace 细节绝不泄露给客户端。trace_id 可用于工单回溯。
{
"error": {
"code": "key_invalid",
"message": "virtual key is invalid or revoked",
"trace_id": "01JX7W3Z5A8M9E2Q1P4K6R8TVB"
}
}Registered error codes
| HTTP | code | 触发条件 |
|---|---|---|
| 401 | key_invalid | VK 缺失、错误、或已 revoke。 |
| 403 | model_forbidden | 请求的 model 不在 VK 的 allowed_models 白名单中。 |
| 403 | ip_forbidden | 请求 IP 不在 VK 的 IP 白名单中。 |
| 403 | signature_invalid | VK HMAC 校验失败(例如 pepper 不对齐)。 |
| 402 | budget_exhausted | DoW 预扣判定此调用会让 VK 超过月度美元 cap。 |
| 402 | context_too_long | prompt tokens 超过 pricing registry 登记的该模型 context window。 |
| 429 | rate_limited | GCRA 限流命中(tenant / vk / vk×model / vk×ip 四维任意一维)。 |
| 400 | ssrf_forbidden | 多模态 image_url 指向内网/私有地址,被 SSRF 过滤器拦截。 |
| 400 | model_unknown | 模型 ID 在 pricing registry 查不到。 |
| 400 | bad_request | JSON 格式错误、必填字段缺失。 |
| 413 | payload_too_large | 请求体超过 SIDECAR_MAX_BODY_MB(默认 10MB,可调 1..100)。 |
| 415 | unsupported_media | multipart 上传 mime 不在白名单。 |
| 502 | upstream_error | Anthropic 或 OpenAI 返回 5xx。(Gemini 上游即将支持。) |
| 504 | upstream_timeout | 上游请求超时。 |
| 500 | internal_error | 网关内部异常。 |
| 503 | service_unavailable | 多 key 池全部在冷却期(熔断器)。preview |
Inference
核心推理 endpoint —— Anthropic Messages 原生协议 + OpenAI 兼容路径(OpenAI SDK 零客户端改动直连 Claude 或 GPT)。Claude + GPT 上游已接通;Gemini 即将支持。
Create message (Anthropic native)
Anthropic 原生 Messages endpoint。请求体和响应体兼容官方 Anthropic Messages API(Claude Code 用的就是这个端点)。我们只在前面加了认证、用量限制、限流、计费日志几层,不改请求体语义。
Headers
| 字段 | 类型 | 说明 | |
|---|---|---|---|
| Authorization | string | 必填 | Bearer sk-xa-... |
| Content-Type | string | 必填 | application/json |
| anthropic-beta | string | 多个 beta 用逗号分隔。透传给 Anthropic。 | |
| X-Request-Id | string | 客户端 trace id。 |
Request body
| 字段 | 类型 | 说明 | |
|---|---|---|---|
| model | string | 必填 | 模型 ID,必须在 VK 的 allowed_models 内,见 GET /v1/models。 |
| max_tokens | int | 必填 | 上限由 pricing registry 查该模型的 token window。 |
| messages | array | 必填 | Anthropic 消息数组,role 为 user / assistant,content 可为字符串或 block 数组(含 text / image / tool_use / tool_result)。 |
| system | string | array | system prompt。数组形式可带 cache_control。 | |
| temperature | number | 0.0 - 1.0。 | |
| top_p | number | 核采样。 | |
| top_k | int | top-k 采样。 | |
| stop_sequences | string[] | 自定义停止词。 | |
| tools | array | Anthropic 原生 tools 格式。 | |
| tool_choice | object | {type: "auto"|"any"|"tool", name?: "..."} | |
| stream | bool | 默认 false。true 返回 SSE(text/event-stream),事件名为 Anthropic 原生(message_start / content_block_delta / ...)。 | |
| thinking | object | {type: "enabled", budget_tokens: 32000} extended thinking。若未显式设置 anthropic-beta,SACTL 自动注入。 | |
| cache_control | object | 嵌在 content block / tools / system 内,{type: "ephemeral"}。SACTL 原样透传给 Anthropic,命中按 cache write/read 单价计费。 | |
| metadata | object | {user_id: "..."} 原样透传,落审计。 |
Request example
{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "Hello, Claude." }
]
}Response
HTTP/1.1 200 OK
X-SACTL-Usage-Prompt-Tokens: 12
X-SACTL-Usage-Completion-Tokens: 28
X-SACTL-Usage-Cost-USD: 0.000336
X-SACTL-Budget-Remaining-USD: 49.832104
{
"id": "msg_01AbCdEfGhIjKlMnOpQrStUv",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-4-6",
"content": [
{ "type": "text", "text": "Hello! How can I help you today?" }
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 12,
"output_tokens": 28
}
}curl
curl https://api.sactl.ai/v1/messages \ -H "Authorization: Bearer YOUR_VK" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [ { "role": "user", "content": "Hello, Claude." } ] }'
Errors
| HTTP | code | 场景 |
|---|---|---|
| 401 | key_invalid | VK 缺失或已 revoke。 |
| 403 | model_forbidden | model 不在 allowlist。 |
| 402 | budget_exhausted | DoW 预扣超限。 |
| 402 | context_too_long | prompt 超 token window。 |
| 429 | rate_limited | GCRA 命中,带 Retry-After。 |
| 502 | upstream_error | Anthropic 返回 5xx。 |
| 504 | upstream_timeout | 上游超时。 |
创建 embeddings
Embeddings(OpenAI 兼容 schema)。Anthropic 不提供 embeddings,此 endpoint 路由到 GPT(text-embedding-3-small / text-embedding-3-large)上游。Gemini(text-embedding-004)即将支持。按 token 实扣,跟推理 endpoint 共用 budget / rate-limit 体系。
请求体
| 字段 | 类型 | 说明 | |
|---|---|---|---|
| model | string | 必填 | Embedding 模型 ID。 |
| input | string | array | 必填 | 单个字符串或字符串数组。 |
| encoding_format | string | "float"(默认)/ "base64"。 | |
| dimensions | int | 仅 text-embedding-3-* 系列有效,截断 embedding 维度。 |
响应
{
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [0.0023, -0.0091, 0.0412, /* ... 1536 floats */]
}
],
"model": "text-embedding-3-small",
"usage": {
"prompt_tokens": 7,
"total_tokens": 7
}
}curl
curl https://api.sactl.ai/v1/embeddings \ -H "Authorization: Bearer YOUR_VK" \ -H "Content-Type: application/json" \ -d '{ "model": "text-embedding-3-small", "input": "The quick brown fox jumps over the lazy dog." }'
错误
| HTTP | code | 场景 |
|---|---|---|
| 401 | key_invalid | VK 无效。 |
| 400 | model_unknown | 模型 ID 未注册。 |
| 413 | payload_too_large | input 数组过大。 |
| 429 | rate_limited | 命中限流。 |
List models
列出你的 VK 有权限调用的所有模型。返回 Anthropic 原生 shape {data: [{id, type, ...}]}。这个列表由 VK 的 allowed_models 数组交集 pricing registry 动态生成 —— 即使 pricing registry 登记了某个模型,如果 VK 没开通该模型,它不会出现。
Response
{
"data": [
{ "type": "model", "id": "claude-opus-4-7", "display_name": "Claude Opus 4.7", "created_at": "2025-09-29T00:00:00Z" },
{ "type": "model", "id": "claude-opus-4-6", "display_name": "Claude Opus 4.6", "created_at": "2025-08-05T00:00:00Z" },
{ "type": "model", "id": "claude-sonnet-4-6", "display_name": "Claude Sonnet 4.6", "created_at": "2025-07-10T00:00:00Z" },
{ "type": "model", "id": "claude-haiku-4-5", "display_name": "Claude Haiku 4.5", "created_at": "2025-05-03T00:00:00Z" }
],
"has_more": false,
"first_id": "claude-opus-4-7",
"last_id": "claude-haiku-4-5"
}
# GPT 模型(gpt-4o / gpt-4o-mini / text-embedding-3-* 等)已加入此列表(与 VK allowed_models 取交集)。Gemini 上游接入后自动出现。curl
curl https://api.sactl.ai/v1/models \
-H "Authorization: Bearer YOUR_VK"Files API
Anthropic Files API 透传。用于上传文档 / 图片供后续 /v1/messages 引用。最大文件大小由 SIDECAR_FILES_MAX_MB(默认 100MB)控制,MIME 白名单可配。所有 Files 操作会写一条计费日志(file.uploaded / file.deleted),便于对账。
Upload file
上传文件。multipart/form-data。文件 id 随后可在 /v1/messages 的 content block 里通过 {type: "document", source: {type: "file", file_id: "..."}} 引用。
Form fields
| 字段 | 类型 | 说明 | |
|---|---|---|---|
| file | file | 必填 | 文件二进制。MIME 白名单:application/pdf, image/png, image/jpeg, image/gif, image/webp, text/plain, text/csv, text/markdown。 |
| purpose | string | 可选标签,落审计。 |
Response
{
"id": "file_01AbCdEfGhIjKlMnOpQrStUv",
"type": "file",
"filename": "contract.pdf",
"mime_type": "application/pdf",
"size_bytes": 183204,
"created_at": "2026-04-20T08:12:31.441Z",
"downloadable": false
}curl
curl https://api.sactl.ai/v1/files \ -H "Authorization: Bearer YOUR_VK" \ -F "[email protected];type=application/pdf" \ -F "purpose=context"
Errors
| HTTP | code | 场景 |
|---|---|---|
| 401 | key_invalid | VK 无效。 |
| 413 | payload_too_large | 文件超过 SIDECAR_FILES_MAX_MB(默认 100MB)。 |
| 415 | unsupported_media | MIME 不在白名单。 |
List files
列出当前 VK 上传过的文件(其它 VK 的文件不可见)。分页基于 cursor。
Query params
| 字段 | 类型 | 说明 | |
|---|---|---|---|
| limit | int | 1 - 1000,默认 20。 | |
| before_id | string | cursor,返回此 id 之前的文件。 | |
| after_id | string | cursor,返回此 id 之后的文件。 |
Response
{
"data": [
{ "id": "file_01AbCd...", "filename": "contract.pdf", "size_bytes": 183204, "mime_type": "application/pdf", "created_at": "2026-04-20T08:12:31.441Z" }
],
"has_more": false,
"first_id": "file_01AbCd...",
"last_id": "file_01AbCd..."
}curl
curl "https://api.sactl.ai/v1/files?limit=20" \ -H "Authorization: Bearer YOUR_VK"
Get file metadata
读取单个文件的元数据。SACTL 不落文件二进制本身,本 endpoint 返回的是 Anthropic 上游的 metadata 透传。
curl
curl https://api.sactl.ai/v1/files/file_01AbCdEfGhIjKlMnOpQrStUv \
-H "Authorization: Bearer YOUR_VK"Errors
| HTTP | code | 场景 |
|---|---|---|
| 401 | key_invalid | VK 无效。 |
| 404 | bad_request | file id 不存在或不属于本 VK。 |
Delete file
删除文件。删除成功后 /v1/messages 引用该 file_id 的请求会得到 bad_request。审计会 emit file.deleted。
Response
{
"id": "file_01AbCdEfGhIjKlMnOpQrStUv",
"type": "file_deleted"
}curl
curl -X DELETE https://api.sactl.ai/v1/files/file_01AbCdEfGhIjKlMnOpQrStUv \
-H "Authorization: Bearer YOUR_VK"MCP Servers API
每个 VK 可以挂载若干 MCP server(filesystem / fetch / 自定义 tool 链)。SACTL 把 server 配置存在 admin-api 的 mcp_servers 表,Anthropic Messages 调用前 sidecar 拼到 mcp_servers 字段透传给上游。OAuth-token 类 server 需先调 /auth-url 拿到授权链接由用户完成绑定。
Create MCP server
注册一个新的 MCP server 配置。type 取 url(直连 SSE)、stdio(本地命令)或 oauth(需先走 /auth-url 拿 token)。
curl
curl -X POST https://api.sactl.ai/v1/mcp_servers \ -H "Authorization: Bearer YOUR_VK" \ -H "Content-Type: application/json" \ -d '{"name":"fs","type":"url","url":"https://mcp.example.com/sse"}'
List MCP servers
列出当前 VK 配置的所有 MCP server。limit 默认 1000(与 Anthropic 上游一致)。
curl
curl "https://api.sactl.ai/v1/mcp_servers?limit=1000" \ -H "Authorization: Bearer YOUR_VK"
Retrieve MCP server
读取单个 MCP server 配置。
curl
curl https://api.sactl.ai/v1/mcp_servers/mcp_01AbCd... \
-H "Authorization: Bearer YOUR_VK"Generate OAuth auth URL
对 type=oauth 的 MCP server,返回授权链接,用户走完之后回调写回 token。
curl
curl https://api.sactl.ai/v1/mcp_servers/mcp_01AbCd.../auth-url \
-H "Authorization: Bearer YOUR_VK"Update MCP server
局部更新 server 配置。仅支持改 name / url / headers;type 不可变。
curl
curl -X PATCH https://api.sactl.ai/v1/mcp_servers/mcp_01AbCd... \ -H "Authorization: Bearer YOUR_VK" \ -H "Content-Type: application/json" \ -d '{"name":"fs-prod"}'
Delete MCP server
删除 server 配置。已 in-flight 的会话不受影响。
curl
curl -X DELETE https://api.sactl.ai/v1/mcp_servers/mcp_01AbCd... \
-H "Authorization: Bearer YOUR_VK"Batch API
大量 Messages 请求打包提交,状态机:validating → in_progress → ended | canceled | failed。结果以 JSONL 返回,只在 ended 状态可下载。
计费:批量调用按官方 50% 折扣再叠加 SACTL 3 折,Claude 批量价 = 官方 × 0.15(见 定价页)。计费结算由后台 poller 在 batch 进入 ended 状态时触发(默认 5 分钟扫一轮),canceled / failed 的 request 不计费。
Create message batch
提交一批 Messages 请求。每条 request 与单次 /v1/messages 的 body 等价,外层加 custom_id 用于对账。
Request body
| 字段 | 类型 | 说明 | |
|---|---|---|---|
| requests | array | 必填 | 1 - 10,000 个 {custom_id, params} 条目。 |
| requests[].custom_id | string | 必填 | 批内唯一,用于把结果对回业务 id。 |
| requests[].params | object | 必填 | 与 /v1/messages 请求体同构(model / max_tokens / messages / ...)。 |
Request example
{
"requests": [
{
"custom_id": "job-001",
"params": {
"model": "claude-haiku-4-5",
"max_tokens": 256,
"messages": [ { "role": "user", "content": "Summarize Go channels." } ]
}
},
{
"custom_id": "job-002",
"params": {
"model": "claude-haiku-4-5",
"max_tokens": 256,
"messages": [ { "role": "user", "content": "What is GCRA?" } ]
}
}
]
}Response
{
"id": "msgbatch_01Wx9...",
"type": "message_batch",
"processing_status": "in_progress",
"request_counts": { "processing": 2, "succeeded": 0, "errored": 0, "canceled": 0, "expired": 0 },
"ended_at": null,
"created_at": "2026-04-20T08:12:31.441Z",
"expires_at": "2026-04-21T08:12:31.441Z",
"cancel_initiated_at": null,
"results_url": null
}curl
curl https://api.sactl.ai/v1/messages/batches \ -H "Authorization: Bearer YOUR_VK" \ -H "Content-Type: application/json" \ -d @batch.json
List message batches
列出当前 VK 的所有 batch。支持 limit / before_id / after_id cursor 分页。
curl
curl "https://api.sactl.ai/v1/messages/batches?limit=20" \ -H "Authorization: Bearer YOUR_VK"
Retrieve message batch
读取单个 batch 的状态。processing_status 在 validating → in_progress → ended / canceled / failed 之间流转。
Response
{
"id": "msgbatch_01Wx9...",
"processing_status": "ended",
"request_counts": { "processing": 0, "succeeded": 2, "errored": 0, "canceled": 0, "expired": 0 },
"ended_at": "2026-04-20T08:14:02.112Z",
"results_url": "https://api.sactl.ai/v1/messages/batches/msgbatch_01Wx9.../results"
}curl
curl https://api.sactl.ai/v1/messages/batches/msgbatch_01Wx9... \
-H "Authorization: Bearer YOUR_VK"Retrieve message batch results
以 JSONL (application/x-ndjson) 返回每条 request 的结果。仅在 processing_status=ended 时可用,其它状态返回 bad_request。
Response (one line per request)
{"custom_id":"job-001","result":{"type":"succeeded","message":{"id":"msg_...","content":[...],"usage":{...}}}}
{"custom_id":"job-002","result":{"type":"succeeded","message":{"id":"msg_...","content":[...],"usage":{...}}}}curl
curl https://api.sactl.ai/v1/messages/batches/msgbatch_01Wx9.../results \
-H "Authorization: Bearer YOUR_VK"Cancel message batch
把一个 in_progress / validating 的 batch 迁移到 canceling。已 in-flight 的 request 仍可能完成,完成的计费正常,取消的 request 不计费。
curl
curl -X POST https://api.sactl.ai/v1/messages/batches/msgbatch_01Wx9.../cancel \
-H "Authorization: Bearer YOUR_VK"System
健康检查和 metrics。不需要 VK,但生产部署应在 ingress 层限制只对运维网段开放。
Health check
健康检查。返回 200 + JSON,字段展示各依赖的连通性。Kubernetes liveness / readiness 可直接用此端点。无需 auth。
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"status": "ok",
"deps": {
"redis": "ok",
"vault": "ok"
}
}任一依赖不可达时,status 退化为 degraded,HTTP 状态码升至 503,Kubernetes liveness 直接据此剔除 pod。
curl
curl https://api.sactl.ai/health
Prometheus metrics
Prometheus metrics endpoint。返回 text/plain 格式的所有业务 metric。所有 series 以 sactl_gateway_* 为前缀(熔断状态用 sactl_breaker_*)。挂在 Prometheus scrape config 里即可。
Response (节选)
# HELP sactl_gateway_ratelimit_rejected_total Requests rejected by GCRA rate limiter # TYPE sactl_gateway_ratelimit_rejected_total counter sactl_gateway_ratelimit_rejected_total{dimension="tenant"} 12 sactl_gateway_ratelimit_rejected_total{dimension="vk"} 3 sactl_gateway_ratelimit_rejected_total{dimension="vk_model"} 1 sactl_gateway_ratelimit_rejected_total{dimension="vk_ip"} 0 # HELP sactl_gateway_request_duration_seconds Request duration histogram # TYPE sactl_gateway_request_duration_seconds histogram sactl_gateway_request_duration_seconds_bucket{route="/v1/messages",le="0.5"} 4821 sactl_gateway_request_duration_seconds_bucket{route="/v1/messages",le="1"} 5910 sactl_gateway_request_duration_seconds_bucket{route="/v1/messages",le="+Inf"} 6024 # HELP sactl_gateway_upstream_errors_total Upstream 5xx / timeout / parse errors # TYPE sactl_gateway_upstream_errors_total counter sactl_gateway_upstream_errors_total{provider="anthropic",kind="5xx"} 2 # HELP sactl_breaker_open Per-provider breaker open=1 closed=0 # TYPE sactl_breaker_open gauge sactl_breaker_open{provider="anthropic"} 0 # HELP sactl_gateway_ssrf_blocked_total Multimodal URL blocked by SSRF filter # TYPE sactl_gateway_ssrf_blocked_total counter sactl_gateway_ssrf_blocked_total 0
curl
curl https://api.sactl.ai/metrics
Preview / Coming soon
以下能力代码就绪但未挂热路径,或仍在灰度。生产客户请不要依赖它们的行为。
Multi-key pool PickMiddleware preview
单个 upstream provider 下挂多把 API key,按 health / cost / usage 选择调用对象,某把 key 被上游 429 / 401 时进入冷却窗口,所有 key 冷却时整个 provider 熔断并返回 503 service_unavailable。
- 当前状态:中间件代码已实现,未默认挂在 forward 路径上。
- 预期 GA:2026 Q2。挂入后不会改变 API 契约,只会把
503 service_unavailable从 "理论可能" 变成真实观察到。
Markup multiplier settle preview
对多层转售场景,支持给 VK 配置 markup_multiplier(例如 1.20 = 加 20% 渠道差价),结算时自动把差价归入渠道账户。
- 当前状态:schema 字段已落库,结算阶段尚未应用乘子,实际计费等于原价。
- 预期 GA:与 Multi-key pool 同批次 2026 Q2。
- 注意:这不会向客户端暴露新 endpoint,只会改变
X-SACTL-Usage-Cost-USDheader 的金额含义。