开放平台 API
开放平台是 AuraBoot 的机器对机器入口。第三方系统——ERP 同步任务、仓储机器人集群、合作伙伴门户——拥有独立身份,用短期 token 调用已发布 API,接收 Webhook,并可向 AuraBoot 自动化注入外部事件。
它与面向登录用户的 REST API 相互独立:人类会话不出现在这里,机器身份也不冒充员工。
一分钟心智模型
base_url + client_id + client_secret
│
├─ POST /oauth2/token (OAuth 2.0 client_credentials)
▼
短期不透明 access_token (默认 1 小时;服务端只存哈希)
│
├─ Authorization: Bearer <access_token>
▼
/api/open/v1/** (Tenant 由 token 派生)写代码前先记住三条不变量:
- Tenant 来自 token。 没有
X-Tenant-Id头,也没有 Tenant 查询参数。客户端不能切换租户;每个 token 只绑定你的应用在一个 Tenant + Environment 上的一次安装。 - 只有已发布的能力存在。 内部 Controller、DSL 模型和命令不会自动暴露。不在 OpenAPI 注册表里的路由等于不存在——探测只会得到
404。 - Secret 不是业务 API Key。
client_secret只用于 token 端点。业务 API 只接受短期 Bearer token。
第一步 — 管理员配置
平台管理员(具备 sys.connector.update 角色)在 设置 → API 与开放平台(/settings/api-docs)完成:
- 创建应用。 创建者自动成为首名
owner。 - 安装到 Tenant/Environment(
development/staging/production)并授予 scope——例如assets.read、assets.manage、inventory.stockins.manage。Scope 以授权为上限:token 永远不能超出安装被授予的范围。 - 创建凭据。
client_id/client_secret只显示一次。把 secret 存进你的 secret manager;服务端只保留强哈希。
应用协作:owner 管理成员、安装、scope、凭据、重放和应用生命周期;maintainer 管运行时事务但不能管成员;viewer 只读运维与审计;非成员什么都看不到。每次成员变更都写审计,最后一名 owner 不可移除或降级。
第二步 — 用凭据换 token
export AURABOOT_BASE_URL='https://your-tenant.example.com'
export AURABOOT_CLIENT_ID='ab_client_...'
export AURABOOT_CLIENT_SECRET='只放在 secret manager 里'
ACCESS_TOKEN="$(
curl -fsS "$AURABOOT_BASE_URL/oauth2/token" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode grant_type=client_credentials \
--data-urlencode client_id="$AURABOOT_CLIENT_ID" \
--data-urlencode client_secret="$AURABOOT_CLIENT_SECRET" \
| jq -er .access_token
)"成功响应:
{
"access_token": "…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "assets.read assets.manage"
}可用可选参数 scope 请求被授权范围的子集;请求未授权的 scope 会得到 invalid_scope。
缓存 token,在 expires_in 结束前约 30 秒内复用。不要每个请求都换 token——该端点有限流和审计。业务调用返回 401 invalid_token 时,只刷新一次并重试一次;仍失败就停下排查安装或凭据状态。
第三步 — 调用 API
每个业务调用都是 base_url + access_token:
curl -fsS "$AURABOOT_BASE_URL/api/open/v1/whoami" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "X-Request-Id: $(uuidgen)"每个请求都带上 X-Request-Id。它会被回显,并可在租户的调用审计和 Webhook 投递日志中检索——是支持排障的关联键。
读取资源
已发布资源(例如 assets、inventory.stock-ins)支持 list 和 get:
# 列表 — 通过不透明 cursor 做 keyset 分页
curl -fsS "$AURABOOT_BASE_URL/api/open/v1/resources/assets?limit=50" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# 第二页 — 原样传回 nextCursor;它有签名,24 小时过期
curl -fsS "$AURABOOT_BASE_URL/api/open/v1/resources/assets?limit=50&cursor=$NEXT_CURSOR" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# 单条 — 注意响应里的 ETag 头
curl -fsSI "$AURABOOT_BASE_URL/api/open/v1/resources/assets/$RECORD_PID" \
-H "Authorization: Bearer $ACCESS_TOKEN"- 记录用稳定的公共 PID 寻址,绝不暴露数据库 ID。
nextCursor是带签名的不透明 token。篡改、跨资源使用或过期都会失败。不要解析它,原样存储回传即可。
执行命令
已发布命令(例如 assets.assign)同时要求最近 GET 拿到的强 If-Match ETag 和你生成的 Idempotency-Key:
ETAG=$(curl -fsSI "$AURABOOT_BASE_URL/api/open/v1/resources/assets/$RECORD_PID" \
-H "Authorization: Bearer $ACCESS_TOKEN" | sed -n 's/^ETag: //p')
curl -fsS -X POST "$AURABOOT_BASE_URL/api/open/v1/commands/assets.assign:execute" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "If-Match: $ETAG" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{"targetPid":"'$RECORD_PID'","input":{"assignee":"alice"}}'- 过期或缺失
If-Match返回412 precondition_failed。重新 GET、合并业务意图,然后用新的幂等 key 重试——没有 last-write-wins。 - 相同
Idempotency-Key+ 相同请求体重放会返回原始结果(idempotentReplay: true),不会产生重复副作用;同 key 不同 body 返回冲突。 - SDK 对
412永不自动重试。
错误模型
错误使用稳定信封:code、message、requestId、details。按 code 分支,不要按文案分支。
| HTTP | code | 含义 — 调用方动作 |
|---|---|---|
| 400 | invalid_scope | 修正 token 请求的 scope;不要原样重试 |
| 400 | invalid_request | 路径、body 或请求头不合法;先修正再重试 |
| 401 | invalid_client | 凭据对错误;轮换或修正——禁止盲重试 |
| 401 | invalid_token | token 过期或被撤销;刷新一次,仍失败则排查 |
| 403 | insufficient_scope | 请管理员授予 scope;客户端无法绕过 |
| 404 | open_api_capability_not_found | 路由未发布;不要探测内部路径 |
| 412 | precondition_failed | ETag 不匹配;重新 GET 后用新幂等 key 重试 |
| 429 | rate_limit_exceeded | 带抖动的指数退避;降低并发 |
接收 Webhook
被订阅的事件发生时(例如 assets.assignment.changed),AuraBoot 向每个已登记的 webhook 端点发送带签名的 POST。投递是 at-least-once,请按 event id 去重。
解析前先对原始请求 body 验签:
import { createHmac, timingSafeEqual } from 'node:crypto';
const timestamp = request.headers.get('X-Webhook-Timestamp');
const received = request.headers.get('X-Webhook-Signature');
const expected = `sha256=${createHmac('sha256', webhookSecret)
.update(`${timestamp}.${rawBody}`, 'utf8').digest('hex')}`;
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
const valid = fresh && received?.length === expected.length
&& timingSafeEqual(Buffer.from(received), Buffer.from(expected));- 先校验时间窗口(5 分钟),再用常量时间比较签名,最后才解析。
- 用
X-Webhook-Delivery/ 事件id去重——重试沿用相同标识。 - 失败投递按指数退避重试,进入死信队列后可在管理端安全 replay。Webhook secret 只显示一次,支持重叠轮换。
注入外部事件
第三方系统可以通过安装级 event source 把事件推入租户的自动化:
curl -fsS -X POST "$AURABOOT_BASE_URL/api/open/v1/event-sources/$SOURCE_CODE/events" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Idempotency-Key: $STABLE_KEY" \
-H 'Content-Type: application/json' \
-d '{"type":"line.stopped","schemaVersion":1,"occurredAt":"2026-09-27T08:00:00Z","data":{}}'该入口只做认证、schema 校验、幂等去重和入队——不会在 HTTP 请求线程里执行长链自动化。注册一个带 external-event 触发器的自动化来消费这些事件。
凭据轮换
轮换时创建新凭据并设置宽限期(5 分钟到 7 天)。窗口内两对凭据都能换 token;窗口结束旧凭据即被拒绝。轮换前签发的 token 会活完自己的短 TTL——事故级撤销请使用凭据撤销或停用安装,两者会立即失效所有 active token。
TypeScript SDK
SDK 封装了完整流程——token 缓存、401 只刷新一次、写重试保留幂等 key 与 If-Match、412 不自动重试、错误暴露 status / code / requestId:
import { OpenPlatformClient } from "@auraboot/open-platform-sdk";
// 方式 A:客户端凭证(token 自动缓存与刷新)
const client = new OpenPlatformClient({
baseUrl: process.env.AURABOOT_BASE_URL!,
clientId: process.env.AURABOOT_CLIENT_ID!,
clientSecret: process.env.AURABOOT_CLIENT_SECRET!, // 来自 secret manager,绝不打包进前端
scope: "assets.read assets.manage",
});
const page = await client.listResources("assets", 50);
const { resource, etag } = await client.getAssetVersioned(page.items[0]!.pid);
await client.assignAsset(resource.pid, "alice", crypto.randomUUID(), etag);分发状态(2026-09): SDK 尚未发布到 npm registry。在此之前,请从 monorepo 源码 packages/open-platform-sdk 或打包 tarball 引用——不要执行 npm install @auraboot/open-platform-sdk,它解析不到。包正式发布后本说明会同步更新。
安全禁令
- 只走 TLS。绝不把 token 或 secret 放进 URL、query string、日志或浏览器/localStorage。
- 绝不把
client_secret放进前端、移动端包或共享产物——改用后端代换 token。 client_secret只当 OAuth 凭据用;它不是业务 API key。- 按周期轮换凭据(90 天基线),事故响应使用撤销/停用。
- 像保管密码一样保管
webhookSecret,并同样轮换。