Webhook
RolChat 以签名的 HTTP POST 请求将事件投递到您的系统。本页介绍插件 Webhook;由 Automation 规则发送的 Webhook 在重试、等待时间和密钥轮换方面有所不同,参见自动化 Webhook。当您的系统、插件或提供商向 RolChat 发送请求时,反方向也适用相同的规则。在处理之前,请按以下顺序验证每一次投递。
投递是什么样的
Section titled “投递是什么样的”POST /your/endpoint HTTP/1.1Content-Type: application/jsonUser-Agent: RolChat-Webhook/1.0X-RolChat-Timestamp: 1713615825X-RolChat-Signature: sha256=a1b2c3…X-RolChat-Nonce: nonce_01HKQN9…X-RolChat-Event-Id: evt_01HKQN9…
{ "id": "evt_01HKQN9…", "type": "conversation.created", "occurred_at": "…", "data": { … } }| 标头 | 含义 |
|---|---|
X-RolChat-Timestamp |
发送时的 Unix 秒数。属于签名消息的一部分。 |
X-RolChat-Signature |
sha256=<hex>。前缀标明算法,以便日后更改。 |
X-RolChat-Nonce |
该 HTTP 请求唯一的 ULID。重试时会使用新的 nonce。 |
X-RolChat-Event-Id |
事件自身的 ID。在重试之间保持不变;用它来去重。 |
出站投递只发送到 HTTPS 端点。创建订阅时会拒绝 HTTP URL。
签名消息由时间戳、一个字面意义上的点号以及逐字节的原始请求正文组成:
canonical = "<timestamp>.<body>"signature = hex( HMAC-SHA256( secret, canonical ) )请对您收到的字节进行签名,而不是对解析后的 JSON 重新序列化后的副本签名。空白或键顺序的变化都会导致签名失效。SHA-256 是唯一接受的算法。
请按顺序执行以下五项检查。Python、Node.js、Go、Ruby 和 PHP 的示例见验证示例页面。
- 时效。 如果
|now − X-RolChat-Timestamp| > 300秒则拒绝(400)。五分钟可以容忍时钟偏差,并使被截获的请求在此之后失去价值。请让您的服务器保持 NTP 同步。 - 签名。 计算期望值,并使用恒定时间比较(
hmac.compare_digest、crypto.timingSafeEqual、hash_equals)进行比对。不匹配时以401拒绝。普通字符串比较会泄露时间信息,不可接受。 - Nonce。 使用原子性的“不存在时才设置”(
SET NX EX 600)以 10 分钟 TTL 存储X-RolChat-Nonce。如果已存在,则以409拒绝:该请求是重放。如果您的 nonce 存储不可用,请以503安全失败,让 RolChat 重试;绝不要落入“接受”分支。 - 直到这时才解析正文。
- 按事件 ID 去重。 重试会带有新的 nonce,但
X-RolChat-Event-Id相同。如果您在过去七天内处理过该 ID,请直接返回200而不再处理。
一旦安全接受投递,请立即返回 2xx;实际工作异步进行。RolChat 会等待 15 秒的响应。
- Webhook 密钥是 64 个随机字节,采用 base64url 编码(约 86 个字符)。它在创建订阅或插件时生成,并且只显示一次。
- 请将其存储在您自己的保管库或环境变量中;RolChat 只保留加密副本。
- 建议 90 天后轮换;产品会显示提醒,但绝不会因为未轮换而停用订阅。
- 轮换使用重叠窗口:调用
POST /api/v1/plugins/{id}/webhook-secret/rotate后,新旧密钥在七天内都能通过验证,新的投递使用新密钥签名,旧密钥在窗口结束后删除。轮换的那一刻不会导致任何中断。 - 如果密钥泄露,
POST /api/v1/plugins/{id}/webhook-secret/revoke会立即使其失效、签发新密钥、向账户管理员发送电子邮件并写入审计记录。
非 2xx 响应或网络故障会安排一次重试:
| 尝试次数 | 距上一次的延迟 |
|---|---|
| 1 | 立即 |
| 2 | 15 秒 |
| 3 | 1 分钟 |
| 4 | 5 分钟 |
| 5 | 30 分钟 |
| 6 | 2 小时 |
| 7 | 8 小时 |
| 8 | 24 小时 |
408、429、5xx 和网络错误会被重试。400、401、403、404 和 410 不会重试:投递会直接进入死信队列,因为对被拒绝的签名或不存在的端点重试毫无意义。失败八次后,投递同样会进入死信队列。
死信保留 30 天,可在 设置 → 插件 → {插件} → Webhook 日志 → DLQ 中查看,管理员可以在那里重放(POST /api/v1/plugins/{id}/webhook-dlq/{entry_id}/replay)。
- 出站正文:默认 1 MB(可按插件配置)。发往 RolChat 的入站正文:5 MB。超出则返回
413。 - 插件可以声明
allowed_source_ips;RolChat 会在验证之前应用该允许列表。 - 您可以额外校验
RolChat-Webhook/1.0用户代理作为合理性检查。这不是一项安全控制措施。
发往 RolChat 的入站 Webhook
Section titled “发往 RolChat 的入站 Webhook”提供商使用各自的签名方案(Twilio、Telnyx、Bandwidth、WhatsApp 的 Meta、Stripe)向 RolChat 的 /api/v1/webhooks/{category}/{adapter} 发送请求。每个适配器都会验证其提供商的签名,规范化负载,并带上 RolChat 签名重新发布到内部事件总线。您自己的系统也可以使用上文所述的 X-RolChat-* 标头向 RolChat 发送请求,并使用拥有该端点的插件的密钥签名。
自动化 Webhook
Section titled “自动化 Webhook”Automation 模块的发送 HTTP 请求操作有自己的投递规则:
- 每个账户一个密钥。 在 Automation 页面查看或轮换(所有者和管理员)。轮换立即生效:没有重叠窗口,旧密钥会立刻停止通过验证,因此请先更新您的接收端,或接受短暂的空档。
- 等待时间: RolChat 等待响应 10 秒。
- 重试: 总共最多 6 次尝试。第 2 次尝试前的间隔为 30 秒,随后约为 2 分钟、8 分钟、32 分钟和 2 小时 8 分钟,每次最多浮动 20 %。单次间隔不会超过 6 小时。
- 哪些会重试: 网络错误、超时、
5xx、408、425和429。其他4xx响应、被拦截的地址或过大的正文不会重试:该投递会直接进入死信队列,最后一次尝试失败后也是如此。 - 标头:
X-RolChat-Signature、X-RolChat-Signature-V2、X-RolChat-Timestamp、X-RolChat-Delivery-Id、X-RolChat-Attempt。
测试您的接收端
Section titled “测试您的接收端”POST /api/v1/webhooks/test-signature 会向调用插件的端点发送一个合成的签名事件,以便您在真实流量到达之前端到端地验证实现。
- 针对解析后重新编码的 JSON 而不是原始字节进行验证。
- 使用
==比较签名。 - 按 nonce(每次重试都不同)而不是按事件 ID 去重。
- 时钟偏差超过五分钟。
- 在响应前执行耗时工作,结果在 15 秒时超时并再次收到同一事件。