---
title: "WhatsApp Webhook指南：事件、安全与重试"
description: "WhatsApp Webhook的工作方式：消息与状态事件、可靠的处理程序、Meta签名校验、重试机制、幂等处理，以及事件丢失时的恢复办法，帮助开发者稳定接收送达状态。"
canonical: "https://omnichannels.ai/zh/blog/whatsapp-webhooks-guide/"
language: "zh"
updated: "2026-10-16"
publisher: "BroadNet Technologies"
brand: "OmniChannels.ai"
category: "integration"
primaryQuery: "WhatsApp Webhook"
keywords: ["WhatsApp Business API Webhook", "WhatsApp Webhook状态事件", "X-Hub-Signature-256 WhatsApp", "WhatsApp Webhook重试", "WhatsApp送达状态Webhook", "幂等Webhook处理", "WhatsApp收到消息Webhook"]
published: "2026-10-16"
author: "OmniChannels.ai Editorial Team"
reviewer: "BroadNet Messaging Solutions Team"
readingMinutes: 10
answer: "WhatsApp Webhook是您服务器上用于接收事件的HTTPS端点：包括您所发消息的送达状态，以及客户发给您的消息。在OmniChannels.ai中，状态以SUBMITTED、SENT、DELIVERED、READ、FAILED或REJECTED的形式到达。请保存原始请求体，快速返回2xx，按消息ID去重并异步处理。Meta会为自己的Webhook签名；我们的文档没有定义签名请求头，所以请在传输层和应用层保护您的端点。"
takeaways: ["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状态，而不是重发消息。"]
faq: 
  - q: "什么是WhatsApp Webhook？"
    a: "WhatsApp Webhook是您服务器上的一个URL，每当发生某些事情（如消息被送达、已读或失败，或客户给您发消息）时，平台就会以HTTP POST调用它。它取代了轮询。您开放一个HTTPS端点，与服务商共享，并处理它接收到的JSON事件。"
  - q: "如何校验WhatsApp Webhook的签名？"
    a: "对于Meta直接发送到您自己应用的Webhook，Meta的文档说明，应使用您的应用密钥对原始负载计算HMAC-SHA256，并与X-Hub-Signature-256请求头比对。对于OmniChannels.ai的Webhook，API规范没有定义签名请求头，所以请依靠HTTPS、不可猜测的路径和请求体校验。"
  - q: "如果我的Webhook端点宕机了会怎样？"
    a: "Meta的文档称，它会以递减的频率重试失败的投递，最长7天，这可能产生重复。对于来自我们平台的事件，请与您的开通工程师确认重试行为，并使用bulk-insights端点读取某个批次中每个收件人的WhatsApp状态。对于短信，resendDlr端点可以重新推送回执。"
  - q: "如何处理重复的Webhook事件？"
    a: "让处理具备幂等性。按消息ID查找消息，将传入状态的等级与已存储的比较，并忽略那些不是推进的事件。同一事件应用两次，效果必须与一次相同。请保存每个事件的原始请求体，以便在修复错误后重放。"
  - q: "我的端点必须多快响应？"
    a: "几秒钟之内。保存原始事件，返回2xx，并把真正的工作放到队列或工作进程中。在响应之前，不要调用其他服务、发送消息或运行繁重的查询。如果存储失败，请返回5xx，让发送方可以重试，而不是确认一个您已经丢失的事件。"
  - q: "可以用Webhook自动发送回复吗？"
    a: "可以，但不要在处理程序内部进行。先确认事件，然后在单独的任务中决定回复。在24小时客户服务窗口内，您可以发送自由格式消息；窗口之外则需要已获批的模板。绝不要在Webhook处理程序本身里重发消息，以免出现循环和重复。"
sources: 
  - title: "Webhook概览——WhatsApp Business平台，Meta for Developers"
    url: "https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview"
  - title: "创建Webhook端点——WhatsApp Business平台，Meta for Developers"
    url: "https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/create-webhook-endpoint"
  - title: "RFC 2104：HMAC：用于消息认证的密钥哈希"
    url: "https://www.rfc-editor.org/rfc/rfc2104"
---

# WhatsApp Webhook指南：事件、安全与重试

Canonical: https://omnichannels.ai/zh/blog/whatsapp-webhooks-guide/
Language: zh
Publisher: OmniChannels.ai by BroadNet Technologies

本指南面向需要可靠接收WhatsApp事件的工程师：包括所发消息的送达状态，以及客户发来的消息。内容说明Webhook如何工作、处理程序里要构建什么、Meta的Webhook与我们的Webhook在安全方面有何不同，以及事件丢失时该怎么办。

## 什么是WhatsApp Webhook，为什么要使用？

WhatsApp Webhook是HTTP回调。当一条消息的状态发生变化，或客户给您写信时，平台会向您的端点发送带有JSON请求体的`POST`。您无需轮询，事件一发生您就能看到。

有两类事件很重要：

- 您所发消息的**状态事件：**已排队、已发送、已送达、已读、失败。
- 客户发来的**收到的消息，**它们会开启或延长24小时客户服务窗口。请参阅我们的[窗口指南](/zh/blog/whatsapp-24-hour-customer-service-window/)。

