WhatsApp Webhook指南:事件、安全与重试
WhatsApp Webhook的工作方式:消息与状态事件、可靠的处理程序、Meta签名校验、重试机制、幂等处理,以及事件丢失时的恢复办法,帮助开发者稳定接收送达状态。
作者: OmniChannels.ai Editorial Team · 审阅: BroadNet Messaging Solutions Team
集成
简短回答
WhatsApp Webhook是您服务器上用于接收事件的HTTPS端点:包括您所发消息的送达状态,以及客户发给您的消息。在OmniChannels.ai中,状态以SUBMITTED、SENT、DELIVERED、READ、FAILED或REJECTED的形式到达。请保存原始请求体,快速返回2xx,按消息ID去重并异步处理。Meta会为自己的Webhook签名;我们的文档没有定义签名请求头,所以请在传输层和应用层保护您的端点。
本页内容
要点速览
- Webhook把状态和收到的消息推送到您的HTTPS端点,让您无需轮询;WhatsApp的状态被统一为SUBMITTED、SENT、DELIVERED、READ、FAILED和REJECTED。
- 可靠的处理程序按顺序做三件事:保存原始事件,返回2xx,然后异步且幂等地处理。
- 状态可能乱序或重复到达,所以请为其排序,绝不要让迟到的SENT覆盖已存储的DELIVERED。
- Meta会对发往应用的Webhook使用X-Hub-Signature-256请求头签名,但OmniChannels.ai的API规范没有定义Webhook签名,所以请使用HTTPS、不可猜测的路径和请求体校验。
- 如果错过了事件,请通过bulk-insights端点按批次读取WhatsApp状态,而不是重发消息。
本指南面向需要可靠接收WhatsApp事件的工程师:包括所发消息的送达状态,以及客户发来的消息。内容说明Webhook如何工作、处理程序里要构建什么、Meta的Webhook与我们的Webhook在安全方面有何不同,以及事件丢失时该怎么办。
什么是WhatsApp Webhook,为什么要使用?
WhatsApp Webhook是HTTP回调。当一条消息的状态发生变化,或客户给您写信时,平台会向您的端点发送带有JSON请求体的POST。您无需轮询,事件一发生您就能看到。
有两类事件很重要:
- 您所发消息的**状态事件:**已排队、已发送、已送达、已读、失败。
- 客户发来的**收到的消息,**它们会开启或延长24小时客户服务窗口。请参阅我们的窗口指南。
发送调用只告诉您消息已排队。Webhook才是您得知每位收件人发生了什么的地方。
我会收到哪些事件和状态?
在OmniChannels.ai上,每个渠道报告的都是同一套统一的状态,所以一个处理程序就可以服务WhatsApp、短信和其他渠道。
| 状态 | 含义 | 是否为最终状态 |
|---|---|---|
SUBMITTED |
已被平台接受并排队 | 否 |
SENT |
已交给渠道 | 否 |
DELIVERED |
已在收件人的设备上确认 | 通常是;之后可能出现READ |
READ |
已被收件人打开(在渠道报告已读的情况下) | 是 |
FAILED |
未送达;事件在有原因时会附带原因 | 是 |
REJECTED |
被拒绝,例如内容被封或发件人未注册 | 是 |
请把状态视为一个可能乱序到达的推进过程,并预期随着时间推移会出现新的值。不要因为遇到不认识的状态而失败。请参阅消息送达状态详解。
如何设置端点?
开放一个HTTPS端点,把URL交给您的服务商,然后用您自己的号码测试。
- 创建一个接受带JSON请求体的
POST的端点,例如https://api.example.com/webhooks/omnichannels。 - 把URL发给我们的团队或您的账户管理员,让其在您的账户上配置。转售商可以为每个子账户设置单独的Webhook。
- 向您自己的号码发送一条测试消息,确认事件已到达。
开发期间,ngrok之类的隧道工具可以为您本地的服务器提供一个公开的HTTPS URL。如果您还把应用直接连接到Meta,那么Meta的端点验证是单独的一步:一个带有验证token和hub.challenge值的GET请求,您以200并带上该challenge回应,详见Meta的Webhook端点文档。
负载是什么样的?
因渠道而异。API规范没有定义单一的Webhook结构,所以请先记录原始请求体,再根据您账户上的真实事件映射所需字段。示例展示的是一个送达事件所携带信息的类型,仅供说明。
{
"messageId": "2603121407537573731",
"channel": "SMS",
"destination": "447700900123",
"status": "DELIVERED",
"timestamp": "2026-10-10T09:15:42Z",
"reason": null
}
在核对真实事件之前,不要依据这些字段名编写代码。请向您的开通工程师索取每个渠道的示例负载,并将其保存为测试夹具。更全面的文档请参阅Webhook参考。
如何编写可靠的处理程序?
按顺序做三件事:保存原始事件,以2xx确认,然后异步处理。下面的处理程序遵循我们文档中的模式。
import express from "express";
const app = express();
// 捕获原始请求体,以便按收到的原样存储。
app.post(
"/webhooks/omnichannels",
express.json({ verify: (req, _res, buf) => { req.rawBody = buf.toString("utf8"); } }),
async (req, res) => {
try {
await db.webhookEvents.insert({ receivedAt: new Date(), body: req.rawBody });
} catch (err) {
return res.sendStatus(500); // 存储失败:让事件被重试
}
res.sendStatus(200); // 尽快确认
queue.publish("status-events", req.body); // 把真正的工作放到别处
},
);
// 工作进程:幂等处理
async function processEvent(event) {
const id = event.messageId ?? event.message_id ?? event.id; // 按渠道映射
const status = event.status;
const rank = { SUBMITTED: 1, SENT: 2, DELIVERED: 3, READ: 4, FAILED: 4, REJECTED: 4 };
const current = await db.messages.findOne({ providerId: id });
if (!current) return; // 未知消息:记录日志并调查
if ((rank[status] ?? 0) <= (rank[current.status] ?? 0)) return; // 重复或过时
await db.messages.update({ providerId: id }, { status, updatedAt: new Date() });
}
from datetime import datetime, timezone
from flask import Flask, request
app = Flask(__name__)
RANK = {"SUBMITTED": 1, "SENT": 2, "DELIVERED": 3, "READ": 4, "FAILED": 4, "REJECTED": 4}
@app.post("/webhooks/omnichannels")
def omnichannels_webhook():
raw = request.get_data(as_text=True)
try:
db.webhook_events.insert({"received_at": datetime.now(timezone.utc), "body": raw})
except Exception:
return "", 500 # 存储失败:让事件被重试
queue.publish("status-events", raw) # 异步处理
return "", 200 # 尽快确认
db和queue对象是您自己的存储和任务队列。rank映射使处理具备幂等性:重复或过时的事件不是推进,因此会被忽略。
可靠处理程序的规则
- **在几秒钟之内响应。**响应之前不要做繁重的工作。
- **只有在事件已保存之后才返回
2xx。**如果存储失败,请返回5xx。 - **按消息ID去重。**同一状态可能多次到达。
- **绝不要在处理程序中重发消息。**请在检查状态之后,于单独的任务中决定重试。
- **保留原始请求体,**用于排查问题,以及在修复后重放事件。
如何保护WhatsApp Webhook的安全?
这取决于谁向您的端点发送。Meta会为发送到您在Meta注册的应用的Webhook签名。OmniChannels.ai的API规范没有定义Webhook签名请求头,所以不要等待它。
| Meta到您自己的应用 | OmniChannels.ai到您的端点 | |
|---|---|---|
| 签名 | 按Meta文档,使用X-Hub-Signature-256请求头 |
API规范中未定义 |
| 校验 | 使用应用密钥对原始负载计算HMAC-SHA256,并与请求头比对 | 不适用 |
| 您的控制措施 | 对每个POST检查签名 |
带有效证书的HTTPS、不可猜测的路径、严格的请求体校验 |
对于Meta的方案,Meta的文档说明,应使用您的应用密钥对JSON负载生成HMAC-SHA256哈希,并与X-Hub-Signature-256请求头中sha256=之后的值比对。HMAC本身由RFC 2104定义。请对原始字节计算,而不是对重新序列化的对象计算,并以恒定时间比较。
对于我们的端点:
- 仅使用HTTPS,并配有效证书。
- 使用较长且不可猜测的路径,泄露时请轮换。
- 校验请求体的结构,拒绝任何意外内容。
- 把事件视为通知。在执行退款或欺诈锁定等不可逆操作之前,请通过轮询调用确认状态。
投递失败时会怎样?
重试取决于发送方。Meta的文档称,它会以递减的频率重试失败的投递,最长7天,这可能产生重复通知。这是即使您很少看到重复,也应具备幂等性的理由。对于来自我们平台的事件,请与您的开通工程师确认重试行为,而不要假定它与Meta相同。
无论哪种情况,既要防重复,也要防丢失:您的端点可能在部署期间宕机,防火墙规则也可能悄悄丢弃请求。
如何恢复丢失的事件?
轮询批次,而不是重发消息。
| 需求 | 调用 | 说明 |
|---|---|---|
| 读取每个收件人的WhatsApp状态 | GET /whatsapp/message/bulk-insights |
按status或destination筛选一个批次;用page和size分页 |
| 重新推送短信送达回执 | POST /sms/resendDlr |
按startDate、endDate、senderId和messageStatus筛选 |
curl "https://api.broadnet.me/whatsapp/message/bulk-insights?batch_Id=2609091405000000001&page=0&size=100" \
-H "Authorization: Bearer $BROADNET_TOKEN"
batch_Id是发送调用返回的ID,page从零开始。请运行一个夜间对账任务,读取前一天的批次并填补任何缺口。
故障排查清单
- URL是否配置在了正确的账户或子账户上?
- 端点能否通过带有效证书的HTTPS访问?
- 它是否迅速返回
2xx,还是超时? - 是否有防火墙或WAF规则在拦截请求?
- 您是否在解析之前记录了原始请求体,让解析错误无法掩盖事件?
实战示例:一家零售商的通知管道
“Souq Rafiq”(虚构)是阿联酋的一家零售商,通过WhatsApp发送订单更新,并希望在订单工具中获得准确的状态。
- 订单通过
sendbulk发送,batchId存储在订单上。请参阅通过API发送WhatsApp消息。 - Webhook端点把每个事件写入一张表,然后返回
200。 - 工作进程为状态排序,并更新订单的消息状态。
FAILED事件会为客服创建一个带原因的任务,并按渠道回退考虑短信回退。- 收到的客户消息被转到共享收件箱,让客服可以在窗口内回复。
- 夜间任务对昨天的批次调用
bulk-insights,并纠正任何遗漏。
常见错误有哪些?
- **响应之前做繁重的工作,**导致超时和重复投递。
- **先确认再存储,**导致存储失败时事件丢失。
- **相信顺序。**迟到的
SENT覆盖了DELIVERED。 - **依据示意性负载编写代码,**而不是依据真实事件。
- **检查签名之前重新序列化JSON,**会改变字节。
- **在处理程序中重发,**可能造成循环。
- **局限:**本指南描述的是我们文档中记载的行为。您账户的重试策略和负载字段,应在开通期间确认。
OmniChannels.ai 如何提供帮助
OmniChannels.ai把WhatsApp、短信、RCS和其他渠道的送达状态和收到的消息,以一种统一的格式推送到您的Webhook,并为转售商提供按子账户划分的单独Webhook。BroadNet是Meta官方的Business Solution Provider,因此Meta自身的Webhook要求已为您处理,您只需使用我们的API。请阅读Webhook参考和WhatsApp API文档,或查看事件在共享收件箱中的呈现方式。
常见问题
什么是WhatsApp Webhook?
WhatsApp Webhook是您服务器上的一个URL,每当发生某些事情(如消息被送达、已读或失败,或客户给您发消息)时,平台就会以HTTP POST调用它。它取代了轮询。您开放一个HTTPS端点,与服务商共享,并处理它接收到的JSON事件。
如何校验WhatsApp Webhook的签名?
对于Meta直接发送到您自己应用的Webhook,Meta的文档说明,应使用您的应用密钥对原始负载计算HMAC-SHA256,并与X-Hub-Signature-256请求头比对。对于OmniChannels.ai的Webhook,API规范没有定义签名请求头,所以请依靠HTTPS、不可猜测的路径和请求体校验。
如果我的Webhook端点宕机了会怎样?
Meta的文档称,它会以递减的频率重试失败的投递,最长7天,这可能产生重复。对于来自我们平台的事件,请与您的开通工程师确认重试行为,并使用bulk-insights端点读取某个批次中每个收件人的WhatsApp状态。对于短信,resendDlr端点可以重新推送回执。
如何处理重复的Webhook事件?
让处理具备幂等性。按消息ID查找消息,将传入状态的等级与已存储的比较,并忽略那些不是推进的事件。同一事件应用两次,效果必须与一次相同。请保存每个事件的原始请求体,以便在修复错误后重放。
我的端点必须多快响应?
几秒钟之内。保存原始事件,返回2xx,并把真正的工作放到队列或工作进程中。在响应之前,不要调用其他服务、发送消息或运行繁重的查询。如果存储失败,请返回5xx,让发送方可以重试,而不是确认一个您已经丢失的事件。
可以用Webhook自动发送回复吗?
可以,但不要在处理程序内部进行。先确认事件,然后在单独的任务中决定回复。在24小时客户服务窗口内,您可以发送自由格式消息;窗口之外则需要已获批的模板。绝不要在Webhook处理程序本身里重发消息,以免出现循环和重复。
资料来源
继续阅读
更多指南集成
WhatsApp消息API发送示例:cURL、Node、Python、PHP
如何用API发送WhatsApp消息:提供可运行的cURL、Node.js、Python和PHP示例,涵盖模板消息与会话消息、送达状态跟踪以及常见错误处理,方便开发者快速接入。
10 分钟阅读
集成
WhatsApp 对接 CRM:3 种集成方式与数据同步
WhatsApp CRM 集成详解:现成连接器、中间件或 API 加 webhook 三种方式,数据流向、字段映射、同意状态同步,以及选型检查清单。
10 分钟阅读
WhatsApp
2026年WhatsApp Business API免费消息:哪些不收费
2026年WhatsApp Business API哪些消息免费:每个号码每月1,000条免费服务消息、72小时的免费入口窗口,以及哪些消息要收费,帮助企业准确测算成本。
9 分钟阅读
