跳转到内容

Webhook

RolChat 以签名的 HTTP POST 请求将事件投递到您的系统。本页介绍插件 Webhook;由 Automation 规则发送的 Webhook 在重试、等待时间和密钥轮换方面有所不同,参见自动化 Webhook。当您的系统、插件或提供商向 RolChat 发送请求时,反方向也适用相同的规则。在处理之前,请按以下顺序验证每一次投递。

POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: RolChat-Webhook/1.0
X-RolChat-Timestamp: 1713615825
X-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 的示例见验证示例页面。

  1. 时效。 如果 |now − X-RolChat-Timestamp| > 300 秒则拒绝(400)。五分钟可以容忍时钟偏差,并使被截获的请求在此之后失去价值。请让您的服务器保持 NTP 同步。
  2. 签名。 计算期望值,并使用恒定时间比较(hmac.compare_digest、crypto.timingSafeEqual、hash_equals)进行比对。不匹配时以 401 拒绝。普通字符串比较会泄露时间信息,不可接受。
  3. Nonce。 使用原子性的“不存在时才设置”(SET NX EX 600)以 10 分钟 TTL 存储 X-RolChat-Nonce。如果已存在,则以 409 拒绝:该请求是重放。如果您的 nonce 存储不可用,请以 503 安全失败,让 RolChat 重试;绝不要落入“接受”分支。
  4. 直到这时才解析正文。
  5. 按事件 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 用户代理作为合理性检查。这不是一项安全控制措施。

提供商使用各自的签名方案(Twilio、Telnyx、Bandwidth、WhatsApp 的 Meta、Stripe)向 RolChat 的 /api/v1/webhooks/{category}/{adapter} 发送请求。每个适配器都会验证其提供商的签名,规范化负载,并带上 RolChat 签名重新发布到内部事件总线。您自己的系统也可以使用上文所述的 X-RolChat-* 标头向 RolChat 发送请求,并使用拥有该端点的插件的密钥签名。

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。

POST /api/v1/webhooks/test-signature 会向调用插件的端点发送一个合成的签名事件,以便您在真实流量到达之前端到端地验证实现。

  • 针对解析后重新编码的 JSON 而不是原始字节进行验证。
  • 使用 == 比较签名。
  • 按 nonce(每次重试都不同)而不是按事件 ID 去重。
  • 时钟偏差超过五分钟。
  • 在响应前执行耗时工作,结果在 15 秒时超时并再次收到同一事件。