API 密钥
创建、绑定、撤销 API 密钥,并弄清作用域与速率限制的真实生效范围
API 密钥用于在没有用户登录会话的场景调用 Clouisle API。每个密钥以其创建者用户的身份认证,因此所有者的角色权限、团队成员关系都会作用在该密钥的每次请求上;密钥本身还能额外收窄到指定 Agent 或工作流。
前置条件:你的角色需要具备 apikey:read / apikey:create / apikey:update / apikey:delete 权限(超级管理员默认全部具备)。密钥只能绑定到你本人有权访问的 Agent 与工作流,否则创建/更新会被拒绝。
访问 API 密钥
- 点击右上角个人资料图标。
- 在用户菜单中选择 API 密钥。
- 或直接访问
/app/api-keys。
列表每行显示:名称、状态徽章(活跃 / 已禁用 / 已过期)、key_prefix(前 12 个字符)、速率限制、过期时间、上次使用时间与创建时间。页面顶部只有搜索框,按名称或前缀做前端过滤(后端还支持按状态、按用户过滤,但当前页面没有提供这些筛选控件)。
可见范围由后端决定:只有超级管理员能看到并管理全部用户的密钥,其他用户(即使拥有 apikey:read)只能看到自己的密钥;非超级管理员编辑或删除他人的密钥会返回 3000 权限错误。
创建密钥
/app/api-keys),点击 创建密钥。
| 字段 | 必填 | 说明 |
|---|---|---|
| 名称 | 是 | 描述性名称,1–100 字符 |
| 速率限制 | 否 | 每分钟请求数,0 表示无限制。表单初始值为 0,见速率限制 |
| 过期时间 | 否 | 日期选择器;留空表示永不过期 |
| 允许访问的智能体 | 否 | 勾选列表,只列出已发布的 Agent,最多加载 100 个;留空 = 允许访问全部 |
| 允许访问的工作流 | 否 | 勾选列表,只列出已发布的工作流,最多加载 100 个;留空 = 允许访问全部 |
### 保存并复制密钥 | ||
| 点击 创建(编辑时为 保存)。创建成功后弹出API 密钥已创建对话框,完整密钥只在此时出现一次,点击复制按钮保存到密码管理器或环境变量。 |
完整密钥只显示一次。列表与详情接口只返回 key_prefix 和前 12 个字符,存储的是 bcrypt 哈希,无法反推。密钥丢失只能新建。
scopes 在界面上不可设置(表单不提交该字段),因此从界面创建的密钥一律带上服务端默认值 ["chat"];详见作用域。
密钥格式与存储
clou_<64 个十六进制字符>服务端用 secrets.token_hex(32) 生成 32 字节随机数的十六进制表示(64 字符),加上 clou_ 前缀。持久化的只有两项:
| 字段 | 内容 | 长度 |
|---|---|---|
key_prefix | 完整密钥的前 12 个字符(明文,用于定位与展示) | 12 |
key_hash | 完整密钥的 bcrypt 哈希 | ≤ 255 |
认证时服务端取请求密钥的前 12 个字符做索引查询,再对候选记录逐条校验哈希,因此前缀重复不影响正确性,只影响少量额外哈希计算。
编辑密钥
PUT /api/v1/api-keys/{id} 可修改:名称、作用域、速率限制、过期时间、启用状态(is_active,仅编辑时显示开关)、Agent / 工作流绑定。绑定字段传入 null(JSON 中省略)表示不改动,传入空数组 [] 表示清空绑定、放开为“全部可访问”。
编辑和删除都需要所有者的身份;无法编辑密钥本身——没有轮换端点,轮换的做法是新建密钥、迁移调用、再停用旧密钥。
过期时间由日期选择器提交为当天 00:00:00Z。选择 2026-01-01 意味着密钥在 2026-01-01 零点(UTC)起失效,而不是当天结束。
停用 / 重新激活密钥
停用会立即让密钥认证失败,但保留记录与审计历史:
- 停用:
POST /api/v1/api-keys/{id}/deactivate - 重新激活:
POST /api/v1/api-keys/{id}/activate
重复操作会报错:已停用时再次停用返回 api_key_already_inactive,已激活时再次激活返回 api_key_already_active(均为 1002 参数错误)。
删除密钥
DELETE /api/v1/api-keys/{id} 永久删除记录,无需先停用,且不可恢复。删除前会写入审计日志,因此事后仍能查到操作人、时间与被删密钥的 key_prefix。
使用 API 密钥
把完整密钥放进 Authorization 头,格式与 JWT 相同:
curl -X GET "https://your-domain.com/api/v1/agents" \
-H "Authorization: Bearer clou_your_api_key_here"import requests
api_key = "clou_your_api_key_here"
response = requests.get(
"https://your-domain.com/api/v1/agents",
headers={"Authorization": f"Bearer {api_key}"},
)认证与生效条件
请求进入后端后按前缀分流:clou_ 开头走 API 密钥认证,其余按 JWT 解析。密钥必须同时满足:
- 前缀能查到记录,且哈希校验通过,否则返回
2001(invalid_api_key)。 - 记录处于启用状态——查询条件里带
is_active=True,停用的密钥等同于不存在。 - 未过期——
expires_at为 null 表示永不过期;已过期返回2002(api_key_expired)。 - 所有者账号仍然有效——所有者被停用或处于待审批状态时返回
2004(inactive_user/pending_approval_user)。 - 绑定范围允许本次调用——密钥绑定过 Agent 时只能调用这些 Agent;绑定过工作流时只能触发这些工作流。绑定为空表示不限制。被拦下时返回
3000与api_key_no_agent_access/api_key_no_workflow_access。
每次成功认证都会刷新 last_used_at,这也是排查“密钥是否还在被调用”的依据。
绑定范围真正生效的位置是调用入口:Agent 会话(/api/v1/chat 相关路径)、iframe 嵌入(/api/v1/embed 下的 Agent 与工作流运行),以及工作流 Webhook 触发(POST /api/v1/workflows/webhook/{webhook_token},该接口只接受 clou_ 密钥,且要求工作流已发布并启用 Webhook 触发器)。
作用域
scopes 是一个 JSON 字符串数组,只存不判:后端记录并回显你传入的值,认证链路从不读取它。
- 界面上没有该字段,从界面创建的密钥固定为默认值
["chat"]。 - 通过 API 创建/更新时,
scopes接受任意字符串数组(例如["agent:read","kb:read"]),没有枚举校验、没有通配符语义,也没有独立的作用域管理端点。 - 更新时是整体替换(
PUT语义),不存在“只增不减”规则。 - 数据模型注释写的是“空数组表示完全访问”,但由于没有任何代码消费该字段,这句话目前没有可观察的行为。
不要用 scopes 做访问控制。真正决定权限的是密钥所有者的角色权限、团队成员关系,以及密钥的 Agent / 工作流绑定。
速率限制
rate_limit 是按密钥存储的“每分钟请求数”,0 表示无限制,模型与请求 schema 的默认值是 1000。
当前版本没有任何代码读取或强制执行它:中间件、依赖注入和认证链路都没有限速逻辑,响应也不带 X-RateLimit-* 头。界面上看到的 1000/min 或无限制只是展示值。
还有一个容易误判的默认值差异:请求 schema 默认 1000,但创建表单的初始值是 0(无限制)。由于表单总会提交该字段,从界面创建的密钥实际是无限速,而不是 1000。
需要真正的调用约束时,请使用密钥绑定(收窄可调用的 Agent / 工作流)、过期时间与角色权限,或在上游网关自行限速。参考页 API 密钥参考 记录字段默认值,但同样不代表已被强制执行。
过期提醒
后端每天 09:00 运行 tasks.check_api_key_expiration(default 队列),向密钥所有者发送站内通知:
- 即将过期(
apikey.expiring):启用中、未过期、且剩余7天内的密钥;且仅在剩余天数正好为 7 / 3 / 1 天时发送,避免每天重复提醒。 - 已过期(
apikey.expired):启用中、且在过去 24 小时内刚过期的密钥。
通知不会改变密钥状态——过期密钥仍保持“启用”标记,只是认证时被 expires_at 拦下。若在过期前把密钥停用,就不会再收到过期通知。
统计
GET /api/v1/api-keys/stats 返回当前用户的密钥统计(total、active、inactive、expired);超级管理员得到的是全站统计。统计口径与列表筛选一致:active 要求启用且未过期,expired 只看 expires_at 是否早于当前时间(不管是否启用)。当前 /app/api-keys 页面没有渲染这组数字。
错误代码
| 代码 | 含义 | 典型 msg_key |
|---|---|---|
2001 | 密钥无效(哈希不匹配) | invalid_api_key |
2002 | 密钥已过期 | api_key_expired |
2004 | 所有者被停用/待审批 | inactive_user / pending_approval_user |
3000 | 权限不足(所有者权限不足,或超出绑定范围) | permission_denied / api_key_no_agent_access / api_key_no_workflow_access |
4000 | 密钥记录不存在 | api_key_not_found |
1002 | 重复启用/停用 | api_key_already_active / api_key_already_inactive |
最佳实践
✅ 推荐
- 按用途单独建密钥(生产、CI、某合作伙伴各一把),便于单独停用。
- 设置过期时间,把长期密钥换成有期限的密钥。
- 把密钥绑定到实际需要调用的 Agent / 工作流。
- 存放在密钥管理器或环境变量中,不要写进代码仓库。
- 定期按
last_used_at清理长期未使用的密钥。
❌ 避免
- 用一个人的密钥承载多个人的调用(权限会跟着这个人变)。
- 把密钥放进前端代码、浏览器插件或公开文档。
- 依赖
scopes或rate_limit做安全边界。
故障排除
密钥无法使用(401)
- 确认密钥处于活跃状态:停用的密钥在认证查询阶段就被过滤掉,等同于不存在,返回
2001。 - 检查是否已过
expires_at(2002)。 - 检查请求头格式:
Bearer clou_...,中间不要有换行或多余空格。 - 在列表里核对
key_prefix(前 12 个字符)是否与手上的密钥一致,避免拿错环境。 - 若完整密钥已丢失——只能新建,无法找回。
密钥有效但请求被拒绝(403)
- 密钥可能绑定了 Agent / 工作流:确认目标资源在绑定列表内,或清空绑定后再试。
- 密钥所有者的角色权限可能不足:切换到所有者视角验证同一操作是否被允许。
- 所有者账号可能已停用或处于待审批状态(此时表现为
2004)。
列表里看不到别人的密钥
只有超级管理员能看到全部密钥,这是后端的权限判定(is_superuser),与 apikey:read 权限无关。
密钥疑似泄露
- 立即在列表里停用(比删除更快、且保留审计记录)。
- 新建密钥、更新调用方,确认旧密钥无流量后再删除。
- 在审计日志中核对创建/更新/删除与后续调用情况。
相关文档
- API Keys API — 端点、请求体与响应字段
- API 密钥参考 — 字段与默认值速查
- 智能体管理 — Agent 的发布状态决定能否被绑定
- 工作流编排 — 工作流发布与 Webhook 触发
- 审计日志 — 密钥操作的审计记录
这篇文章对你有帮助吗?