API —原生集成
网页界面所做的一切都只是普通的 HTTP + SSE API。凡是你能点击完成的操作,都能从自己的代码里完成: 运行智能体、流式接收回答、查询知识库、创建智能体、调度任务、获取结果。
| 基地址 | https://ai.syctra.com/api/v1 |
| 传输 | 仅 HTTPS(HTTP 会被重定向)。TLS 1.2+ |
| 编码 | JSON、UTF-8。所有写操作需 Content-Type: application/json |
| 流式 | Server-Sent Events(text/event-stream),可断点续传 |
| 认证 | Authorization: Bearer <access_token> |
| 版本 | /v1 前缀保持稳定。可能新增字段,但不会在同一前缀下移除已有字段 |
1. 认证
有两种凭据,请选对:
| API 密钥 | 邮箱 + 密码 | |
|---|---|---|
| 适用 | 服务器、脚本、集成、CI | 由真人登录的交互式应用 |
| 获取 | 设置 → 🔑 API 密钥 → 新建密钥 | POST /auth/register 再 /auth/login |
| 形态 | sk_live_… | JWT 对(访问 + 刷新) |
| 有效期 | 直到你吊销 | 访问 15 分钟 · 刷新 30 天 |
切勿把 API 密钥下发到浏览器、移动端或公开仓库。 它拥有创建它的账号的全部权限。任何运行在 客户端设备上的代码都应调用你自己的后端,由后端持有密钥。
1.1 API 密钥 → 访问令牌
API 密钥不是 bearer 令牌:你用它换取一个短期访问令牌,再用该令牌调用任意接口。
export SYCTRA_API_KEY="sk_live_…"
TOKEN=$(curl -sS -X POST https://ai.syctra.com/api/v1/auth/token \
-H "X-API-Key: $SYCTRA_API_KEY" | jq -r .access_token)
POST /auth/token 是唯一接受密钥本身的接口。它支持三种传法 —— X-API-Key: sk_live_…、
Authorization: Bearer sk_live_…,或请求体 {"api_key": "sk_live_…"} —— 返回:
{ "access_token": "eyJhbGciOiJSUzI1NiIs…", "token_type": "Bearer", "expires_in": 900 }
在 expires_in(900 秒)内缓存令牌,过期后再换一次。不要每个请求都换一次:该接口限制为
每个 IP 每分钟 60 次兑换。
1.2 管理密钥
| 方法 | 路径 | 用途 |
|---|---|---|
POST | /api-keys | 创建密钥。{"name": "生产服务器"}。明文只在这次响应中出现 |
GET | /api-keys | 列出有效密钥 —— id、名称、前缀、created_at、last_used_at。绝不返回密文 |
DELETE | /api-keys/{key_id} | 吊销。立即阻止新的兑换 |
每个用户最多 20 个有效密钥。服务端只保存 sha256(密钥),密钥丢失即不可找回 —— 吊销后重新
创建。吊销会立刻阻断新的兑换;已经签发的访问令牌在其有效期内(≤ 15 分钟)仍然有效,与浏览器
退出登录的行为一致。
1.3 邮箱 + 密码(交互式应用)
curl -X POST https://ai.syctra.com/api/v1/auth/register \
-H 'content-type: application/json' \
-d '{"email":"dev@acme.com","password":"…"}' # → 邮件发送 6 位验证码
curl -X POST https://ai.syctra.com/api/v1/auth/verify \
-H 'content-type: application/json' \
-d '{"email":"dev@acme.com","code":"123456"}'
curl -X POST https://ai.syctra.com/api/v1/auth/login \
-H 'content-type: application/json' \
-d '{"email":"dev@acme.com","password":"…"}' # → { access_token, refresh_token }
用 POST /auth/refresh {"refresh_token": "…"} 续期。Google 与 OIDC/SAML 登录见
/auth/google/start 和 /auth/oidc/start。多次失败后登录按账号限流;注册按 IP 限量。
2. 快速上手 —— 提问并流式接收
模式始终一致:启动一次运行(run),然后订阅它的事件。运行是持久的 —— 进程挂了也可以 重新连接,从中断处继续。
# 1 — 启动
RUN=$(curl -sS -X POST https://ai.syctra.com/api/v1/chat \
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"message":"总结 2024 年营收趋势并作图","app_name":"data_copilot"}')
RUN_ID=$(echo "$RUN" | jq -r .run_id)
# 2 — 流式读取(可续传)
curl -N https://ai.syctra.com/api/v1/runs/$RUN_ID/events \
-H "Authorization: Bearer $TOKEN"
Python
import json, os, requests
BASE = "https://ai.syctra.com/api/v1"
token = requests.post(f"{BASE}/auth/token",
headers={"X-API-Key": os.environ["SYCTRA_API_KEY"]},
timeout=30).json()["access_token"]
H = {"Authorization": f"Bearer {token}"}
run = requests.post(f"{BASE}/chat", headers=H, timeout=30, json={
"message": "起草一份双向保密协议并导出为 DOCX",
"app_name": "legal_drafting_dz",
}).json()
answer, cursor = [], 0
with requests.get(f"{BASE}/runs/{run['run_id']}/events", headers=H, stream=True) as r:
event = None
for line in r.iter_lines(decode_unicode=True):
if not line:
continue
if line.startswith("id:"):
cursor = int(line[3:].strip()) # 请保留:这就是断点续传的位置
elif line.startswith("event:"):
event = line[6:].strip()
elif line.startswith("data:"):
payload = json.loads(line[5:].strip())
if event == "token":
answer.append(payload.get("text", ""))
elif event == "artifact":
print("文件:", payload.get("artifact_id"), payload.get("filename"))
elif event in ("done", "error"):
break
print("".join(answer))
Node
const BASE = 'https://ai.syctra.com/api/v1';
const { access_token } = await (await fetch(`${BASE}/auth/token`, {
method: 'POST', headers: { 'X-API-Key': process.env.SYCTRA_API_KEY },
})).json();
const { run_id } = await (await fetch(`${BASE}/chat`, {
method: 'POST',
headers: { authorization: `Bearer ${access_token}`, 'content-type': 'application/json' },
body: JSON.stringify({ message: '列出这份合同的风险', app_name: 'contract_review_dz' }),
})).json();
const res = await fetch(`${BASE}/runs/${run_id}/events`, {
headers: { authorization: `Bearer ${access_token}` },
});
for await (const chunk of res.body) process.stdout.write(Buffer.from(chunk).toString());
3. 约定
错误。 标准 HTTP 状态码,响应体为 JSON {"detail": "…"}。
| 状态码 | 含义 | 处理方式 |
|---|---|---|
400 | 请求格式错误 | 修正载荷;错误信息会指出字段 |
401 | 令牌缺失 / 过期 / 无效 | 重新兑换密钥 |
402 | 额度不足 | 充值;该运行未启动也未计费 |
403 | 已认证但无权限 | 组织不匹配、团队未获授权该智能体,或账号被停用 |
404 | 不存在或不属于你 | 所有 ID 均按组织隔离 |
409 | 冲突(如会话忙) | 查看响应体;排队状态在 POST /chat 中返回 |
429 | 触发限流 | 指数退避,若有 Retry-After 请遵守 |
5xx | 服务端错误 | 退避重试;带 idempotency_key 的运行是幂等的 |
幂等。 在 POST /chat 上传 idempotency_key —— 重放同一个 key 会返回原来的运行
(resumed: true),而不是再启动并再计费一次。
ID 为 UUID。时间戳 为 ISO-8601 UTC。在 POST /chat 中传 client_tz(IANA,如
"Asia/Shanghai"),智能体便会按你的本地时间推理。
限流。
| 面 | 限制 |
|---|---|
POST /chat 与运行 | 持续 20 请求/秒,突发 40,按用户 |
POST /auth/token | 60/分钟,按客户端 IP |
POST /auth/login | 多次失败后按账号限流 |
POST /auth/register | 按 IP 每小时限量 |
| 请求体 | 300 MB(含 base64 上传) |
并发。 运行按会话串行:上一次还在跑时发送第二条消息会被接收并在服务端排队,随后自动
执行(status: "queued",附 queue_seq)。需要并行请使用不同的 session_id。
4. 计费
每一次 AI 操作都会从组织余额中扣除额度 —— 与网页端显示的是同一份余额,无论请求来自浏览器、
API 调用、定时任务还是嵌入式挂件。每次运行都会发出带 token 计数的 usage 事件,
GET /balance 返回当前余额。余额不足的运行会在开始工作之前以 402 拒绝。
5. 安全
平台保证的部分:
- 密钥哈希存储(SHA-256)。明文只在创建响应里出现一次。
- 短期令牌。 访问令牌 15 分钟;停用账号或提升其 token 版本会使其会话失效并阻断后续兑换。
- 凭据脱敏。 智能体无法泄露平台机密:API 密钥、SMTP 密码、连接串和私钥会被一个确定性过滤器 从工具结果、工具错误和模型输出中剥离 —— 而不是靠一条可以被绕过的提示词规则。
- 提示注入隔离。 一旦某次运行摄入了不可信内容(上传的文件、抓取的网页、搜索结果),对外 动作 —— 发邮件、发布、写入记忆、任意 HTTP —— 都会被阻断,直到你明确确认。
- 沙箱执行。 智能体编写的代码运行在一次性 gVisor 容器中,没有任何机密、没有直连网络: 出站流量经过带 SSRF 过滤的代理。
- 租户隔离。 每个 ID 都归属于你的组织并在服务端复核;身份始终来自令牌,绝不取自请求字段。
- 可审计。 每一次工具调用都会连同运行、组织和状态一起记录。
需要你负责的部分:
- 密钥只放在服务端,存于密钥管理器或环境变量 —— 不要出现在浏览器、移动端包体、git 仓库或 CI 日志中。
- 每个系统一把密钥,并以该系统命名,便于单独吊销。
- 定期轮换:先创建新密钥、部署,再吊销旧的。
last_used_at会告诉你何时可以安全下线一把密钥。 - 任何出现在日志、截图或工单里的密钥都视为已泄露 —— 立即吊销。
- 若你把该 API 再暴露给自己的用户,请实施你自己的授权:平台密钥并不认识你的终端用户。
完整接口清单:API 参考。