Встраивание чата (iframe)

    8 мин чтения
    ·
    103 просмотра
    ·
    Обновлено 6 сентября 2026
    🚀 Быстрый старт:
    1. Получите API ключ в кабинете WazzaBee → Коннекторы → API
    2. С вашего бэкенда отправьте POST запрос на /v1/iframe с API ключом
    3. Вставьте полученный URL в <iframe> на вашей странице
    4. Готово! Менеджер видит все чаты прямо в вашем приложении

    Два варианта использования

    ВариантscopeГде использовать
    Общие чатыglobalОтдельная страница «Чаты» или «Мессенджеры» в меню вашего приложения
    Чат клиентаcardКарточка клиента — показывает только переписку с этим клиентом

    POST /v1/iframe — встраивание чата WazzaBee

    Этот эндпоинт позволяет встроить чат WazzaBee в вашу CRM-систему или любое веб-приложение через <iframe>. Идеально подходит для кастомных CRM на React, Vue, Angular или любом другом фреймворке.

    💡 Как это работает:
    1. Ваш бэкенд запрашивает iframe-сессию через API
    2. API возвращает URL для встраивания
    3. Ваш фронтенд показывает <iframe> с этим URL
    4. Пользователь видит полноценный чат WazzaBee внутри вашего приложения

    URL

    POST https://api.wazzabee.com/api-gateway/v1/iframe

    Заголовки

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

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

    ПараметрТипОбязательныйОписание
    userobject✅ ДаИнформация о пользователе вашей CRM
    user.idstring✅ ДаИдентификатор пользователя в вашей CRM-системе
    user.namestringНетИмя пользователя (для отображения)
    scopestringНетРежим отображения: global (все чаты) или card (чат конкретного контакта). По умолчанию: global
    filterobjectУсловноФильтры для scope=card. Обязателен при scope=card
    filter.phonestringНомер телефона контакта для фильтрации
    filter.channelIdsarrayОграничить показ определёнными каналами
    activeChatobjectНетОткрыть конкретный чат сразу при загрузке
    optionsobjectНетДополнительные настройки отображения

    Режимы отображения (scope)

    global — все чаты

    Показывает полный список диалогов, как в основном интерфейсе WazzaBee. Подходит для отдельной страницы «Чаты» в вашей CRM.

    card — чат конкретного контакта

    Фильтрует диалоги по телефону контакта. Идеально для встраивания в карточку клиента — менеджер видит только переписку с этим клиентом.

    Доступные UI-действия в шапке списка диалогов

    В iframe есть встроенный набор управляющих кнопок над списком чатов. Их видимость зависит от выбранного scope — UI автоматически адаптируется под сценарий менеджера в CRM:

    Действие scope=global scope=card
    📌 Закрепить чат
    👁 Пометить непрочитанным
    📦 Архивировать чат + переключатель «активные / архив»❌ скрыто
    ✓✓ Прочитать все (в текущей линии / во всех линиях)❌ скрыто
    🔄 Обновить список диалогов❌ скрыто
    ➕ Добавить контакт вручную❌ скрыто — вместо этого автоматически открывается попап «Начать чат», если контакт по filter.phone ещё не существует
    Отправка сообщений, медиа, голосовых, шаблонов
    💡 Логика: в режиме card менеджер CRM работает с одним контактом — управляющие кнопки списка диалогов там не нужны и могут отвлекать. Поэтому они намеренно скрыты, чтобы UI был сфокусирован на переписке. В scope=global доступны все возможности основного кабинета WazzaBee.
    📦 Архивные чаты: архивация скрывает диалог из активного списка, но переписка сохраняется. В шапке списка есть отдельная иконка Архив — клик переключает режим просмотра между активными и архивными чатами. На каждом архивном диалоге появляется кнопка восстановления.

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

    Пример 1. Все чаты (scope=global)

    curl -X POST "https://api.wazzabee.com/api-gateway/v1/iframe" \
      -H "Authorization: Bearer ВАШ_API_КЛЮЧ" \
      -H "Content-Type: application/json" \
      -d '{
        "user": {
          "id": "manager-42",
          "name": "Алексей Менеджер"
        },
        "scope": "global"
      }'

    Пример 2. Чат конкретного клиента (scope=card)

    curl -X POST "https://api.wazzabee.com/api-gateway/v1/iframe" \
      -H "Authorization: Bearer ВАШ_API_КЛЮЧ" \
      -H "Content-Type: application/json" \
      -d '{
        "user": {
          "id": "manager-42",
          "name": "Алексей Менеджер"
        },
        "scope": "card",
        "filter": {
          "phone": "77001234567"
        }
      }'

    Пример 3. С ограничением по каналам

    curl -X POST "https://api.wazzabee.com/api-gateway/v1/iframe" \
      -H "Authorization: Bearer ВАШ_API_КЛЮЧ" \
      -H "Content-Type: application/json" \
      -d '{
        "user": {
          "id": "manager-42",
          "name": "Алексей Менеджер"
        },
        "scope": "card",
        "filter": {
          "phone": "77001234567",
          "channelIds": ["550e8400-e29b-41d4-a716-446655440000"]
        }
      }'

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

    {
      "success": true,
      "url": "https://app.wazzabee.com/chat/embed/a1b2c3d4...64-символьный-токен",
      "token": "a1b2c3d4...64-символьный-токен",
      "expiresAt": "2026-03-13T10:00:00.000Z",
      "scope": "card"
    }
    ПолеТипОписание
    urlstringГотовый URL для вставки в <iframe src="...">
    tokenstringТокен сессии (64 символа hex)
    expiresAtstring (ISO 8601)Время истечения сессии (24 часа от создания)
    scopestringВыбранный режим: global или card

    Встраивание на фронтенде

    Простой HTML

    <iframe
      src="https://app.wazzabee.com/chat/embed/ТОКЕН_СЕССИИ"
      style="width: 100%; height: 600px; border: none;"
      allow="clipboard-write"
    ></iframe>

    React-компонент

    import { useEffect, useState } from "react";
    
    function WazzaBeeChat({ contactPhone }) {
      const [iframeUrl, setIframeUrl] = useState(null);
    
      useEffect(() => {
        // Запрос к вашему бэкенду, который проксирует вызов к WazzaBee API
        fetch("/api/wazzabee/iframe", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            phone: contactPhone,
            scope: "card"
          })
        })
          .then(res => res.json())
          .then(data => setIframeUrl(data.url));
      }, [contactPhone]);
    
      if (!iframeUrl) return <div>Загрузка чата...</div>;
    
      return (
        <iframe
          src={iframeUrl}
          style={{ width: "100%", height: "600px", border: "none" }}
          allow="clipboard-write"
        />
      );
    }
    ⚠️ Важно: Запрос к WazzaBee API (с API-ключом) должен идти через ваш бэкенд, а не напрямую из браузера клиента. API-ключ нельзя хранить на фронтенде!

    События postMessage

    iframe отправляет события в родительское окно через window.postMessage. Вы можете слушать их для интеграции с вашей CRM.

    📌 Формат события: все события приходят в виде объекта event.data с полями source: "wazzabee", event: "WZ_..." и payload-полями. Проверяйте event.origin и event.data.source для безопасности.

    Доступные события

    СобытиеКогда срабатываетДанные (поля payload)
    WZ_READYЧат загрузился и готов к работе{ userId, scope }
    WZ_CHAT_OPENEDМенеджер открыл диалог или переключился на другой. Приходит при каждой смене активного чата — по нему обновляйте карточку клиента в своей CRM{ conversationId, chatId, phone, name, contactId, isGroup, contactPhone, contactName }
    ⚠️ contactPhone, contactName — deprecated, используйте phone, name
    WZ_CHAT_CLOSEDЧат выгружен — пользователь ушёл со страницы с iframe. При переключении между диалогами не отправляется: смену активного чата ловите по WZ_CHAT_OPENED{}
    WZ_MESSAGE_SENTМенеджер отправил сообщение{ conversationId, chatId, phone, contactPhone }
    WZ_MESSAGE_RECEIVEDПолучено новое сообщение (входящее или исходящее){ messageId, conversationId, chatId, phone, direction, contentType, content, text, timestamp }
    WZ_MESSAGES_READМенеджер открыл чат (прочитал сообщения){ conversationId, chatId, phone, contactPhone, unreadCount }
    ⚠️ contactPhone — deprecated, используйте phone
    WZ_CREATE_CONTACTЗапрос на создание контакта в CRM{ phone, name }
    WZ_CHAT_NOT_FOUNDЗапрошенный чат не найден (например, неверный телефон в scope=card){ phone, reason }
    WZ_ACTION_NOT_SUPPORTEDДействие недоступно в текущем режиме (например, попытка переключиться вне scope=card){ action, reason }
    WZ_UNREAD_TOTALИзменилась сумма непрочитанных — удобно для бейджа на вкладке CRM{ channelId, total }
    channelId = null, если к сессии привязано несколько линий связи
    WZ_SESSION_REFRESHEDТокен сессии продлён автоматически — реакции не требует{}
    WZ_SESSION_EXPIREDСессия истекла — запросите новую ссылку на iframe{}
    WZ_CONNECTION_LOSTОборвалось realtime-соединение (сеть). Восстанавливается само, можно показать индикатор{ scope }
    📞 Про поля phone и contactPhone: в событиях, связанных с контактом, мы шлём оба вариантаphone (рекомендуется) и устаревшее contactPhone. То же для name и contactName. Так интеграции не ломаются. В новых интеграциях используйте phone/name; устаревшие поля поддерживаются и удаляться не будут.

    Пример обработки событий

    window.addEventListener("message", (event) => {
      // Проверяем источник
      if (event.origin !== "https://app.wazzabee.com") return;
      if (event.data?.source !== "wazzabee") return;
    
      const { event: type, ...data } = event.data;
    
      switch (type) {
        case "WZ_READY":
          console.log("Чат WazzaBee загружен, user:", data.userId, "scope:", data.scope);
          break;
    
        case "WZ_CHAT_OPENED":
          // Рекомендуется: data.phone, data.name
          console.log("Открыт диалог:", data.conversationId, "телефон:", data.phone);
          // Можно обновить карточку клиента в CRM
          break;
    
        case "WZ_CHAT_CLOSED":
          console.log("Диалог закрыт");
          break;
    
        case "WZ_MESSAGE_SENT":
          console.log("Менеджер отправил сообщение в чат:", data.chatId, data.phone);
          // Можно добавить запись в историю CRM
          break;
    
        case "WZ_MESSAGE_RECEIVED":
          console.log("Новое сообщение:", data.content);
          console.log("Направление:", data.direction);
          break;
    
        case "WZ_MESSAGES_READ":
          console.log("Менеджер прочитал чат:", data.conversationId);
          break;
    
        case "WZ_CREATE_CONTACT":
          console.log("Запрос на создание контакта:", data.phone, data.name);
          // Можно автоматически создать контакт в вашей CRM
          break;
    
        case "WZ_CHAT_NOT_FOUND":
          console.warn("Чат не найден:", data.phone, "причина:", data.reason);
          // Например, предложить создать контакт
          break;
    
        case "WZ_ACTION_NOT_SUPPORTED":
          console.warn("Действие недоступно:", data.action, "причина:", data.reason);
          break;
      }
    });

    Управление чатом из вашей CRM

    Обмен двусторонний: не только iframe сообщает вам о происходящем, но и вы можете управлять им. Отправьте в iframe postMessage с полем source: "crm".

    ActionДанныеПоведение
    OPEN_CHAT_BY_PHONE{ phone }Открыть чат с этим номером. В ответ придёт WZ_CHAT_OPENED (нашли) или WZ_CHAT_NOT_FOUND (нет такого контакта). В режиме scope=card вернётся WZ_ACTION_NOT_SUPPORTED

    Номер нормализуется по цифрам — формат любой: +7 (700) 123-45-67, 77001234567, 87001234567 сработают одинаково.

    iframeRef.current?.contentWindow?.postMessage({
      source: "crm",
      action: "OPEN_CHAT_BY_PHONE",
      phone: "+77001234567",
    }, "*");

    Сценарий: карточка клиента слева, чат справа

    Типовая задача при встраивании в CRM: справа менеджер ведёт переписку, слева — карточка клиента, каталог товаров, кнопки действий. Когда менеджер переключается на другой чат, левая часть должна сама подтянуть данные нового собеседника.

    ✅ Никаких браузерных расширений и разбора вёрстки не нужно. Встроенный чат сам сообщает о смене активного диалога событием WZ_CHAT_OPENED — достаточно подписаться на message в родительском окне.
    const [activeChat, setActiveChat] = useState(null);
    
    useEffect(() => {
      const handler = (event) => {
        if (event.origin !== "https://app.wazzabee.com") return;
        if (event.data?.source !== "wazzabee") return;
    
        // Менеджер переключился на другой чат
        if (event.data.event === "WZ_CHAT_OPENED") {
          setActiveChat({
            chatId: event.data.chatId,
            phone: event.data.phone,
            name: event.data.name,
          });
          // Тянем карточку клиента по номеру из своей CRM
          loadCustomerCard(event.data.phone);
        }
      };
    
      window.addEventListener("message", handler);
      return () => window.removeEventListener("message", handler);
    }, []);

    Обратный ход — менеджер открыл сделку в CRM и хочет сразу перейти к переписке: пошлите в iframe OPEN_CHAT_BY_PHONE, и чат сам переключится на нужный диалог.

    Возможные ошибки

    КодОшибкаПричина
    400Поле user.id обязательноНе передан идентификатор пользователя CRM
    400Для scope=card необходимо указать filterВыбран режим card, но не указан фильтр

    Время жизни сессии

    • Сессия действует 24 часа с момента создания
    • После истечения iframe покажет ошибку — нужно запросить новый токен
    • Рекомендуем обновлять токен заранее (например, каждые 12 часов)
    • Для каждого пользователя CRM создаётся отдельная сессия

    Кто из менеджеров ответил клиенту

    Имя, которое вы передаёте в user.name при создании сессии, — это автор сообщений. Под каждым исходящим сообщением в ленте чата видна подпись вида «из WazzaBee · Назерке А.»: так по переписке понятно, кто из менеджеров ответил. Фамилия сокращается до инициала.

    Подпись работает для всего, что оператор отправляет из встроенного чата: текста, файлов, голосовых и WABA-шаблонов.

    Что нужно передавать

    ПолеЗачем
    user.nameИмя сотрудника — попадает в подпись под сообщением и в вебхук messages_outgoing (author.name)
    user.idИдентификатор сотрудника в вашей CRM — возвращаем его в вебхуке как author.userId, чтобы вы сопоставили ответ со своим менеджером
    ✅ Отдельных пользователей в WazzaBee заводить не нужно. Мы берём имя и идентификатор прямо из сессии, поэтому сопоставлять сотрудников вашей CRM с пользователями WazzaBee не требуется. Заводить сотрудников в разделе «Команда» стоит только тем, кто заходит в кабинет WazzaBee напрямую, а не через встроенный чат.
    ℹ️ Подпись видна только вашим сотрудникам. Клиенту в WhatsApp или Telegram она не отправляется — он видит только текст сообщения. Изменить формат подписи в настройках нельзя.