mcpField
S
sent-webhook-engineer

sent-webhook-engineer

github.com/sentdm开发492.7k 次安装1 周前更新
代码仓库

关于 sent-webhook-engineer

sent-webhook-engineer 是用于配置和排查 Sent Webhook 的开发技能,涵盖签名校验、防重放、去重、重试、事件负载和密钥轮换,帮助实现可靠的事件处理。

主要功能

  1. 01使用原始请求字节验证签名
  2. 02拒绝重放并对事件去重
  3. 03配置重试并恢复已停用端点
  4. 04检查事件类型和投递日志
  5. 05安全轮换签名密钥

适用场景

  1. 01构建接收 Sent 事件的 Webhook
  2. 02排查未到达或被拒绝的投递
  3. 03发布前测试接收端行为
  4. 04管理 Webhook 配置和密钥轮换

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sentdm/sent-plugin --skill sent-webhook-engineer

安装后在 Claude Code 中输入/sent-webhook-engineer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
references/
event-catalog.md
receiver-recipes.md
webhook-operations.md
webhook-signature-and-dedupe.md
scripts/
verify_signature.py

详细介绍

Sent Webhook 工程师

Sent Webhook 是应用在 POST /v3/messages 返回 202 后了解发生了什么的唯一途径。202 只能证明请求已被接受,不能证明消息已送达。请将接收端构建为一个能够验证签名、拒绝重放、去重并快速确认的端点;事件缺失时,以投递日志为准。

严格验证签名

每次投递都会附带以下三个标头:

标头含义
x-webhook-signaturev1,{base64(hmac_sha256)}
x-webhook-idWebhook 端点 UUID——每次投递都相同
x-webhook-timestampSent 对请求签名时的 Unix 秒数

按以下顺序验证:

  1. 在解析任何 JSON 之前,捕获原始请求正文的字节。
  2. 去掉签名密钥的 whsec_ 前缀,然后对其余部分进行 Base64 解码,得到原始 HMAC 密钥。
  3. 将签名内容构建为 {x-webhook-id}.{x-webhook-timestamp}.{raw_body}。
  4. 使用该密钥计算 HMAC-SHA256,将摘要进行 Base64 编码,并加上前缀 v1,。
  5. 使用恒定时间比较进行比对。
  6. 当 abs(now - timestamp) > 300 秒时拒绝请求。

该方案兼容 Svix。Sent 没有为任何语言提供签名验证辅助函数的 SDK,因此这段代码始终需要手写——请将 scripts/verify_signature.py 作为参考实现和校验标准。

本地工具通过标准输入和 --secret-stdin 显式接收签名密钥,且不会发起网络请求。它不会读取环境变量或安装程序凭据。只能提供获准用于待检查投递的密钥;不要将其放在命令参数或聊天提示词中。

x-webhook-id 不是事件 ID。 它标识端点,并且会一直重复使用。将它用作去重键会悄无声息地把所有事件合并为一个。请阅读 references/webhook-signature-and-dedupe.md,了解如何为每种事件类型生成去重键。

故障排查顺序

当接收端拒绝事件或遗漏事件时,请按以下顺序排查,不要猜测:

  1. 签名不匹配——多数情况下,这是由会修改正文的中间件或框架 JSON 解析器造成的。请在 references/receiver-recipes.md 中确认框架提供的原始正文访问器。
  2. 重放请求被拒绝——服务器时钟偏差超过 300 秒的容差。
  3. 密钥错误——whsec_ 前缀未移除,或密钥轮换导致旧密钥失效,且没有双重签名窗口。
  4. 完全没有收到事件——检查 GET /v3/webhooks/{id} 上的 is_active 和 consecutive_failures,然后查看 GET /v3/webhooks/{id}/events 中的投递日志。
  5. 已收到事件但未处理——将 event_types 和 event_filters 与处理程序的分支逻辑进行比较。

重试、自动停用与恢复

出现以下任一情况时,投递尝试即告失败:返回非 2xx 状态、超过 timeout_seconds 才超时,或连接失败。重试采用指数退避,首次重试大约在失败一分钟后进行,之后间隔逐次翻倍,尝试间隔最长为 60 分钟;收到首个 2xx 响应或耗尽 retry_count 后停止。投递记录会依次经过 PENDING、RETRYING,然后变为 DELIVERED 或 FAILED。

