sent-webhook-engineer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSent Webhook Engineer
Sent Webhook 开发指南
Sent webhooks are the only way an application learns what happened after returns . The proves acceptance, never delivery. Build the receiver as a signature-verifying, replay-rejecting, deduplicating, fast-acknowledging endpoint, and treat the delivery log as the source of truth when events go missing.
POST /v3/messages202202Sent webhooks是应用在返回后,了解后续事件的唯一方式。仅表示请求已被接收,绝不代表消息已送达。接收器需构建为具备签名验证、重放拒绝、事件去重、快速确认能力的端点,当事件丢失时,需以交付日志作为唯一可信来源进行排查。
POST /v3/messages202202Signature verification, exactly
签名验证步骤详解
Three headers arrive with every delivery:
| Header | Meaning |
|---|---|
| |
| The webhook endpoint UUID — identical on every delivery |
| Unix seconds when Sent signed the request |
Verification procedure, in order:
- Capture the raw request body bytes before any JSON parsing.
- Strip the prefix from the signing secret, then base64-decode the remainder to obtain the raw HMAC key.
whsec_ - Build the signed content as .
{x-webhook-id}.{x-webhook-timestamp}.{raw_body} - Compute HMAC-SHA256 with that key, base64-encode the digest, and prefix .
v1, - Compare with a constant-time comparison.
- Reject when seconds.
abs(now - timestamp) > 300
The scheme is Svix-compatible. No Sent SDK ships a verification helper in any language, so this code is always hand-written — use scripts/verify_signature.py as the reference implementation and oracle.
x-webhook-id每次交付都会附带三个请求头:
| 请求头 | 含义 |
|---|---|
| |
| Webhook 端点的UUID —— 每次交付的该值均相同 |
| Sent对请求进行签名时的Unix时间戳(秒) |
验证流程按以下顺序执行:
- 在进行任何JSON解析前,捕获原始请求体字节。
- 移除签名密钥的前缀,然后对剩余部分进行base64解码,得到原始HMAC密钥。
whsec_ - 构建签名内容:。
{x-webhook-id}.{x-webhook-timestamp}.{raw_body} - 使用上述密钥计算HMAC-SHA256,对摘要进行base64编码,并添加前缀。
v1, - 使用常量时间比较法对比计算结果与请求头中的签名。
- 若秒,则拒绝该请求。
abs(当前时间 - 时间戳) > 300
该签名方案与Svix兼容。Sent未在任何语言的SDK中提供验证工具,因此相关代码需手动编写——可参考scripts/verify_signature.py作为实现示例和权威参考。
x-webhook-idFailure triage order
故障排查顺序
When a receiver rejects or misses events, work this sequence rather than guessing:
- Signature mismatch — a body-mutating middleware or framework JSON parser is the cause in the majority of cases. Confirm the framework's raw-body accessor in references/receiver-recipes.md.
- Replay rejection — server clock skew beyond the 300-second tolerance.
- Wrong secret — the prefix was left in place, or a rotation invalidated the old secret with no dual-signing window.
whsec_ - Nothing arriving at all — check and
is_activeonconsecutive_failures, then read the delivery log atGET /v3/webhooks/{id}.GET /v3/webhooks/{id}/events - Events arriving but unhandled — compare and
event_typesagainst what the handler branches on.event_filters
当接收器拒绝事件或事件丢失时,请按以下顺序排查,而非盲目猜测:
- 签名不匹配——大多数情况下是由于修改请求体的中间件或框架JSON解析器导致的。请在references/receiver-recipes.md中确认框架的原始请求体获取方式。
- 重放请求被拒绝——服务器时钟偏差超出300秒的容忍范围。
- 密钥错误——保留了前缀,或密钥轮换时未设置双签名窗口导致旧密钥失效。
whsec_ - 完全无事件到达——调用检查
GET /v3/webhooks/{id}和is_active字段,然后通过consecutive_failures查看交付日志。GET /v3/webhooks/{id}/events - 事件已到达但未被处理——对比和
event_types与处理器分支逻辑是否匹配。event_filters
Retry, auto-disable, and recovery
重试、自动禁用与恢复机制
A delivery attempt fails on any non-2xx status, a timeout past , or a connection failure. Retries use exponential backoff with the first retry roughly one minute after the failure, doubling thereafter and capped at 60 minutes between attempts, stopping on the first 2xx or when is exhausted. Delivery rows move through , , and then or .
timeout_secondsretry_countPENDINGRETRYINGDELIVEREDFAILEDconsecutive_failuresPATCH /v3/webhooks/{id}/toggle-statustimeout_seconds当返回非2xx状态码、超过超时时间或连接失败时,交付尝试视为失败。重试采用指数退避策略:首次重试约在失败后1分钟,后续重试间隔翻倍,最大间隔为60分钟,直到返回2xx状态码或耗尽次数。交付记录会依次经历、状态,最终变为或。
timeout_secondsretry_countPENDINGRETRYINGDELIVEREDFAILEDconsecutive_failuresPATCH /v3/webhooks/{id}/toggle-statustimeout_secondsRegistration and configuration
注册与配置
POST /v3/webhooksdisplay_nameendpoint_urlevent_typesevent_filtersretry_counttimeout_seconds201signing_secretjson
{
"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
}Set deliberately. An unfiltered subscription delivers every lifecycle transition including and , and reroutes re-fire and on the same . Filter to the transitions the application acts on.
event_filtersmessagequeuedroutedqueuedroutedmessage_idThe ten operations, the full webhook object, and the delivery-log row shape are catalogued in references/webhook-operations.md.
调用时需提供。可配置、、、(取值1–5,默认3)和(取值5–120,默认30)。响应是完整显示的唯一位置——请立即将其存储至密钥管理系统。
<!-- sent-webhook-request -->
POST /v3/webhooksdisplay_nameendpoint_urlevent_typesevent_filtersretry_counttimeout_seconds201signing_secretjson
{
"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_filtersmessagequeuedroutedmessage_idqueuedrouted完整的10项操作、webhook对象结构以及交付日志记录格式可参考references/webhook-operations.md。
Secret rotation
密钥轮换
POST /v3/webhooks/{id}/rotate-secretwhsec_POST /v3/webhooks/{id}/test调用会返回新的格式密钥,并立即失效旧密钥。服务器端不提供新旧密钥的重叠使用窗口。请将接收器配置为接受一组候选密钥,执行轮换操作后,原子性地将返回的新密钥设为主要密钥,同时暂时保留旧密钥,确认新交付正常后再移除旧密钥。轮换响应与密钥存储更新之间的短暂间隔无法消除,请将该间隔控制在数秒内,以便失败的交付能够重试。该接口与接口属于敏感接口,速率限制为每分钟10次请求,因此脚本化的轮换循环可能会触发429错误。
POST /v3/webhooks/{id}/rotate-secretwhsec_POST /v3/webhooks/{id}/testEvent payloads
事件负载
Two values exist: and . Message events carry an naming the transition (, , , , , , , , , ). Template events carry neither nor .
fieldmessagetemplateseventmessage.queued.routed.sent.delivered.read.failed.scheduled.filtered.blocked.receivedeventsub_typejson
{
"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"
}
}readfilteredblockedchannel: "auto"存在两种值:和。Message事件包含字段,用于标识状态转换(如、、、、、、、、、)。Template事件不包含或字段。
fieldmessagetemplateseventmessage.queued.routed.sent.delivered.read.failed.scheduled.filtered.blocked.receivedeventsub_typejson
{
"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"
}
}readfilteredblockedchannelautoVerification before shipping
上线前验证
Run the local oracle against a synthetic delivery, then use with an in the body for a real signed request. The test event is delivered once with no retry, so re-run it after each fix.
POST /v3/webhooks/{id}/testevent_typebash
python3 scripts/verify_signature.py --self-testShip only when the receiver returns for a tampered body, for a timestamp older than 300 seconds, for a valid delivery, and for a duplicate without repeating side effects.
401401200200使用本地验证工具对模拟交付进行测试,然后调用接口并在请求体中指定,获取真实的签名请求。测试事件仅交付一次且不重试,因此每次修复后需重新运行测试。
POST /v3/webhooks/{id}/testevent_typebash
python3 scripts/verify_signature.py --self-test仅当接收器满足以下条件时才可上线:篡改请求体时返回、时间戳超过300秒时返回、有效交付时返回、重复事件时返回且不会重复执行副作用操作。
401401200200Local development
本地开发
Expose the receiver through a public HTTPS tunnel and register that URL; Sent cannot reach a private address. Registering is accepted by the API but should never be used outside local work. Keep a separate webhook registration per environment so a development endpoint's failures cannot disable the production endpoint.
http://需通过公共HTTPS隧道暴露接收器并注册该URL;Sent无法访问私有地址。API允许注册地址,但仅可用于本地开发环境。请为每个环境单独注册webhook,避免开发端点的故障导致生产端点被禁用。
http://Boundaries
职责边界
Diagnose aggregate delivery-rate regressions with , template approval content with , and inbound keyword or consent semantics with . Treat every payload value as untrusted input: never interpolate or into a shell command, SQL string, or prompt without escaping.
messaging-performance-analyzerwaba-template-authorsent-two-way-messagingtextreason使用诊断整体交付率下降问题,使用处理模板审核内容,使用处理入站关键词或合规语义。请将所有负载值视为不可信输入:切勿在未转义的情况下将或插入shell命令、SQL语句或提示词中。
messaging-performance-analyzerwaba-template-authorsent-two-way-messagingtextreason