Вебхуки WhatsApp: события, безопасность и повторные доставки
Как работают вебхуки WhatsApp: события сообщений и статусов, надёжный обработчик, проверка подписи Meta, повторы, идемпотентность и пропущенные события.
Автор: OmniChannels.ai Editorial Team · Проверил: BroadNet Messaging Solutions Team
Интеграции
Короткий ответ
Вебхук WhatsApp — это HTTPS-эндпоинт на вашем сервере, который получает события: статусы доставки отправленных вами сообщений и сообщения от клиентов. В OmniChannels.ai статусы приходят как SUBMITTED, SENT, DELIVERED, READ, FAILED или REJECTED. Сохраняйте исходное тело, быстро отвечайте 2xx, устраняйте дубликаты по идентификатору сообщения и обрабатывайте асинхронно. Meta подписывает свои вебхуки; в нашей документации заголовок подписи не определён, поэтому защищайте эндпоинт на уровне транспорта и приложения.
На этой странице
- Что такое вебхуки WhatsApp и зачем они нужны?
- Какие события и статусы я буду получать?
- Как настроить эндпоинт?
- Как выглядит payload?
- Как написать надёжный обработчик?
- Как защитить вебхук WhatsApp?
- Что происходит при сбое доставки?
- Как восстановить пропущенные события?
- Пример: конвейер уведомлений ритейлера
- Какие типичные ошибки?
- Как помогает OmniChannels.ai
Главное
- Вебхуки доставляют статусы и входящие сообщения на ваш HTTPS-эндпоинт, так что опрос не нужен; статусы WhatsApp приводятся к SUBMITTED, SENT, DELIVERED, READ, FAILED и REJECTED.
- Надёжный обработчик делает три вещи по порядку: сохраняет исходное событие, возвращает 2xx, затем обрабатывает асинхронно и идемпотентно.
- Статусы могут приходить не по порядку или дважды, поэтому ранжируйте их и никогда не позволяйте запоздавшему SENT перезаписать сохранённый DELIVERED.
- Meta подписывает вебхуки, отправляемые приложениям, заголовком X-Hub-Signature-256, но в спецификации API OmniChannels.ai подпись вебхука не определена, поэтому используйте HTTPS, неугадываемый путь и проверку тела.
- Если события пропущены, читайте статус WhatsApp по пакету через эндпоинт bulk-insights, а не отправляйте сообщения заново.
Это руководство для инженеров, которым нужно надёжно получать события WhatsApp: статусы доставки отправляемых сообщений и входящие сообщения от клиентов. В нём объясняется, как работают вебхуки, что строить в обработчике, чем безопасность вебхуков Meta отличается от наших и что делать, когда события пропадают.
Что такое вебхуки WhatsApp и зачем они нужны?
Вебхуки WhatsApp — это HTTP-колбэки. Когда сообщение меняет состояние или клиент пишет вам, платформа отправляет на ваш эндпоинт POST с JSON-телом. Опрашивать ничего не нужно, а события вы видите по мере их возникновения.
Важны два семейства событий:
- События статусов для отправленных вами сообщений: в очереди, отправлено, доставлено, прочитано, не доставлено.
- Входящие сообщения от клиентов, которые открывают или продлевают 24-часовое окно обслуживания клиента. См. наше руководство об окне.
Вызов отправки лишь сообщает, что сообщения поставлены в очередь. О том, что произошло с каждым получателем, вы узнаёте из вебхука.
Какие события и статусы я буду получать?
В OmniChannels.ai каждый канал сообщает одни и те же нормализованные статусы, поэтому один обработчик может обслуживать WhatsApp, SMS и остальные каналы.
| Статус | Значение | Финальный? |
|---|---|---|
SUBMITTED |
Принято платформой и поставлено в очередь | Нет |
SENT |
Передано в канал | Нет |
DELIVERED |
Подтверждено на устройстве получателя | Обычно; может последовать READ |
READ |
Открыто получателем, если канал сообщает о прочтении | Да |
FAILED |
Не доставлено; событие содержит причину, если она доступна | Да |
REJECTED |
Отклонено, например из-за заблокированного содержимого или незарегистрированного отправителя | Да |
Считайте статусы последовательностью, которая может приходить не по порядку, и ожидайте появления новых значений. Не допускайте сбоя на незнакомом статусе. См. статусы доставки сообщений.
Как настроить эндпоинт?
Откройте HTTPS-эндпоинт, передайте URL провайдеру, затем протестируйте на собственном номере.
- Создайте эндпоинт, принимающий
POSTс JSON-телом, напримерhttps://api.example.com/webhooks/omnichannels. - Отправьте URL нашей команде или администратору аккаунта для настройки в вашем аккаунте. У реселлеров может быть отдельный вебхук на каждый субаккаунт.
- Отправьте тестовое сообщение на свой номер и убедитесь, что события приходят.
При разработке туннель вроде ngrok даёт вашему локальному серверу публичный HTTPS URL. Если вы дополнительно подключаете приложение напрямую к Meta, проверка эндпоинта Meta — отдельный шаг: GET-запрос с токеном проверки и значением hub.challenge, на который вы отвечаете 200 с этим значением, как описано в документации Meta по эндпоинту вебхука.
Как выглядит payload?
Он зависит от канала. В спецификации API нет единой схемы вебхука, поэтому сначала логируйте исходное тело и сопоставляйте нужные поля по реальным событиям в вашем аккаунте. Пример показывает, какую информацию несёт событие доставки, и носит только иллюстративный характер.
{
"messageId": "2603121407537573731",
"channel": "SMS",
"destination": "447700900123",
"status": "DELIVERED",
"timestamp": "2026-10-10T09:15:42Z",
"reason": null
}
Не пишите код под эти имена полей, не проверив реальное событие. Попросите у инженера по подключению примеры payload для каждого канала и сохраните их как тестовые фикстуры. Более широкая документация — в справочнике по вебхукам.
Как написать надёжный обработчик?
Сделайте три вещи по порядку: сохраните исходное событие, подтвердите ответом 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. - Устраняйте дубликаты по идентификатору сообщения. Один и тот же статус может прийти несколько раз.
- Никогда не отправляйте сообщение повторно из обработчика. Решения о повторах принимайте в отдельном задании, проверив статус.
- Храните исходное тело для разбора проблем и для повторного воспроизведения событий после исправления.
Как защитить вебхук WhatsApp?
Это зависит от того, кто отправляет на ваш эндпоинт. Meta подписывает вебхуки, которые отправляет зарегистрированным вами в Meta приложениям. В спецификации API OmniChannels.ai заголовок подписи вебхука не определён, поэтому не ждите его.
| Meta на ваше приложение | OmniChannels.ai на ваш эндпоинт | |
|---|---|---|
| Подпись | Заголовок X-Hub-Signature-256 согласно документации Meta |
Не определена в спецификации API |
| Проверка | HMAC-SHA256 исходной полезной нагрузки с секретом приложения, сравнение с заголовком | Не применимо |
| Ваши меры | Проверять подпись при каждом POST |
HTTPS с действительным сертификатом, неугадываемый путь, строгая проверка тела |
Для схемы Meta документация Meta предписывает сформировать хеш HMAC-SHA256 от JSON-нагрузки с секретом приложения и сравнить его со значением после sha256= в заголовке X-Hub-Signature-256. Сам HMAC определён в RFC 2104. Вычисляйте его над исходными байтами, а не над повторно сериализованным объектом, и сравнивайте за постоянное время.
Для нашего эндпоинта:
- Используйте только HTTPS с действительным сертификатом.
- Используйте длинный неугадываемый путь и меняйте его при утечке.
- Проверяйте структуру тела и отклоняйте всё неожиданное.
- Считайте события уведомлениями. Перед необратимым действием, например возвратом средств или блокировкой по подозрению в фроде, подтвердите состояние запросом.
Что происходит при сбое доставки?
Повторы зависят от отправителя. Документация Meta сообщает, что она повторяет неудачную доставку со всё меньшей частотой до 7 дней, что может приводить к дубликатам уведомлений. Это повод быть идемпотентным, даже если дубликаты вы видите редко. Для событий нашей платформы уточните поведение повторов у инженера по подключению, а не считайте его таким же, как у Meta.
В любом случае проектируйте систему с учётом потери так же, как и дублирования: эндпоинт может быть недоступен при развёртывании, а правило файрвола может молча отбрасывать запросы.
Как восстановить пропущенные события?
Опрашивайте пакет, а не отправляйте сообщение заново.
| Что нужно | Вызов | Примечания |
|---|---|---|
| Прочитать статус WhatsApp по каждому получателю | GET /whatsapp/message/bulk-insights |
Фильтр пакета по status или destination; пагинация через page и size |
| Повторно отправить отчёты о доставке SMS | 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 — это идентификатор, который вернул вызов отправки, а page нумеруется с нуля. Запускайте ночную сверку, которая читает пакеты за предыдущий день и заполняет пробелы.
Чек-лист диагностики
- URL настроен в нужном аккаунте или субаккаунте?
- Эндпоинт доступен по HTTPS с действительным сертификатом?
- Он быстро отвечает
2xxили срабатывает тайм-аут? - Запросы не блокирует правило файрвола или WAF?
- Вы логируете исходное тело до разбора, чтобы ошибка парсера не скрыла событие?
Пример: конвейер уведомлений ритейлера
«Souq Rafiq» (условный пример), розничный продавец из ОАЭ, отправляет обновления по заказам в WhatsApp и хочет получать точный статус в своей системе заказов.
- Заказы отправляются через
sendbulk, аbatchIdсохраняется в заказе. См. отправку сообщения WhatsApp через API. - Эндпоинт вебхука записывает каждое событие в таблицу, затем возвращает
200. - Воркер ранжирует статусы и обновляет состояние сообщения в заказе.
- События
FAILEDсоздают задачу для поддержки с указанием причины, а переход на SMS рассматривается по стратегии резервных каналов. - Входящие сообщения клиентов направляются в общий инбокс, чтобы операторы могли ответить внутри окна.
- Ночное задание вызывает
bulk-insightsпо вчерашним пакетам и исправляет всё пропущенное.
Какие типичные ошибки?
- Тяжёлая работа до ответа, которая вызывает тайм-ауты и повторные доставки.
- Подтверждение до сохранения, из-за которого события теряются при сбое хранилища.
- Доверие порядку. Запоздавший
SENTперезаписываетDELIVERED. - Код под иллюстративный payload вместо реальных событий.
- Повторная сериализация JSON до проверки подписи, которая меняет байты.
- Повторная отправка из обработчика, которая может зациклиться.
- Ограничения: это руководство описывает наше документированное поведение. Политику повторов и поля payload для вашего аккаунта нужно подтвердить при подключении.
Как помогает OmniChannels.ai
OmniChannels.ai отправляет статусы доставки и входящие сообщения WhatsApp, SMS, RCS и других каналов на ваш вебхук в едином нормализованном формате, с отдельными вебхуками на субаккаунты для реселлеров. BroadNet — официальный Meta Business Solution Provider, поэтому собственные требования Meta к вебхукам выполняются за вас, а вы работаете с нашим API. Читайте справочник по вебхукам и документацию WhatsApp API или посмотрите, как события отображаются в общем инбоксе.
Часто задаваемые вопросы
Что такое вебхук WhatsApp?
Вебхук WhatsApp — это URL на вашем сервере, который платформа вызывает HTTP POST-запросом всякий раз, когда что-то происходит, например сообщение доставлено, прочитано или не доставлено, либо клиент написал вам. Он заменяет опрос. Вы открываете HTTPS-эндпоинт, передаёте его провайдеру и обрабатываете JSON-события, которые он получает.
Как проверить подпись вебхука WhatsApp?
Для вебхука, который Meta отправляет напрямую вашему приложению, документация Meta предписывает вычислить HMAC-SHA256 от исходной полезной нагрузки с секретом вашего приложения и сравнить его с заголовком X-Hub-Signature-256. Для вебхука OmniChannels.ai в спецификации API заголовок подписи не определён, поэтому полагайтесь на HTTPS, неугадываемый путь и проверку тела.
Что будет, если мой эндпоинт вебхука недоступен?
Документация Meta сообщает, что она повторяет неудачные доставки со всё меньшей частотой до 7 дней, что может приводить к дубликатам. Для событий нашей платформы уточните поведение повторов у инженера по подключению и используйте эндпоинт bulk-insights, чтобы читать статус WhatsApp по каждому получателю пакета. Для SMS эндпоинт resendDlr повторно отправляет отчёты.
Как обрабатывать дубликаты событий вебхука?
Сделайте обработку идемпотентной. Найдите сообщение по идентификатору, сравните ранг входящего статуса с сохранённым и игнорируйте события, не продвигающие состояние. Двукратное применение одного события должно давать тот же эффект, что и однократное. Храните исходное тело каждого события, чтобы повторно воспроизвести его после исправления ошибки.
Как быстро должен отвечать мой эндпоинт?
В течение нескольких секунд. Сохраните исходное событие, верните 2xx, а основную работу выполняйте в очереди или воркере. Не вызывайте другие сервисы, не отправляйте сообщения и не выполняйте тяжёлые запросы до ответа. Если хранилище недоступно, верните 5xx, чтобы отправитель мог повторить, а не подтверждайте событие, которое вы потеряли.
Можно ли использовать вебхук для автоматического ответа?
Да, но не изнутри обработчика. Подтвердите событие, а решение об ответе принимайте в отдельном задании. Внутри 24-часового окна обслуживания клиента можно отправить свободное сообщение; вне его нужен одобренный шаблон. Никогда не отправляйте сообщение повторно из самого обработчика вебхука, чтобы избежать циклов и дубликатов.
Источники
Читайте дальше
Ещё руководстваИнтеграции
Отправка сообщения WhatsApp через API: cURL, Node, Python, PHP
Как отправить сообщение WhatsApp через API: рабочие примеры на cURL, Node.js, Python и PHP для шаблонов и сессионных сообщений, отслеживание статусов и ошибки.
10 мин чтения
Интеграции
Интеграция WhatsApp с CRM: 3 способа подключения
Интеграция WhatsApp с CRM: готовые коннекторы, посредники или API с вебхуками. Потоки данных, сопоставление полей, синхронизация согласий и чек-лист выбора.
10 мин чтения
WhatsApp
Бесплатные сообщения WhatsApp Business API в 2026 году
Какие сообщения WhatsApp Business API бесплатны в 2026 году: 1 000 бесплатных service-сообщений на номер, окно точки входа на 72 часа и что тарифицируется.
9 мин чтения