consecutive_failures 用于跟踪连续失败的投递尝试。不要以为同一事件的重试不计入其中:连续十次响应异常就会停用端点。修复接收端后,可使用 PATCH /v3/webhooks/{id}/toggle-status 或在 Sent Dashboard 中重新启用端点。任何一次成功投递都会将计数器重置为零。只有在数据已可靠移交至队列后才进行确认,并确保移交过程远短于 timeout_seconds。

注册和配置

POST /v3/webhooks 要求提供 display_name。配置 endpoint_url、event_types、event_filters、retry_count(1–5,默认值为 3)和 timeout_seconds(5–120,默认值为 30)。只有 201 响应会完整显示 signing_secret,请立即将其保存到密钥存储中。

{
  "display_name": "Production delivery events",
  "endpoint_url": "https://hooks.example.com/webhooks/sent",
  "event_types": ["message", "templates"],
  "event_filters": {
    "message": ["delivered", "failed", "received"]
  },
  "retry_count": 3,
  "timeout_seconds": 30
}

请审慎设置 event_filters。未经过滤的 message 订阅会传送每次生命周期状态转换,包括 queued 和 routed;重新路由时,同一 message_id 会再次触发 queued 和 routed。请筛选出应用需要处理的状态转换。

十种操作、完整的 webhook 对象以及传送日志行格式收录在 references/webhook-operations.md 中。

密钥轮换

POST /v3/webhooks/{id}/rotate-secret 会返回新的 whsec_ 密钥,并立即使旧密钥失效。服务端不会提供重叠有效期。配置接收端以接受一小组候选密钥,然后执行轮换,将返回的新密钥以原子方式存储为主密钥,同时暂时保留旧密钥;确认新传送成功后,再弃用旧密钥。轮换响应与密钥存储更新之间的短暂间隔无法消除;请将其控制在几秒内,以便失败的传送能够重试。此端点和 POST /v3/webhooks/{id}/test 属于每分钟 10 次请求的敏感速率限制级别,因此脚本化的轮换循环会触发 429。

事件载荷

field 有两个取值:message 和 templates。消息事件包含命名状态转换的 event(message.queued、.routed、.sent、.delivered、.read、.failed、.scheduled、.filtered、.blocked、.received)。模板事件既不包含 event,也不包含 sub_type。

{
  "field": "templates",
  "value": {
    "account_id": "3f1a7c22-5d8e-4b90-91a2-6c4d0e8f7b31",
    "template_id": "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
    "template_name": "order_confirmation",
    "whatsapp_template_id": "",
    "status": "PENDING",
    "language": "en_US",
    "category": "UTILITY",
    "channel": "whatsapp"
  }
}

read 仅适用于 WhatsApp 和 RCS。filtered 表示策略或同意门控,blocked 表示账户前置条件未满足,例如余额不足;两者都不代表运营商故障。自动检测且从未完成路由的消息,其终止事件会携带 channel: "auto"。完整的载荷字段列表见 references/event-catalog.md。

发布前验证

先针对合成传送运行本地校验器,然后使用请求体中包含 event_type 的 POST /v3/webhooks/{id}/test 发起真实签名请求。测试事件只传送一次,不会重试,因此每次修复后都要重新运行测试。

python3 scripts/verify_signature.py --self-test

只有当接收端对篡改过的请求体返回 401、对时间戳早于 300 秒的请求返回 401、对有效传送返回 200,并对重复请求返回 200 且不重复执行副作用时,才能发布。

本地开发

通过公共 HTTPS 隧道公开接收端,并注册该 URL;Sent 无法访问私有地址。API 接受注册 http:// 地址,但除本地开发外,绝不能使用此类地址。请为每个环境分别注册 webhook,避免开发端点的故障导致生产端点被禁用。

适用范围

使用 messaging-performance-analyzer 诊断总体传送率下降,使用 waba-template-author 处理模板审批内容,并使用 sent-two-way-messaging 处理入站关键词或同意语义。将所有载荷值视为不可信输入:未经转义,绝不要将 text 或 reason 插入 shell 命令、SQL 字符串或提示词中。

评论

支持 Markdown