连接器 API
连接第三方账号(邮箱、IM、文档等),供工作流触发与调用。
基础路径:/api/connectors认证:用户会话(登录态)
连接器接口不开放 OAuth 第三方应用访问——这里管的是你的个人第三方账号凭据, 让外部应用列出/新建/撤销它们是一次实打实的权限扩张,需要单独设计 scope 后才考虑开放。
边界说明
- 授权归属个人:你连接的账号只有你自己可见可用,团队其他成员看不到、也用不了。
- 只用
connectionId寻址:接口不接受、也不返回内部寻址标识。 - 不返回凭据:任何响应都只包含掩码(
credentialMask),不含明文或密文。
获取连接器目录
GET /api/connectors/catalog返回已上架的连接器。未上架或已下架的不会出现在结果中。
响应 200
{
"success": true,
"data": [
{
"id": "uuid",
"service": "qq_mail",
"displayName": "QQ 邮箱",
"description": "通过 IMAP/SMTP 连接 QQ 邮箱,支持新邮件触发工作流",
"categories": ["Productivity", "Email"],
"authType": "custom_credential",
"credentialFields": [
{ "key": "username", "label": "邮箱地址", "type": "text", "secret": false, "required": true },
{ "key": "password", "label": "IMAP 授权码", "type": "password", "secret": true, "required": true,
"helpUrl": "https://service.mail.qq.com/detail/0/75" }
],
"supportedTriggers": ["poll_new_email"],
"status": "approved",
"circuitBroken": false,
"creditMultiplier": 1000
}
]
}credentialFields 用于驱动授权表单渲染:secret: true 的字段应以密码框呈现, helpUrl 指向「去哪里获取这串凭据」的说明页。
我的连接列表
GET /api/connectors/connections响应 200
{
"success": true,
"data": [
{
"id": "uuid",
"service": "qq_mail",
"accountLabel": "me@qq.com",
"credentialMask": { "username": "me@qq.com", "password": "abc****wxyz" },
"status": "active",
"lastVerifiedAt": "2026-07-27T10:00:00.000Z",
"lastErrorCode": null,
"createdAt": "2026-07-27T09:00:00.000Z",
"updatedAt": "2026-07-27T10:00:00.000Z"
}
]
}status 取值:pending(已保存但尚未验证)、active(已验证可用)、invalid(凭据失效,需重新授权)、revoked(已撤销)。
新建连接一律是
pending——建连接只校验字段完整性、不联网。只有「测试连接」成功才升为active; 工作流发布校验只认active,避免填错的凭据被发布成一个永远不触发的工作流。
建立连接
POST /api/connectors/connections请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
service | string | 是 | 连接器标识,取自目录接口 |
credentials | object | 是 | 按该连接器的 credentialFields 提交,键为字段 key |
accountLabel | string | 否 | 账号显示名;不传时前端通常取非密字段(如邮箱地址) |
{
"service": "qq_mail",
"credentials": { "username": "me@qq.com", "password": "<IMAP 授权码>" },
"accountLabel": "me@qq.com"
}响应 201:返回创建的连接(结构同列表项),status 为 pending。 创建后应立即调用「测试连接」完成验证。
错误码
| 状态码 | error | 说明 |
|---|---|---|
| 400 | credential_field_missing | 缺少必填凭据字段 |
| 400 | invalid_body | 请求体格式无效 |
| 403 | connector_not_approved | 该连接器尚未上架 |
| 404 | connector_not_found | 连接器不存在 |
| 503 | connector_circuit_broken | 该连接器已临时停用 |
| 503 | encryption_not_configured | 服务端加密密钥未配置 |
测试连接
POST /api/connectors/connections/:connectionId/test会真的向上游发一次最轻的探测请求。成功刷新 lastVerifiedAt; 凭据本身不对才把连接标记为 invalid——网关不可达或超时属于服务端故障,不影响连接状态。
限流:每用户每分钟 10 次(可经 CONNECTOR_TEST_RATE_LIMIT_PER_MINUTE 调整)。
响应 200
{ "success": true, "data": { "ok": false, "errorCode": "credential_invalid" } }errorCode 取值:credential_invalid、upstream_rate_limited、upstream_error、 gateway_unreachable、gateway_timeout、gateway_auth_failed。
前端展示约定(issue #1050):
errorCode是机器码,不可直接展示给用户。 前端统一通过services/connectorErrorMessage.ts的getConnectorErrorMessage()翻译成本地化文案;未知码回退到调用方的兜底文案,绝不显示码本身或后端message原文 (后端message含内部细节)。新增错误码时请同步该映射表与errors.connector.*双语文案。
只有 credential_invalid 会把连接标记为 invalid;其余均为服务端侧故障,不改变连接状态 (gateway_auth_failed 指平台令牌被网关拒绝,与用户账号无关)。
撤销连接
DELETE /api/connectors/connections/:connectionId撤销后依赖该连接的工作流触发器会一并失效(fail-closed)。
响应 200
{ "success": true }触发执行入口(内部,外部调用方无法使用)
POST /api/connector-triggers/dispatch连接器触发的工作流由平台后台自动派发,这个端点不面向外部调用方, 列在这里只是为了说明「新邮件到了之后,工作流是怎么被跑起来的」。
它只接受平台后台派发器签发的 60 秒短期令牌,且会对着数据库逐条校验: 事件租约(含一次性消费闸)、绑定归属、启用状态、连接可用性。 校验未通过一律返回同一个 404,不区分原因。 请求体不被读取——邮件内容只会从平台自己的事件账本里取,不接受调用方提供。 响应也只回 runId,不透出工作流的执行输出。
因此拿到令牌也无法用它触发别人的工作流,更无法把别的内容塞进去执行, 也无法靠重放同一个令牌把一封邮件跑成多次计费。
| 状态码 | 说明 |
|---|---|
| 200 | 工作流已触发,返回 runId |
| 402 | 团队积分不足(派发器会退避重试,充值后自动补跑) |
| 409 | 确证未执行:令牌签发后触发器绑定被改动(通常是此刻重新发布了文档),或事件、连接、部署、余额等执行前预检瞬时失败;派发器可安全重试 |
| 429 | 被限流挡下,请求未进入执行;派发器按 Retry-After 重试 |
| 404 | 令牌无效,或校验未通过(不区分具体原因,防止探测) |
通用错误
| 状态码 | error | 说明 |
|---|---|---|
| 401 | unauthorized | 未登录 |
| 404 | connection_not_found | 连接不存在或不属于你(刻意不区分,防止探测他人连接) |