外观
Webhook 接入
Webhook 将 IM 内已发生的事件通知客户业务服务。它适合同步项目状态、审计与业务工作流,不替代客户端长连接,也不要求主业务事务等待接收方完成全部工作。
订阅内容
部署时配置接收 URL、订阅事件和签名密钥,启用后按所选事件范围投递通知。
| 示例事件 | 用途 |
|---|---|
GROUP_CREATED | 项目群首次创建 |
GROUP_MEMBER_CHANGED | 项目群成员同步 |
GROUP_PROFILE_CHANGED | 群资料变化 |
GROUP_DISMISSED | 群解散 |
MESSAGE_REVOKED | 消息撤回 |
FILE_MESSAGE_CREATED | 文件消息进入投递链路 |
AGENT_MENTIONED | 智能体被提及 |
验签顺序
保留原始 HTTP body,读取 X-Timestamp、X-Nonce、X-Signature,再读取 X-Event-Id、X-Event-Type 与追踪信息。先验签和校验时间窗,再反序列化并执行业务。
Webhook 的签名原文使用换行分隔,与 Server API 无分隔的拼接规则不同:
text
UPPERCASE_METHOD + "\n"
+ REQUEST_PATH + "\n"
+ TIMESTAMP + "\n"
+ NONCE + "\n"
+ SHA256(RAW_BODY)以下 Node.js 函数仅演示签名比对;时间窗、nonce 防重放、请求大小限制和事件消费由客户服务实现。
js
import { createHash, createHmac, timingSafeEqual } from 'node:crypto';
export function matchesWebhookSignature({
method, requestPath, timestamp, nonce, rawBody, signature, secret,
}) {
if (typeof signature !== 'string' || !/^[a-f0-9]{64}$/.test(signature)) {
return false;
}
const bodyHash = createHash('sha256').update(rawBody).digest('hex');
const canonical = [
method.toUpperCase(), requestPath, timestamp, nonce, bodyHash,
].join('\n');
const expected = createHmac('sha256', secret)
.update(canonical, 'utf8').digest();
return timingSafeEqual(expected, Buffer.from(signature, 'hex'));
}不要先重新格式化 JSON 再验签。反向代理转发路径应与约定的接收路径一致。
幂等消费
同一个事件可能被重复投递。以 X-Event-Id 作为消费去重依据,在本地事务中记录事件和需要执行的业务动作;可靠落库后返回 2xx。耗时操作可交给客户自己的任务队列。
收到重复事件时确认此前已经持久接受,即可返回成功。不要因为重复而反复创建工单、发放权益或发送另一条消息。
失败、重试与重放
启用可靠投递后,IM 保存尝试次数、下次重试时间和结果。Webhook 失败不会回滚已经成功的聊天或群操作。超过自动重试范围的事件由授权管理员审查后重放,接收方仍按原事件 ID 去重。
联调应验证签名错误、重发、接收超时、暂时不可用和恢复后的消费。排查时记录事件 ID、追踪 ID 与错误类别,不记录密钥和完整敏感正文。更多见 故障排查。