发送调用只告诉您消息已排队。Webhook才是您得知每位收件人发生了什么的地方。

## 我会收到哪些事件和状态？

在OmniChannels.ai上，每个渠道报告的都是同一套统一的状态，所以一个处理程序就可以服务WhatsApp、短信和其他渠道。

| 状态 | 含义 | 是否为最终状态 |
|---|---|---|
| `SUBMITTED` | 已被平台接受并排队 | 否 |
| `SENT` | 已交给渠道 | 否 |
| `DELIVERED` | 已在收件人的设备上确认 | 通常是；之后可能出现`READ` |
| `READ` | 已被收件人打开（在渠道报告已读的情况下） | 是 |
| `FAILED` | 未送达；事件在有原因时会附带原因 | 是 |
| `REJECTED` | 被拒绝，例如内容被封或发件人未注册 | 是 |

请把状态视为一个可能乱序到达的推进过程，并预期随着时间推移会出现新的值。不要因为遇到不认识的状态而失败。请参阅[消息送达状态详解](/zh/blog/message-delivery-status-explained/)。

## 如何设置端点？

开放一个HTTPS端点，把URL交给您的服务商，然后用您自己的号码测试。

1. 创建一个接受带JSON请求体的`POST`的端点，例如`https://api.example.com/webhooks/omnichannels`。
2. 把URL发给我们的团队或您的账户管理员，让其在您的账户上配置。转售商可以为每个子账户设置单独的Webhook。
3. 向您自己的号码发送一条测试消息，确认事件已到达。

开发期间，ngrok之类的隧道工具可以为您本地的服务器提供一个公开的HTTPS URL。如果您还把应用直接连接到Meta，那么Meta的端点验证是单独的一步：一个带有验证token和`hub.challenge`值的`GET`请求，您以`200`并带上该challenge回应，详见Meta的[Webhook端点文档](https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/create-webhook-endpoint)。

## 负载是什么样的？

因渠道而异。API规范没有定义单一的Webhook结构，所以请先记录原始请求体，再根据您账户上的真实事件映射所需字段。示例展示的是一个送达事件所携带信息的类型，仅供说明。

```json
{
  "messageId": "2603121407537573731",
  "channel": "SMS",
  "destination": "447700900123",
  "status": "DELIVERED",
  "timestamp": "2026-10-10T09:15:42Z",
  "reason": null
}
```

在核对真实事件之前，不要依据这些字段名编写代码。请向您的开通工程师索取每个渠道的示例负载，并将其保存为测试夹具。更全面的文档请参阅[Webhook参考](/developers/webhooks/)。

## 如何编写可靠的处理程序？

按顺序做三件事：保存原始事件，以`2xx`确认，然后异步处理。下面的处理程序遵循我们文档中的模式。

```js
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() });
}
```

```python
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`筛选 |

```bash
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`从零开始。请运行一个夜间对账任务，读取前一天的批次并填补任何缺口。

### 故障排查清单

1. URL是否配置在了正确的账户或子账户上？
2. 端点能否通过带有效证书的HTTPS访问？
3. 它是否迅速返回`2xx`，还是超时？
4. 是否有防火墙或WAF规则在拦截请求？
5. 您是否在解析之前记录了原始请求体，让解析错误无法掩盖事件？

## 实战示例：一家零售商的通知管道

“Souq Rafiq”（虚构）是阿联酋的一家零售商，通过WhatsApp发送订单更新，并希望在订单工具中获得准确的状态。

1. 订单通过`sendbulk`发送，`batchId`存储在订单上。请参阅[通过API发送WhatsApp消息](/zh/blog/send-whatsapp-message-api-examples/)。
2. Webhook端点把每个事件写入一张表，然后返回`200`。
3. 工作进程为状态排序，并更新订单的消息状态。
4. `FAILED`事件会为客服创建一个带原因的任务，并按[渠道回退](/zh/blog/channel-fallback-strategy/)考虑短信回退。
5. 收到的客户消息被转到共享收件箱，让客服可以在窗口内回复。
6. 夜间任务对昨天的批次调用`bulk-insights`，并纠正任何遗漏。

## 常见错误有哪些？

- **响应之前做繁重的工作，**导致超时和重复投递。
- **先确认再存储，**导致存储失败时事件丢失。
- **相信顺序。**迟到的`SENT`覆盖了`DELIVERED`。
- **依据示意性负载编写代码，**而不是依据真实事件。
- **检查签名之前重新序列化JSON，**会改变字节。
- **在处理程序中重发，**可能造成循环。
- **局限：**本指南描述的是我们文档中记载的行为。您账户的重试策略和负载字段，应在开通期间确认。

## OmniChannels.ai 如何提供帮助

OmniChannels.ai把WhatsApp、短信、RCS和其他渠道的送达状态和收到的消息，以一种统一的格式推送到您的Webhook，并为转售商提供按子账户划分的单独Webhook。BroadNet是Meta官方的Business Solution Provider，因此Meta自身的Webhook要求已为您处理，您只需使用我们的API。请阅读[Webhook参考](/developers/webhooks/)和[WhatsApp API文档](/developers/whatsapp/)，或查看事件在[共享收件箱](/zh/platform/unified-inbox/)中的呈现方式。
