概述
Webhook 是 Qoder Cloud Agents 提供的事件驱动推送机制。当 Agent、Session 等资源发生生命周期变化时,系统以 HTTP POST 方式将结构化事件推送到开发者注册的 URL,无需轮询即可实时获取状态变更。 核心特性:- 事件驱动推送 — 资源状态变更时主动通知,无需客户端轮询
- 信封结构 — 统一的
BetaWebhookEvent { id, created_at, data, type:"event" }格式 - 投递语义 — at-least-once 保证;HMAC-SHA256 签名验证;指数退避重试
- 通配符订阅 — 使用
*订阅所有事件类型,简化集成配置
| 场景 | 说明 |
|---|---|
| 异步任务完成通知 | Session 执行完毕后触发下游流程 |
| Agent 配置变更审计 | 记录 Agent 创建/更新/删除操作 |
| 多 Agent 协作编排 | Thread 状态变更驱动子任务调度 |
| 运维监控告警 | 连续失败计数超阈值时告警 |
Domain Types
本节定义 Webhook 事件的数据结构。每种事件类型的data 字段遵循统一的 object 格式,包含资源 ID、事件类型及事件特定的额外字段。
Session 生命周期事件
Webhook Session Created Event Data
-
WebhookSessionCreatedEventData object { id, type }-
id: string触发事件的 Session ID。 -
type: "session.created""session.created"
POST /sessions接口创建 Session 后,系统立即发送此事件。
-
Webhook Session Updated Event Data
-
WebhookSessionUpdatedEventData object { id, type }-
id: string触发事件的 Session ID。 -
type: "session.updated""session.updated"
-
Webhook Session Archived Event Data
-
WebhookSessionArchivedEventData object { id, type }-
id: string触发事件的 Session ID。 -
type: "session.archived""session.archived"
-
Webhook Session Deleted Event Data
-
WebhookSessionDeletedEventData object { id, type }-
id: string触发事件的 Session ID。 -
type: "session.deleted""session.deleted"
-
Session 状态事件
Webhook Session Status Run Started Event Data
-
WebhookSessionStatusRunStartedEventData object { id, type }-
id: string触发事件的 Session ID。 -
type: "session.status_run_started""session.status_run_started"
-
Webhook Session Status Idled Event Data
-
WebhookSessionStatusIdledEventData object { id, type }-
id: string触发事件的 Session ID。 -
type: "session.status_idled""session.status_idled"
-
Session Thread 事件
适用于多 Agent 协作场景。Thread 事件在 Session 事件基础上额外携带session_thread_id 字段,标识具体的执行线程。
Webhook Session Thread Created Event Data
-
WebhookSessionThreadCreatedEventData object { id, type, session_thread_id }-
id: string触发事件的 Session ID。 -
type: "session.thread_created""session.thread_created"
-
session_thread_id: string所属的 Session Thread ID。
-
Webhook Session Thread Idled Event Data
-
WebhookSessionThreadIdledEventData object { id, type, session_thread_id }-
id: string触发事件的 Session ID。 -
type: "session.thread_idled""session.thread_idled"
-
session_thread_id: string所属的 Session Thread ID。
-
Webhook Session Thread Terminated Event Data
-
WebhookSessionThreadTerminatedEventData object { id, type, session_thread_id }-
id: string触发事件的 Session ID。 -
type: "session.thread_terminated""session.thread_terminated"
-
session_thread_id: string所属的 Session Thread ID。
-
Agent 生命周期事件
Agent 事件在基础字段之外额外携带version 字段,标识 Agent 的配置版本号。
Webhook Agent Created Event Data
-
WebhookAgentCreatedEventData object { id, type, version }-
id: string触发事件的 Agent ID。 -
type: "agent.created""agent.created"
POST /agents接口创建 Agent 后,系统发送此事件。 -
version: integerAgent 版本号。首次创建时为1。
-
Webhook Agent Updated Event Data
-
WebhookAgentUpdatedEventData object { id, type, version }-
id: string触发事件的 Agent ID。 -
type: "agent.updated""agent.updated"
version递增。 -
version: integerAgent 版本号。
-
Webhook Agent Archived Event Data
-
WebhookAgentArchivedEventData object { id, type, version }-
id: string触发事件的 Agent ID。 -
type: "agent.archived""agent.archived"
-
version: integerAgent 版本号。
-
Webhook Agent Deleted Event Data
-
WebhookAgentDeletedEventData object { id, type, version }-
id: string触发事件的 Agent ID。 -
type: "agent.deleted""agent.deleted"
-
version: integerAgent 版本号。
-
Webhook Endpoint API
管理 Webhook 端点的 CRUD 接口。通过这些接口可以创建、查询、更新、删除 Webhook 端点,以及发送测试事件和控制端点的启用/禁用状态。 Base URL:| 区域 | 地址 |
|---|---|
| Global | https://api.qoder.com/api/v1/cloud |
| CN | https://api.qoder.com.cn/api/v1/cloud |
POST /webhook_endpoints
创建一个新的 Webhook 端点。 请求参数:| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | 是 | 接收事件推送的 HTTP 或 HTTPS URL(生产环境强烈建议使用 HTTPS) |
description | string | 否 | 端点描述,便于管理识别 |
events | string[] | 是 | 订阅的事件类型列表,支持 * 通配符 |
201 Created
注意: signing_secret 仅在创建时返回一次,请妥善保存。后续查询接口不会再次返回此字段。
状态码:
| 状态码 | 说明 |
|---|---|
| 201 | 创建成功 |
| 400 | 请求参数无效(如 URL 格式错误、事件类型不合法) |
| 401 | 未授权,Token 无效或过期 |
| 422 | 语义错误(如 URL 重复注册) |
GET /webhook_endpoints
获取当前账户下所有 Webhook 端点列表。 请求参数: 无 响应:200 OK
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 端点唯一标识 |
url | string | 接收事件的 URL |
description | string | 端点描述 |
events | string[] | 订阅的事件类型列表 |
active | boolean | 是否启用 |
signing_secret_version | integer | 签名密钥版本号 |
consecutive_fail | integer | 连续投递失败次数 |
last_success_at | string | 最近一次成功投递时间(RFC 3339) |
last_failure_at | string | 最近一次投递失败时间(RFC 3339),无失败记录时为 null |
created_at | string | 创建时间(RFC 3339) |
updated_at | string | 最近更新时间(RFC 3339) |
GET /webhook_endpoints/{id}
获取指定 Webhook 端点的详细信息。 路径参数:| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Webhook 端点 ID |
200 OK
返回单个端点对象,结构与列表接口中的元素一致。
状态码:
| 状态码 | 说明 |
|---|---|
| 200 | 成功 |
| 404 | 端点不存在 |
PUT /webhook_endpoints/{id}
更新指定 Webhook 端点的配置。 路径参数:| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Webhook 端点 ID |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | 否 | 更新接收事件推送的 URL |
description | string | 否 | 更新端点描述 |
events | string[] | 否 | 更新订阅的事件类型列表 |
200 OK
返回更新后的完整端点对象。
状态码:
| 状态码 | 说明 |
|---|---|
| 200 | 更新成功 |
| 400 | 请求参数无效 |
| 404 | 端点不存在 |
DELETE /webhook_endpoints/{id}
永久删除指定的 Webhook 端点。删除后所有未投递的事件将被丢弃。 路径参数:| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Webhook 端点 ID |
204 No Content
状态码:
| 状态码 | 说明 |
|---|---|
| 204 | 删除成功 |
| 404 | 端点不存在 |
POST /webhook_endpoints/{id}/test
向指定端点发送一个测试事件,用于验证端点连通性和签名验证逻辑。 路径参数:| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Webhook 端点 ID |
202 Accepted
| 字段 | 类型 | 说明 |
|---|---|---|
event_id | integer | 测试事件的内部 ID |
delivery_rows | integer | 事件发布的消息分区数 |
| 状态码 | 说明 |
|---|---|
| 202 | 测试事件已发送 |
| 404 | 端点不存在 |
POST /webhook_endpoints/{id}/enable
启用一个被禁用的 Webhook 端点。启用后端点将重新接收事件推送。 路径参数:| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Webhook 端点 ID |
200 OK
返回启用后的端点对象(active: true)。
状态码:
| 状态码 | 说明 |
|---|---|
| 200 | 启用成功 |
| 404 | 端点不存在 |
POST /webhook_endpoints/{id}/disable
禁用一个 Webhook 端点。禁用后端点将停止接收事件推送,但不会被删除。 路径参数:| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Webhook 端点 ID |
200 OK
返回禁用后的端点对象(active: false)。
状态码:
| 状态码 | 说明 |
|---|---|
| 200 | 禁用成功 |
| 404 | 端点不存在 |
GET /webhook_events
列出 Webhook 事件投递记录,用于审计和排查。 请求参数: 无 响应:200 OK
返回事件列表,包含事件的投递状态、时间戳等信息。
示例:
GET /webhook_events/{id}
获取单个 Webhook 事件的详细信息。 路径参数:| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Webhook 事件 ID |
200 OK
状态码:
| 状态码 | 说明 |
|---|---|
| 200 | 成功 |
| 404 | 事件不存在 |
错误响应格式
所有接口在遇到错误时,返回统一的错误结构:| 字段 | 类型 | 说明 |
|---|---|---|
error.message | string | 人类可读的错误描述 |
error.type | string | 错误分类(如 invalid_request_error、not_found_error) |
request_id | string | 请求追踪 ID,用于排查问题时提供给支持团队 |
type | string | 固定值 "error" |
注意: 鉴权层(如 Token 无效或缺失)返回的 401 响应可能不遵循上述业务错误结构,而是由网关直接返回。
Webhook Delivery(投递机制)
投递方式
系统通过 HTTP POST 将事件推送到注册的 URL,请求格式如下:- Method:
POST - Content-Type:
application/json - Body: JSON 格式的信封结构(详见信封结构详解)
请求头
每次投递包含以下 HTTP 请求头:| Header | 说明 |
|---|---|
Content-Type | application/json |
User-Agent | QoderCloudAgents-Webhook/1.0 |
Webhook-Signature | HMAC-SHA256 签名,格式为 t=<unix_epoch>,v1=<hmac_sha256_hex> |
签名验证
为确保事件来源的真实性和数据完整性,每次投递都携带 HMAC-SHA256 签名。开发者应在接收端验证签名后再处理事件。 签名格式:signing_secret— 创建端点时返回的密钥(whsec_前缀)t— 签名时的 Unix 时间戳(秒)raw_body— 请求体的原始字节内容(未经解析)
- 从
Webhook-SignatureHeader 中提取t和v1 - 检查时间戳是否在容忍窗口内(建议 600 秒),防止重放攻击
- 使用
signing_secret对"<t>.<raw_body>"计算 HMAC-SHA256 - 将计算结果与
v1进行恒时比较(timing-safe comparison)
签名验证代码示例
Node.js:重试策略
当投递失败时,系统采用指数退避策略进行重试:| 尝试次数 | 延迟 | 说明 |
|---|---|---|
| 第 1 次 | 立即 | 首次投递 |
| 第 2 次 | 1 秒 | 第一次重试 |
| 第 3 次 | 5 秒 | 第二次重试 |
| 第 4 次 | 30 秒 | 最终重试 |
响应码处理
| 响应码范围 | 处理方式 |
|---|---|
| 2xx | 投递成功,事件标记为已送达 |
| 4xx | 不重试,事件标记为已丢弃(客户端错误应由开发者修复) |
| 5xx | 触发重试(服务端临时故障) |
| 超时 | 触发重试(默认超时 30 秒) |
自动降级
当某个端点的consecutive_fail 计数超过 20 时,系统将触发降级警告。建议开发者监控此指标,并在持续失败时检查:
- 端点 URL 是否可达
- SSL 证书是否有效
- 服务端是否正常响应
- 签名验证逻辑是否存在 Bug
Supported Event Types(完整清单)
事件类型总览
| 事件类型 | 状态 | data 额外字段 | 触发条件 |
|---|---|---|---|
session.created | ✅ 已实现 | — | POST /sessions 成功创建 Session |
session.updated | ✅ 已实现 | — | Session 元数据被修改 |
session.archived | ✅ 已实现 | — | Session 被归档 |
session.deleted | ✅ 已实现 | — | Session 被永久删除 |
session.status_run_started | ✅ 已实现 | — | Agent 开始一轮运行 |
session.status_idled | ✅ 已实现 | — | Turn 执行完成,回到 idle 状态 |
session.thread_created | ✅ 已实现 | session_thread_id | 新的 Session Thread 被创建 |
session.thread_idled | ✅ 已实现 | session_thread_id | Thread 执行结束进入 idle |
session.thread_terminated | ✅ 已实现 | session_thread_id | Thread 被终止 |
agent.created | ✅ 已实现 | version | POST /agents 创建 Agent |
agent.updated | ✅ 已实现 | version | Agent 配置被更新 |
agent.archived | ✅ 已实现 | version | Agent 被归档 |
agent.deleted | ✅ 已实现 | version | Agent 被删除 |
session.pending | 🔜 白名单已注册 | — | Session 等待调度 |
session.running | 🔜 白名单已注册 | — | Session 正在运行 |
session.idled | 🔜 白名单已注册 | — | Session 进入 idle |
session.requires_action | 🔜 白名单已注册 | — | Session 需要人工介入 |
session.status_run_failed | 🔜 白名单已注册 | — | 运行失败 |
session.status_terminated | 🔜 白名单已注册 | — | Session 被终止 |
session.status_rescheduled | 🔜 白名单已注册 | — | Session 被重新调度 |
session.status_paused_pending_input | 🔜 白名单已注册 | — | 暂停等待用户输入 |
session.status_paused_pending_approval | 🔜 白名单已注册 | — | 暂停等待审批 |
session.status_paused_user_intervention | 🔜 白名单已注册 | — | 暂停等待人工干预 |
session.outcome_evaluation_started | 🔜 白名单已注册 | — | 结果评估开始 |
session.outcome_evaluation_ended | 🔜 白名单已注册 | — | 结果评估结束 |
vault.created | 🔜 白名单已注册 | — | 密钥库创建 |
vault.deleted | 🔜 白名单已注册 | — | 密钥库删除 |
vault.updated | 🔜 白名单已注册 | — | 密钥库更新 |
vault_credential.created | 🔜 白名单已注册 | — | 凭据创建 |
vault_credential.updated | 🔜 白名单已注册 | — | 凭据更新 |
vault_credential.deleted | 🔜 白名单已注册 | — | 凭据删除 |
vault_credential.shared | 🔜 白名单已注册 | — | 凭据共享 |
vault_credential.revoked | 🔜 白名单已注册 | — | 凭据撤销 |
deployment.created | 📋 规划中 | — | 部署创建 |
deployment.updated | 📋 规划中 | — | 部署更新 |
deployment.archived | 📋 规划中 | — | 部署归档 |
deployment.deleted | 📋 规划中 | — | 部署删除 |
deployment.paused | 📋 规划中 | — | 部署暂停 |
deployment.unpaused | 📋 规划中 | — | 部署恢复 |
deployment_run.started | 📋 规划中 | — | 部署运行开始 |
deployment_run.failed | 📋 规划中 | — | 部署运行失败 |
deployment_run.succeeded | 📋 规划中 | — | 部署运行成功 |
environment.created | 📋 规划中 | — | 环境创建 |
environment.updated | 📋 规划中 | — | 环境更新 |
environment.archived | 📋 规划中 | — | 环境归档 |
environment.deleted | 📋 规划中 | — | 环境删除 |
memory_store.created | 📋 规划中 | — | 记忆存储创建 |
memory_store.archived | 📋 规划中 | — | 记忆存储归档 |
memory_store.deleted | 📋 规划中 | — | 记忆存储删除 |
特殊事件类型
| 事件类型 | 说明 |
|---|---|
* | 通配符,订阅所有事件类型。在创建端点时使用 ["*"] 可接收全部事件推送 |
webhook.test | 内部测试事件,通过 POST /webhook_endpoints/{id}/test 触发 |
状态标记说明
| 标记 | 含义 |
|---|---|
| ✅ 已实现 | 系统中存在完整的事件触发点,可正常订阅和接收 |
| 🔜 白名单已注册 | 事件类型已在验证白名单中注册(可用于 events 字段),但暂无触发点 |
| 📋 规划中 | 事件类型已在 API 设计层面完成定义,尚未注册到白名单 |
信封结构详解
所有 Webhook 事件均使用统一的信封(envelope)结构进行封装投递。基础信封结构
Thread 事件信封
Thread 事件的data 中额外包含 session_thread_id 字段:
Agent 事件信封
Agent 事件的data 中额外包含 version 字段:
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 事件唯一标识。格式为 whevt_ + 唯一标识符,由事件的 idempotency key 确定性生成 |
created_at | string | 事件创建时间,RFC 3339 UTC 格式 |
type | string | 固定值 "event",标识这是一个事件信封 |
data | object | 事件负载,包含触发资源的信息 |
data.id | string | 触发事件的资源 ID(Session ID 或 Agent ID) |
data.type | string | 事件类型字符串,如 "session.created"、"agent.updated" |
data.session_thread_id | string | (仅 Thread 事件)所属的 Session Thread ID |
data.version | integer | (仅 Agent 事件)Agent 配置版本号 |
幂等性处理
信封中的id 字段可作为去重键(deduplication key)。由于投递语义为 at-least-once,同一事件可能被多次推送。接收端应:
- 使用
id作为唯一键进行去重 - 在处理前检查该
id是否已被消费 - 确保事件处理逻辑的幂等性
规划中的事件(Coming Soon)
以下事件类型已在 API 设计层面完成对齐,将随各资源功能上线后逐步开放。具体时间线视产品需求确定。deployment.* (部署生命周期,6 个事件)
| 事件类型 | 触发条件 |
|---|---|
deployment.created | 新部署被创建 |
deployment.updated | 部署配置被更新 |
deployment.archived | 部署被归档 |
deployment.deleted | 部署被删除 |
deployment.paused | 部署被暂停(停止触发新运行) |
deployment.unpaused | 部署被恢复(重新开始触发运行) |
deployment_run.* (部署运行状态,3 个事件)
| 事件类型 | 触发条件 |
|---|---|
deployment_run.started | 一次部署运行开始执行 |
deployment_run.failed | 部署运行失败 |
deployment_run.succeeded | 部署运行成功完成 |
environment.* (环境管理,4 个事件)
| 事件类型 | 触发条件 |
|---|---|
environment.created | 运行环境被创建 |
environment.updated | 环境配置被更新 |
environment.archived | 环境被归档 |
environment.deleted | 环境被删除 |
memory_store.* (记忆存储,3 个事件)
| 事件类型 | 触发条件 |
|---|---|
memory_store.created | 记忆存储实例被创建 |
memory_store.archived | 记忆存储被归档 |
memory_store.deleted | 记忆存储被删除 |
vault.* (密钥库,3 个事件)
| 事件类型 | 触发条件 |
|---|---|
vault.created | 密钥库被创建 |
vault.updated | 密钥库配置被更新 |
vault.deleted | 密钥库被删除 |
vault_credential.* (凭据管理,5 个事件)
| 事件类型 | 触发条件 |
|---|---|
vault_credential.created | 新凭据被添加到密钥库 |
vault_credential.updated | 凭据信息被更新 |
vault_credential.deleted | 凭据被删除 |
vault_credential.shared | 凭据被共享给其他 Agent |
vault_credential.revoked | 凭据的共享权限被撤销 |
附录 A:快速接入指南
步骤 1:创建 Webhook 端点
步骤 2:保存 signing_secret
从创建响应中获取signing_secret 并安全存储,后续验证签名时使用。
步骤 3:实现接收端
在您的服务中实现 Webhook 接收端点,确保:- 验证
Webhook-Signature签名 - 使用事件
id做幂等去重 - 返回
200 OK确认收到 - 异步处理业务逻辑(避免超时)
步骤 4:发送测试事件
步骤 5:验证并上线
确认测试事件接收正常后,即可投入生产使用。附录 B:最佳实践
| 实践 | 说明 |
|---|---|
| 签名验证 | 始终验证 Webhook-Signature,拒绝无效请求 |
| 幂等处理 | 使用事件 id 去重,防止重复消费 |
| 快速响应 | 接收端应在 5 秒内返回 2xx,耗时逻辑异步处理 |
| 时间戳校验 | 拒绝超过容忍窗口的事件,防止重放攻击 |
| 精确订阅 | 仅订阅需要的事件类型,减少不必要的网络开销 |
| 监控告警 | 监控 consecutive_fail 指标,及时发现投递异常 |