Webhooks

    7 мин чтения
    ·
    103 просмотра
    ·
    Обновлено 7 сентября 2026

    Webhooks — уведомления о событиях

    Webhooks позволяют получать HTTP-уведомления на ваш сервер при наступлении событий: входящие сообщения, исходящие сообщения, изменения статусов и другие.

    Как работает

    1. Вы регистрируете URL вашего сервера через PATCH /v1/webhooks
    2. WazzaBee отправляет тестовый запрос на ваш URL — если он не вернёт 200, регистрация будет отклонена
    3. При наступлении событий WazzaBee отправляет POST-запрос на ваш URL с данными события
    4. Каждый запрос подписан HMAC-SHA256 — вы можете проверить подлинность

    Два способа подключить webhook

    1. Через личный кабинет (просто, без программиста). Откройте «Коннекторы» → «API» → блок «Webhook для входящих». Вставьте адрес вашего сервера и нажмите «Подключить» — кабинет сам проверит адрес и покажет секретный ключ для подписи.

    2. Через REST API (для автоматизации). Отправьте запрос PATCH /v1/webhooks — описано ниже.


    PATCH /v1/webhooks — регистрация/обновление

    URL

    PATCH https://api.wazzabee.com/api-gateway/v1/webhooks

    Заголовки

    Authorization: Bearer ВАШ_API_КЛЮЧ
    Content-Type: application/json

    Параметры тела запроса

    ПараметрТипОбязательныйОписание
    webhooksUristring (URL)✅ ДаURL вашего сервера для получения уведомлений. Только https, должен быть доступен из интернета и возвращать 200 на POST-запросы. Не может указывать на локальные или внутренние адреса (localhost, 127.0.0.1, 10.x, 172.16–31.x, 192.168.x, 169.254.x)
    subscriptionsarray of stringsНетСписок событий для подписки. По умолчанию: ["messages_incoming"]

    Доступные подписки (subscriptions)

    СобытиеОписание
    messages_incomingВходящие сообщения от клиентов
    messages_outgoingИсходящие сообщения (отправленные через API или вручную)
    messages_statusИзменение статуса сообщения (доставлено, прочитано, ошибка)
    contactsAndDealsCreationСоздание новых контактов и сделок
    channelsUpdatesИзменение статуса каналов (подключение, отключение, ошибка)

    Пример запроса

    curl -X PATCH "https://api.wazzabee.com/api-gateway/v1/webhooks" \
      -H "Authorization: Bearer ВАШ_API_КЛЮЧ" \
      -H "Content-Type: application/json" \
      -d '{
        "webhooksUri": "https://ваш-сервер.com/webhook/wazzabee",
        "subscriptions": [
          "messages_incoming",
          "messages_outgoing",
          "messages_status"
        ]
      }'

    Успешный ответ (200 OK)

    {
      "success": true,
      "webhooksUri": "https://ваш-сервер.com/webhook/wazzabee",
      "subscriptions": [
        "messages_incoming",
        "messages_outgoing",
        "messages_status"
      ],
      "secret": "a1b2c3d4e5f6...64-символьный-hex-ключ",
      "message": "Вебхук установлен. Сохраните secret — он используется для подписи уведомлений (HMAC-SHA256)."
    }
    🔴 Важно! Поле secret генерируется заново при каждом вызове PATCH. Обязательно сохраните его — он нужен для проверки подписи входящих уведомлений.

    Тестовый запрос

    При регистрации вебхука WazzaBee сразу отправляет тестовый POST-запрос на ваш URL:

    {
      "test": true,
      "event": "webhook_test",
      "challenge": "случайная_строка_для_подтверждения",
      "timestamp": "2026-03-12T10:00:00.000Z",
      "message": "WazzaBee webhook test — если вы видите это, ваш webhook работает корректно."
    }

    Ваш сервер должен ответить статусом 200. Если ответ не 200 или URL недоступен — регистрация вебхука будет отклонена с ошибкой.

    Поле challenge (рекомендуется). В тестовом запросе приходит поле challenge со случайной строкой. Если ваш сервер вернёт это же значение — в теле ответа или JSON-полем challenge (например {"challenge": "то же значение"}) — webhook получит отметку «подтверждён» (более высокий уровень доверия). Это не обязательно: если ваш приёмник (например готовый no-code сервис) не умеет возвращать значение, webhook всё равно подключится и будет работать под защитой HMAC-подписи.

    Возможные ошибки при регистрации

    КодОшибкаПричина
    400Поле webhooksUri обязательноНе передан URL
    400webhooksUri должен быть валидным URLФормат URL некорректен
    400webhooksUri должен использовать https://Адрес начинается не с https://
    400webhooksUri не может указывать на локальный или внутренний адресURL ведёт на localhost, приватную сеть или metadata-адрес
    400Тестовый запрос вернул ошибку NВаш сервер вернул не-200 ответ
    400Не удалось отправить тестовый запросURL недоступен или таймаут (10 секунд)

    GET /v1/webhooks — текущие настройки

    URL

    GET https://api.wazzabee.com/api-gateway/v1/webhooks

    Пример запроса

    curl -X GET "https://api.wazzabee.com/api-gateway/v1/webhooks" \
      -H "Authorization: Bearer ВАШ_API_КЛЮЧ"

    Ответ — вебхук настроен (200 OK)

    {
      "success": true,
      "webhook": {
        "webhooksUri": "https://ваш-сервер.com/webhook/wazzabee",
        "subscriptions": ["messages_incoming", "messages_outgoing"],
        "status": "active",
        "failureCount": 0,
        "createdAt": "2026-03-12T08:00:00.000Z",
        "updatedAt": "2026-03-12T08:00:00.000Z"
      }
    }

    Ответ — вебхук не настроен (200 OK)

    {
      "success": true,
      "webhook": null,
      "message": "Вебхук не настроен"
    }

    Описание полей

    ПолеТипОписание
    webhooksUristringЗарегистрированный URL
    subscriptionsarrayАктивные подписки
    statusstringactive или disabled
    failureCountintegerКоличество неудачных доставок подряд
    createdAtstring (ISO 8601)Дата создания
    updatedAtstring (ISO 8601)Дата последнего обновления

    Проверка подписи (HMAC-SHA256)

    Каждое уведомление подписано с помощью secret, полученного при регистрации вебхука. Рекомендуем всегда проверять подпись для защиты от поддельных запросов.

    Пример проверки на Node.js

    const crypto = require("crypto");
    
    function verifyWebhookSignature(body, signature, secret) {
      const expected = crypto
        .createHmac("sha256", secret)
        .update(JSON.stringify(body))
        .digest("hex");
      return signature === expected;
    }
    
    // В обработчике webhook:
    app.post("/webhook/wazzabee", (req, res) => {
      const signature = req.headers["x-webhook-signature"];
      if (!verifyWebhookSignature(req.body, signature, YOUR_WEBHOOK_SECRET)) {
        return res.status(401).send("Invalid signature");
      }
      // Обработка события...
      res.status(200).send("OK");
    });

    Пример проверки на Python

    import hmac
    import hashlib
    import json
    
    def verify_signature(body: dict, signature: str, secret: str) -> bool:
        expected = hmac.new(
            secret.encode(),
            json.dumps(body).encode(),
            hashlib.sha256
        ).hexdigest()
        return hmac.compare_digest(signature, expected)
    
    # В обработчике Flask:
    @app.route("/webhook/wazzabee", methods=["POST"])
    def handle_webhook():
        signature = request.headers.get("X-Webhook-Signature", "")
        if not verify_signature(request.json, signature, YOUR_WEBHOOK_SECRET):
            return "Invalid signature", 401
        # Обработка события...
        return "OK", 200

    Формат входящего webhook-уведомления

    При поступлении входящего сообщения WazzaBee отправляет POST-запрос на ваш URL со следующей структурой:

    {
      "channel_id": "uuid",
      "channel_type": "whatsapp_waba",
      "channel_name": "WhatsApp Business",
      "conversation_id": "uuid",
      "message": {
        "id": "msg_id",
        "from": "77771234567",
        "sender_name": "Клиент",
        "type": "text",
        "text": "Здравствуйте!",
        "timestamp": "1712345678"
      },
      "operator": {
        "crm_user_id": "manager-42",
        "crm_user_name": "Алексей Менеджер"
      },
      "ad": {
        "ad_id": "120255518599590051",
        "source_type": "ad",
        "headline": "Visa4U",
        "text": "Отказали в визе? Не все потеряно!",
        "media_type": "video",
        "image_url": "https://scontent.xx.fbcdn.net/...jpg",
        "video_url": "https://video.xx.fbcdn.net/...mp4",
        "url": "https://fb.me/2abcXYZ",
        "click_id": "AfgHqhgPqgcChoXjlCqLkHfWKeS8"
      },
      "raw": { ... }
    }

    Описание полей

    ПолеТипОписание
    channel_idstring (UUID)Идентификатор линии связи WazzaBee
    channel_typestringТип линии: whatsapp_waba, whatsapp_qr, telegram_bot, telegram_personal
    channel_namestringНазвание линии связи
    conversation_idstring (UUID) | nullИдентификатор диалога. Используйте для группировки сообщений одного клиента
    messageobjectДанные сообщения
    operatorobject | nullПоследний оператор, ответивший в этом чате. null если ещё никто не отвечал
    operator.crm_user_idstringID пользователя из вашей CRM (переданный при создании iframe-сессии)
    operator.crm_user_namestringИмя оператора. Берём его из user.name вашей iframe-сессии, а при отправке через API — из поля senderName
    adobject | отсутствуетИсточник обращения: с какого объявления пришёл клиент. Блок приходит только у обращений с рекламы Click-to-WhatsApp на линиях официального WhatsApp (WABA). Описание полей — ниже
    rawobjectПолный оригинальный payload от провайдера (для отладки)

    Блок ad — источник обращения

    Если клиент нажал «Написать в WhatsApp» в рекламе Instagram или Facebook, вместе с уведомлением приходит блок ad. У обычных обращений его нет — проверяйте наличие самого поля, а не значения внутри. Блок приходит только на линиях официального WhatsApp: такие данные Meta передаёт лишь по WABA.

    ПолеТипОписание
    ad_idstring | nullИдентификатор объявления. По нему объявление находится в рекламном кабинете Meta
    source_typestring | nullad — реклама, post — обычная публикация
    headlinestring | nullЗаголовок объявления. Может повторяться у разных объявлений — для точной привязки используйте ad_id
    textstring | nullТекст объявления
    media_typestring | nullТип креатива: image или video
    image_urlstring | nullКартинка объявления или кадр из видео
    video_urlstring | nullВидео объявления
    urlstring | nullСсылка на объявление
    click_idstring | nullИдентификатор клика по рекламе (ctwa_clid). Сохраните его у себя: по нему в Meta возвращают события о продаже, чтобы реклама оптимизировалась на покупателей
    Что с этим делать. Запишите ad_id и click_id в карточку клиента у себя — тогда в отчётах будет видно, какое объявление приносит сделки, а не просто обращения.
    Поле operator — позволяет вашей CRM точнее направлять уведомления. Если оператор уже отвечал клиенту — вы знаете кому адресовать новое входящее сообщение.

    Ответы клиентов на кнопки WABA-шаблонов

    Когда вы отправляете клиенту одобренный WABA-шаблон с кнопками Quick Reply (или Interactive buttons / list), и клиент нажимает кнопку — WazzaBee присылает в webhook дополнительные поля message.interactive и message.context. Поле message.text содержит реальный текст нажатой кнопки — его можно показывать в чате CRM как обычное сообщение клиента.

    Значения message.type

    ЗначениеКогда приходит
    textОбычное текстовое сообщение
    image, video, audio, document, stickerМедиа
    location, contacts, reactionСлужебные типы WhatsApp
    buttonКлик по Quick Reply кнопке WABA-шаблона (старый Meta формат)
    interactiveКлик по Interactive button / list (Meta Cloud API v3)

    Структура поля message.interactive

    {
      "type": "button" | "button_reply" | "list_reply",
      "id":    "идентификатор кнопки" | null,
      "title": "текст кнопки, который видел клиент" | null,
      "description": "описание (только для list_reply)" | null,
      "payload": "значение payload (только для type=button)" | null
    }

    Структура поля message.context

    Ссылка на исходное HSM-сообщение, на кнопку которого нажал клиент:

    {
      "id":          "uuid исходящего сообщения в WazzaBee",
      "from":        "номер отправителя (ваш WABA-номер)",
      "gs_id":       "внутренний ID сообщения у провайдера",
      "meta_msg_id": "wamid.HBgL... — ID в Meta"
    }

    Пример webhook: клиент нажал кнопку «Да, буду вовремя»

    {
      "channel_id": "f0e8...",
      "channel_type": "whatsapp_waba",
      "channel_name": "WhatsApp Business",
      "conversation_id": "b1c2...",
      "message": {
        "id": "wamid.HBgLNzcwMTEwMDEwNTkVAgASGBQzQTY5NkJCNjE3M0JGQjE1MjVEMQA=",
        "from": "77011001059",
        "sender_name": "Фархат",
        "type": "button",
        "text": "Да, буду вовремя",
        "interactive": {
          "type": "button",
          "title": "Да, буду вовремя",
          "payload": "Да, буду вовремя"
        },
        "context": {
          "id": "584abab5-911a-49ca-a750-95e8116be1e1",
          "from": "77079119977",
          "gs_id": "378ba4ed-5a5d-4c58-a4e8-b6ca08b2ad7a",
          "meta_msg_id": "wamid.HBgL..."
        },
        "timestamp": "1776870877"
      },
      "operator": null,
      "raw": { ... }
    }
    ✅ Совместимость со старыми интеграциями. Поле message.text теперь содержит реальный текст нажатой кнопки — старые клиенты, которые читают только text, будут видеть в чате осмысленное сообщение «Да, буду вовремя» вместо технической заглушки.

    Пример: клик по Interactive list (Meta Cloud API v3)

    {
      "message": {
        "type": "interactive",
        "text": "Доставка на дом",
        "interactive": {
          "type": "list_reply",
          "id": "delivery_home",
          "title": "Доставка на дом",
          "description": "По адресу клиента"
        },
        "context": { "meta_msg_id": "wamid.HBgL..." }
      }
    }

    Исходящие WABA-шаблоны (messages_outgoing)

    Когда вы отправляете HSM-шаблон через API или из UI WazzaBee, при подписке на messages_outgoing в webhook приходит структура с полем template (если шаблон содержит кнопки):

    {
      "messages": [{
        "messageId": "uuid",
        "direction": "outgoing",
        "type": "template",
        "text": "Здравствуйте! Подтвердите встречу завтра в 15:00.",
        "template": {
          "name": "appointment_reminder_v2",
          "language": "ru",
          "buttons": [
            { "type": "quick_reply", "text": "Да, буду вовремя" },
            { "type": "quick_reply", "text": "Перенести" }
          ]
        },
        "timestamp": "2026-04-22T15:14:42Z"
      }]
    }

    Это позволяет вашей CRM отрисовать кнопки в ленте чата точно так же, как они выглядят у клиента в WhatsApp.


    Кто отправил исходящее сообщение (author)

    Событие messages_outgoing приходит на любой ответ клиенту: написанный оператором в кабинете WazzaBee, отправленный из чата, встроенного в вашу CRM, посланный шаблоном или через POST /v1/messages/send. Вместе с сообщением приходит блок author — кто именно ответил.

    {
      "messages": [{
        "messageId": "uuid",
        "channelId": "uuid",
        "chatId": "77001234567",
        "contactId": "uuid",
        "conversationId": "uuid",
        "direction": "outgoing",
        "type": "text",
        "text": "Здравствуйте! Ваши документы готовы.",
        "author": {
          "kind": "operator",
          "name": "Назерке А.",
          "userId": "manager-42"
        },
        "timestamp": "2026-07-30T09:20:12.000Z"
      }]
    }

    Поля блока author

    ПолеТипОписание
    author.kindstringВид отправки: operator — ответил человек, api — сообщение отправила ваша система, automation — робот или автоответ, broadcast — рассылка
    author.namestring | nullИмя сотрудника. Для ответов из встроенного чата — то самое имя, которое вы передали в user.name при создании iframe-сессии; при отправке по API — senderName. null, если имя не передавали
    author.userIdstring | nullИдентификатор сотрудника в вашей CRM (user.id из iframe-сессии или userId при отправке по API). Возвращаем как есть — сопоставлять его с пользователями WazzaBee не нужно
    ✅ Как получить имя автора. Ничего дополнительно настраивать не нужно: передавайте user.id и user.name при создании iframe-сессии — и имя сотрудника будет и в подписи под сообщением в чате, и в этом вебхуке. Для сообщений, которые ваша система отправляет сама, передавайте senderName и userId в запросе на отправку.