Документация для разработчиков

Подключите собственное ПО к вашему входящему ящику SyBox

Отправляйте сообщения WhatsApp со своего номера, показывайте данные клиентов рядом с каждым чатом, создавайте обращения, получайте все события на свой сервер и добавьте онлайн-чат на свой сайт. Маршрут отправки работает в формате WhatsApp Cloud API, поэтому коду, написанному для Meta, нужны только новый базовый URL и ключ SyBox.

Базовый URLhttps://api.sybox.io

Начало работы

Быстрый старт

Три вещи, и ваше первое сообщение отправлено: базовый URL, ключ устройства и эндпоинт сообщений.

POST/v26.0/{phone-number-id}/messagesAuthorization: Bearer <DEVICE_KEY>

Сегмент версии принимается в любой форме v<n> (v26.0, v1.0, …) — оставьте ту, что уже использует ваш код. {phone-number-id} — это ID вашего номера WhatsApp, точно как в Meta.

{phone-number-id} указан после id на карточке номера в разделе Настройки › Сообщения › Каналы. Ключ отправки привязан к одному номеру и отправляет только с него.

cURL
curl -X POST https://api.sybox.io/v26.0/PHONE_NUMBER_ID/messages \
  -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "to": "15551234567",
    "type": "text",
    "text": { "body": "Hello from SyBox" }
  }'

На карточке каждого ключа в разделе Настройки › Интеграции › Ключи API видно, когда он использовался в последний раз, — это самый быстрый способ проверить, доходят ли ваши вызовы.

Начало работы

Аутентификация и ключи

Каждый вызов передаёт ключ в заголовке Authorization как Bearer-токен. У каждого ключа одна задача: ключ для отправки не может создать обращение, а ключ для обращений не может отправлять сообщения. Храните все ключи только на своём сервере — единственное исключение составляет открытый ключ онлайн-чата, который создан для размещения на ваших страницах.

HTTP
Authorization: Bearer YOUR_KEY
Content-Type: application/json
КлючДля чего
Отправка через WhatsAppPOST /v{n}/{phone-number-id}/messages
Зарегистрировать обращениеPOST /v1/case
Запустить автоматизациюPOST /v1/automation
Чтение и обновление контактовPOST /v1/contacts/…
Приём из программыPOST /v1/inbound
Открытый ключ · lcpk_<script … data-key="lcpk_…">
Ключ подписи для авторизованных пользователей · lcid_Авторизованные пользователи: ключ подписи
Ключ сервера (для собственного экрана чата) · lcsk_/v1/livechat/*
Ключ подписи ответов · lcwh_x-sybox-signature
Ключ подписиX-Hub-Signature-256

Начало работы

Получение ключа устройства

Ключи создаются внутри вашего рабочего пространства.

  1. Откройте рабочее пространство → Настройки → Ключи API.
  2. Каждый ключ привязан к одному номеру и может быть отозван в любой момент.
  3. Ваш реальный токен WhatsApp никогда не покидает наш сервер.
  4. Шлюз по умолчанию отключен.

WhatsApp API

Отправка первого сообщения

Обычное текстовое сообщение. Тело представляет собой стандартный payload Cloud API.

POST/v26.0/{phone-number-id}/messagesAuthorization: Bearer <DEVICE_KEY>
curl -X POST https://api.sybox.io/v26.0/PHONE_NUMBER_ID/messages \
  -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "to": "15551234567",
    "type": "text",
    "text": { "body": "Hello from SyBox" }
  }'

Ответ

Вы получаете ровно то, что возвращает Meta, с тем же статусом HTTP.

200 OK · application/json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15551234567", "wa_id": "15551234567" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgARGBI…" }]
}

WhatsApp API

Поддержка всех типов сообщений

Поскольку тело передается без изменений, поддерживаются все типы сообщений Cloud API.

{
  "messaging_product": "whatsapp",
  "to": "15551234567",
  "type": "template",
  "template": {
    "name": "order_shipped",
    "language": { "code": "en_US" },
    "components": [{
      "type": "body",
      "parameters": [{ "type": "text", "text": "#10432" }]
    }]
  }
}

Произвольный текст доходит до клиента только в течение 24 часов после его последнего сообщения. За пределами этого окна отправляйте одобренный шаблон: SyBox отклоняет там произвольный текст с кодом 131047 ещё до того, как он попадёт в Meta.

WhatsApp API

Правила и лимиты ключей

У каждого ключа отправки свои правила, которые задаются при его создании: произвольный текст, шаблоны или и то и другое; медиафайлы или нет; какие шаблоны; в какие страны получателей; в какие часы можно отправлять; и дневной лимит. Если отправка нарушает одно из правил, SyBox отклоняет её ещё до того, как она попадёт в Meta.

СтатусКогда
404Пустой ответ: «API-шлюз» выключен или ключ не принадлежит ни одному рабочему пространству. Проверьте переключатель вверху раздела Настройки › Интеграции › Ключи API.
401Ключ отсутствует или неизвестен.
403Ключ не является ключом отправки или отправляет с номера, к которому он не привязан.
403Сообщение нарушает одно из правил ключа: произвольный текст, шаблоны, медиафайлы, название шаблона, страна или часы.
403Meta приостановила шаблон или ограничила его из-за оценки качества.
40024-часовое окно закрыто (код 131047): отправьте вместо этого шаблон.
400Тело не является корректным JSON, или номер WhatsApp ключа больше не подключён.
409Meta временно запретила отправку с этого номера.
429Достигнут дневной лимит ключа или лимит сообщений WhatsApp для номера.
502Не удалось связаться с Meta. Попробуйте ещё раз.

Дневной счётчик обнуляется в полночь по UTC, а неудачная отправка не учитывается.

WhatsApp API

Ошибки

Ошибки приходят из двух источников, и оба используют формат ошибок Meta, поэтому существующий клиент Cloud API читает их без изменений. Ошибка от Meta возвращается в точности так, как её отправила Meta, с кодом статуса Meta. Ошибка по одному из собственных правил SyBox имеет type SyBoxGatewayError, а её message указывает, какое правило отклонило вызов.

{
  "error": {
    "message": "(#131030) Recipient phone number not in allowed list",
    "type": "OAuthException",
    "code": 131030
  }
}

Ваша система в SyBox

Отображение ваших данных рядом с чатом

Когда оператор открывает чат, SyBox вызывает ВАШ эндпоинт и показывает полученные данные прямо рядом с чатом.

Добавьте свой эндпоинт в разделе Настройки › Интеграции › ERP API. Каждый вызов — это POST с вашим ключом в Authorization и запросом в одном поле data, содержащем JSON-строку, — так любая из сторон может позже добавить поля, ничего не сломав другой. Отвечайте в течение 15 секунд.

POST YOUR_ERP_URL
Authorization: Bearer YOUR_ERP_KEY
X-Sybox-Action: lookup
X-Sybox-Lang: en
X-Sybox-Theme: light
X-Sybox-Agent: 6f1c…
X-Sybox-Number: 104857600000000
Content-Type: application/json

{ "data": "{\"phone\":\"15551234567\",\"email\":\"customer@example.com\",\"ref\":\"\"}" }

Каждый вызов также сообщает, кто спрашивает: X-Sybox-Lang (язык пользователя — отвечайте на нём), X-Sybox-Theme (light или dark, для ответа html), X-Sybox-Agent (id пользователя) и X-Sybox-Number (номер, на котором идёт разговор).

Мы передаем телефон и e-mail — ответьте структурой { renderAs, content }.

renderAs

Укажите renderAs, чтобы сообщить SyBox, как отобразить ваш контент:

renderAsОписание
tableСтроки и столбцы. content — массив плоских объектов (или { rows: [ … ] }); их ключи становятся заголовками столбцов.
cards · kpiНебольшие плитки, например баланс или число заказов. content — массив { label, value } (или { cards: [ … ] }).
feed · list · threadЛента карточек. content — массив { title, text, date, tags, media: [{ type, url, name }] }.
messageОдна запись в виде одной карточки, похожей на сообщение.
htmlВаш собственный HTML в виде строки в content. Только для разметки, которую вы формируете сами.
sectionsНесколько блоков в одном ответе: content — это { sections: [ { title, renderAs, content } ] }.
jsonВариант по умолчанию, если renderAs не указан: SyBox показывает ваш content как отформатированный JSON.

Добавьте sybox_ref к любой строке таблицы, чтобы сделать ее кликабельной.

Поиск товаров (X-Sybox-Action: products)

Тот же эндпоинт может отвечать и на поиск товаров. Пользователь вводит то, что ищет клиент, SyBox отправляет это как «q», а ваш ответ отображается в виде карточек товаров рядом с чатом — с отправкой в одно касание, которая вставляет ссылку на товар в поле ввода сообщения.

POST YOUR_ERP_URL
Authorization: Bearer YOUR_ERP_KEY
X-Sybox-Action: products

# the decoded "data":
{ "q": "wireless keyboard", "phone": "15551234567", "email": "customer@example.com", "ref": "" }
ПолеОписание
nameобязательноЕдинственное обязательное поле. Товар с одним лишь названием тоже даёт аккуратную карточку.
price · oldPrice · currencyЧисла, а не отформатированные строки. Присылайте oldPrice только при наличии реальной прежней цены — скидка считается из пары.
available · stockavailable — true/false; stock — необязательное число рядом. Не присылайте их вовсе, если не ведёте остатки: отсутствующее поле ничего не рисует и никогда не читается как «нет в наличии».
imageПрямая https-ссылка на изображение. Если картинка не загрузилась, остаётся нейтральный фон.
linkСсылка на страницу товара. Без неё карточка только для чтения — «Отправить» появляется, лишь когда есть что отправить.
noteОдна короткая свободная строка под ценой — срок доставки, вариант, условие.

Ничего сверх уже сделанного: тот же URL, тот же ключ, тот же конверт { "data": "…" }. Отличайте поиск товара от запроса клиента по заголовку X-Sybox-Action. Верните content обычным массивом (или { items: [ … ] }).

Ваша система в SyBox

Открытие заявки из вашей системы

Маршрут для ERP или учетных систем для открытия единого тикета в CRM.

POST/v1/caseAuthorization: Bearer <CASE_KEY>
curl -X POST https://api.sybox.io/v1/case \
  -H "Authorization: Bearer $SYBOX_CASE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_unique_id": "CUST-10432",
    "header": "Screen replacement not delivered",
    "description": "Paid on 12 Aug, promised in 3 days, still nothing.",
    "department_id": 5,
    "ext_ref": "TICKET-99817"
  }'

Поля

ПолеОписание
customer_unique_idВаш собственный id клиента — тот же ключ, что использует синхронизация клиентов. Если id нет, отправьте вместо него phone.
phoneНомер телефона клиента, если у вас нет id.
header · descriptionТребуется хотя бы один. Заголовок — это строка, которую пользователи видят в списке.
department_idобязательноintegerОбязательно. Отдел, в котором зарегистрировано обращение: номер, указанный рядом в меню Настройки › Отделы (например, #5).
ext_refНомер вашего тикета.
sectionintegerОт 1 до 4: вкладка обращения, в которую оно попадает. По умолчанию 1.
deadlinestringСрок выполнения, YYYY-MM-DD.
notifybooleanПо умолчанию true: в разговоре клиента в SyBox появляется заметка с номером обращения. Клиенту ничего не отправляется. false — без заметки.
sourcestringНазвание программы, которое показывается на этой заметке. По умолчанию ERP.

В ответе приходит номер обращения — тот самый, который ваши сотрудники называют клиенту. Повторный запрос с тем же ext_ref возвращает "duplicate": true и уже существующее обращение, без customer_id.

Клиент уже должен быть в SyBox. Если id не найден, SyBox один раз синхронизирует клиентов с вашей системой и ищет снова; если клиента по-прежнему нет, ответ — 404, а не обращение для выдуманного клиента.

Используйте ключ типа 'case'.

Ваша система в SyBox

Передача сообщений из сторонних программ

Для сторонних систем (POS, уведомления устройств, устаревшее ПО).

POST/v1/inboundAuthorization: Bearer <INBOUND_KEY>

Ключи этого типа больше не создаются: существующий ключ «Приём из программы» продолжает работать. Для новой интеграции регистрируйте обращение — SyBox сам добавит заметку в разговор.

cURL
curl -X POST https://api.sybox.io/v1/inbound \
  -H "Authorization: Bearer $SYBOX_INBOUND_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "15551234567",
    "text": "Order 4821 has left the warehouse.",
    "source": "Warehouse"
  }'
ПолеОписание
phoneобязательноstringНомер телефона клиента с кодом страны.
textобязательноstringТекст сообщения.
sourcestringНазвание программы, которое показывается на сообщении.

Ответ — {"ok": 1, "id": …}, где id — это идентификатор заметки в разговоре.

Только для входящих записей по проекту.

Ваша система в SyBox

Контакты: чтение и обновление

С помощью ключа типа «Чтение и обновление контактов» программа может находить контакт, читать его и изменять его имя, эл. почту, поля контакта и метки. Каждый вызов представляет собой POST-запрос с телом JSON.

Ключ действует от имени создавшего его человека: он находит только те контакты, которые этот человек может видеть, и только если ему разрешено открывать список контактов и видеть номера. Чтобы что-либо изменить, у этого человека должно быть разрешение на редактирование контактов. Ключ, создатель которого отключён, перестаёт работать.

POST/v1/contacts/searchAuthorization: Bearer <CONTACTS_KEY>

Находит контакты, у которых имя, телефон или эл. почта содержит q. По умолчанию limit равен 20, максимум — 50.

POST/v1/contacts/get

Один контакт по phone или по key (тот же contact.key, который отправляет вебхук).

POST/v1/contacts/update

Изменяет только то, что вы передаёте: name, email и fields (поля контакта по id или по названию вместе с их значениями; пустое значение очищает поле).

POST/v1/contacts/labels

Добавляет и удаляет метки по id или по названию: add и remove представляют собой списки.

curl -X POST https://api.sybox.io/v1/contacts/update \
  -H "Authorization: Bearer $SYBOX_CONTACTS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "201112223344",
    "name": "Sara Ali",
    "fields": { "city": "Alexandria" }
  }'

# Find:   POST /v1/contacts/search  {"q": "sara", "limit": 20}
# Read:   POST /v1/contacts/get     {"phone": "201112223344"}
# Labels: POST /v1/contacts/labels  {"phone": "201112223344", "add": ["VIP"], "remove": [7]}

Каждый вызов возвращает контакт (search — список контактов): key, name, phone, email, channel, labels и fields.

События

Вебхуки: события на вашем сервере

Добавьте свой эндпоинт в разделе Настройки › Интеграции › Вебхуки, выберите события и номера, и SyBox будет отправлять на него POST с каждым событием в момент, когда оно происходит. Событие WhatsApp приходит в собственном формате вебхуков Meta, поэтому обработчик, написанный для Meta, работает без изменений.

СобытиеОписание
messageВходящее сообщениеСообщение клиента.
statusСтатус сообщенияОтправлено, доставлено, прочитано или не доставлено — для отправленного вами сообщения.
reactionРеакцияКлиент отреагировал на сообщение.
doneДиалог отмечен как завершённыйДиалог был отмечен как завершённый: пользователем, автоматизацией или автоматически после молчания клиента.
labelМетка добавлена или удаленаК диалогу была добавлена метка или удалена из него.
contactКонтакт создан или обновлёнБыл создан контакт или изменились его имя, эл. почта либо поля контакта.
dealСделка закрытаВ диалоге была записана закрытая сделка.
caseОбращение CRM открыто или закрытоОбращение CRM было открыто или закрыто (решено, не решено или отменено).
Запрос · SyBox → вам
POST YOUR_WEBHOOK_URL
Content-Type: application/json
X-Hub-Signature-256: sha256=5d1f0c…
x-sybox-signature: sha256=5d1f0c…
x-sybox-event: message
x-sybox-timestamp: 1761400000

{
  "object": "whatsapp_business_account",
  "entry": [{
    "id": "WABA_ID",
    "changes": [{
      "field": "messages",
      "value": {
        "messaging_product": "whatsapp",
        "metadata": { "display_phone_number": "15550001111", "phone_number_id": "PHONE_NUMBER_ID" },
        "contacts": [{ "profile": { "name": "Sara" }, "wa_id": "15551234567" }],
        "messages": [{
          "from": "15551234567",
          "id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgASGBQ…",
          "timestamp": "1761400000",
          "type": "text",
          "text": { "body": "Is my order shipped?" }
        }]
      }
    }]
  }]
}

Медиафайлы. Собственная ссылка Meta открывается только с токеном Meta, поэтому SyBox заменяет её подписанной ссылкой на свою копию файла, действительной 24 часа. Голосовое сообщение также может содержать свой текст в sybox.transcription, если вебхук его запрашивает.

Другие каналы — Messenger, Instagram, Telegram и онлайн-чат — приходят в собственном формате SyBox, и только для события сообщения:

JSON
{
  "object": "sybox",
  "event": "message",
  "channel": "telegram",
  "line": "8123456789",
  "workspace": "yourworkspace",
  "contact": { "key": "tg:6423115118", "name": "Sara" },
  "message": {
    "id": "812345",
    "timestamp": 1761400000,
    "type": "image",
    "text": "",
    "caption": "The broken part",
    "media": { "url": "https://api.sybox.io/…", "mime": "image/jpeg" }
  }
}

События диалогов приходят в собственном формате SyBox для любого канала. Поле event точно указывает, что произошло:

Событие<code>event</code>
Диалог отмечен как завершённыйconversation.done
Метка добавлена или удаленаlabel.added · label.removed
Контакт создан или обновлёнcontact.created · contact.updated
Сделка закрытаdeal.closed
Обращение CRM открыто или закрытоcase.opened · case.closed
Тестовое событиеtest
{
  "object": "sybox",
  "event": "conversation.done",
  "channel": "whatsapp",
  "line": "201001234567",
  "workspace": "yourworkspace",
  "timestamp": 1761400000,
  "contact": {
    "key": "201112223344",
    "name": "Sara",
    "phone": "201112223344",
    "email": "",
    "channel": "whatsapp",
    "labels": [{ "id": 3, "name": "VIP" }],
    "fields": [{ "id": 2, "key": "city", "name": "City", "value": "Cairo" }]
  },
  "data": { "by": "Omar", "note": "Refund sent" }
}

Ключ подписи

ЗаголовокОписание
X-Hub-Signature-256sha256= и шестнадцатеричный HMAC-SHA256 исходного тела, вычисленный с секретом подписи вебхука, — тот же заголовок и тот же метод, что у Meta.
x-sybox-signatureТо же значение, чтобы эндпоинт, который также получает вызовы самой Meta, мог отличить наши.
x-sybox-eventЧто произошло: message, status, reaction, одно из событий диалога ниже (conversation.done, label.added…) или test.
x-sybox-timestampВремя отправки в секундах Unix.
x-sybox-signature-rsaВторая подпись: rsa-sha256= и RSA-SHA256 (PKCS#1 v1.5) в base64 от x-sybox-timestamp, точки и исходного тела.
x-sybox-key-idКаким из наших ключей она создана. Открытый ключ находится по адресу https://api.sybox.io/.well-known/sybox-webhook-key.

Проверяйте подпись по исходным байтам до их разбора и сравнивайте за постоянное время:

const crypto = require('crypto');

// express.raw: the signature is over the bytes as sent, so read them before any JSON parser.
app.post('/sybox/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sent = Buffer.from(req.get('X-Hub-Signature-256') || '');
  const want = Buffer.from('sha256=' + crypto
    .createHmac('sha256', process.env.SYBOX_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex'));
  if (sent.length !== want.length || !crypto.timingSafeEqual(sent, want)) return res.sendStatus(401);

  res.sendStatus(200);                       // answer first, then do the work
  const event = JSON.parse(req.body);
  const kind = req.get('x-sybox-event');     // message | status | reaction
});

Не хотите хранить общий секрет на своём сервере? Тогда проверяйте x-sybox-signature-rsa нашим открытым ключом. В любом случае отклоняйте метку времени старше пяти минут.

Ответьте любым кодом 2xx в течение 15 секунд. При любом другом исходе — ошибке, тайм-ауте, перенаправлении — попытка повторяется через 1, 2, 4, 8, 16 и 32 минуты, после чего событие отбрасывается. Событие может прийти больше одного раза: используйте id сообщения, чтобы пропускать повторы. Адрес должен быть публичным и использовать https.

Вебхук, который завершается ошибкой 20 раз подряд на протяжении более часа, автоматически отключается, а администратор рабочего пространства получает уведомление. Ожидающие события хранятся 24 часа: включите его снова, чтобы отправить их.

Для проверки нажмите «Отправить тестовое событие» на карточке вебхука. В окне «Доставки» на той же карточке показаны последние 50 доставок с ответом вашего сервера, и любую из них можно отправить снова.

Онлайн-чат

Онлайн-чат

Добавьте чат на свой сайт: посетители пишут там, а ваша команда отвечает в SyBox. Добавьте сайт в разделе Настройки › Сообщения › Каналы, на карточке «Онлайн-чат»; в открывшемся окне «Код для вашего сайта» вы получите код и ключи, описанные ниже. Три секретных ключа показываются только один раз.

КлючНачинается сДля чего
Открытый ключlcpk_Размещается на ваших страницах. Его можно публиковать: он только указывает сайт, а чат отображается только на адресах из вашего списка «Адреса сайтов».
Ключ подписи для авторизованных пользователейlcid_Только на вашем сервере. Подписывает каждого авторизованного пользователя, чтобы никто не мог писать от чужого имени.
Ключ сервера (для собственного экрана чата)lcsk_Только на вашем сервере. Нужен только сайту, который создаёт собственное окно чата вместо готового чата.
Ключ подписи ответовlcwh_Только на вашем сервере. Подтверждает, что ответ, отправленный на ваш сервер, пришёл от SyBox.

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

Если выключить сайт, чат сразу скрывается. Вызовы вашего сервера по-прежнему принимаются и сохраняются.

Добавьте чат на свой сайт

Вставьте код один раз на каждую страницу, прямо перед закрывающим тегом body. Чат отображается только на адресах из списка «Адреса сайтов», а его цвет, положение, заголовок и приветствие настраиваются в SyBox, а не в коде.

HTML
<script async src="https://api.sybox.io/lc/w.js" data-key="lcpk_YOUR_PUBLIC_KEY"></script>

WordPress: скачайте наш плагин в том же окне. Shopify: в приложении SyBox для Shopify включите «Онлайн-чат в этом магазине», затем SyBox Live Chat в разделе App embeds — авторизованный покупатель распознаётся без каких-либо дополнительных действий.

Авторизованные пользователи: ключ подписи

Когда пользователь вошёл на ваш сайт, ваш сервер сообщает чату, кто он, с помощью короткого токена — JWT, HS256, — подписанного ключом подписи. Тогда у пользователя один разговор под его именем на всех устройствах, и никто не сможет открыть его, введя чужой id.

  1. После входа ваш сервер формирует токен из перечисленных ниже claims.
  2. Страница передаёт его чату как identity — до кода чата.
  3. Если вход произошёл без перезагрузки страницы, вызовите вместо этого SyBoxChat('identify', token).
ПолеОписание
subобязательноstringid вашего пользователя, до 120 символов. Он не может начинаться с v_.
expобязательноnumberСрок действия в секундах Unix — не более чем на сутки вперёд. Десяти минут достаточно: токен читается только при открытии чата.
namestringИмя, которое показывается в разговоре.
emailstringАдрес электронной почты пользователя.
phonestringНомер телефона пользователя.
picturestringАдрес https фотографии пользователя, до 500 символов. SyBox хранит свою копию и загружает её заново при следующем входе, если адрес изменился.
function b64url(string $s): string { return rtrim(strtr(base64_encode($s), '+/', '-_'), '='); }

$header  = b64url(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
$payload = b64url(json_encode([
    'sub'   => (string) $user->id,
    'name'  => $user->name,
    'email' => $user->email,
    'exp'   => time() + 600,          // ten minutes
]));
$signature = b64url(hash_hmac('sha256', "$header.$payload", getenv('SYBOX_LC_SIGNING_KEY'), true));
$token = "$header.$payload.$signature";
HTML
<script>
  window.SyBoxChatConfig = { identity: "TOKEN_FROM_YOUR_SERVER", locale: "en" };
</script>
<script async src="https://api.sybox.io/lc/w.js" data-key="lcpk_YOUR_PUBLIC_KEY"></script>

Создавайте токен только на своём сервере. Ключ подписи на веб-странице позволит любому писать от имени любого из ваших пользователей. И никогда не размещайте на странице просто id пользователя вместо токена: так любой сможет выбрать, чей разговор читать.

Если включено «Только авторизованные пользователи моего сайта», посетитель без токена не видит чат.

Посетителя, который писал в чат до входа, один раз спросят в том же браузере, продолжить ли этот разговор в его учётной записи. Вам для этого ничего делать не нужно.

Управление чатом со своей страницы

Код чата добавляет на вашу страницу одну функцию, SyBoxChat:

JavaScript
SyBoxChat('open');                       // open the chat
SyBoxChat('close');                      // close it
SyBoxChat('identify', token);            // after a sign-in without a page reload
SyBoxChat('locale', 'fr');               // the chat's language
SyBoxChat('on', 'message', function (m) {
  // a reply arrived
});
SyBoxChat('on', 'open', function () { /* the chat opened */ });
SyBoxChat('on', 'close', function () { /* the chat closed */ });

// Before the chat's code, every setting is optional:
window.SyBoxChatConfig = {
  identity: 'TOKEN_FROM_YOUR_SERVER',    // a signed-in user (see above)
  name: 'Sara', email: 'sara@example.com', // a visitor without an account: fills the form for them
  locale: 'ar',
  serviceWorker: 'auto'                   // 'auto' | '/your-sw.js' | 'site' | 'off'
};

Уведомления после закрытия сайта

Пока открыта страница вашего сайта, ответ сам показывается как уведомление. Чтобы достучаться до посетителя после закрытия сайта, браузеры требуют service worker на вашем собственном домене: отдавайте эту одну строку как /sybox-lc-sw.js в корне сайта, с типом application/javascript.

/sybox-lc-sw.js
try { importScripts('https://api.sybox.io/lc/sw.js'); } catch (e) {}

Выберите режим в SyBox («Уведомления, когда сайт закрыт») или на странице через serviceWorker: "auto" (файл выше, по умолчанию), собственный путь, "site" (поместите ту же строку в начало своего service worker) или "off".

Чат никогда не заменяет service worker, который уже есть на вашем сайте, а в файле нет обработчика fetch, поэтому он никогда не видит запросы вашего сайта. Плагин WordPress отдаёт этот файл за вас. Safari на iPhone не показывает уведомления для обычного сайта.

Собственное окно чата: ключ сервера

Сайт или приложение с собственным окном чата обращается к SyBox со своего сервера вместо готового чата. Каждый вызов передаёт Authorization: Bearer lcsk_…. Ваш сервер указывает пользователя по вашему собственному id, и ему можно доверять, потому что у него есть ключ.

ВызовОписание
POST /v1/livechat/messagesВаш пользователь пишет сообщение. client_id делает повторный запрос безопасным: сообщение с тем же id сохраняется один раз ("duplicate": 1).
POST /v1/livechat/uploadФайл в виде multipart form data: file, user_id, client_id. До 16 MB: изображения, голосовые сообщения, видео, PDF, текст и документы Office.
GET /v1/livechat/messages?user_id=&since=&before=&limit=Разговор: сообщения после since и agents_read_id — самое новое из сообщений вашего пользователя, прочитанное агентом.
POST /v1/livechat/readВаш пользователь прочитал ответы до up_to.
POST /v1/livechat/editВаш пользователь редактирует своё сообщение в течение 15 минут.
POST /v1/livechat/deleteВаш пользователь удаляет своё сообщение для всех в течение 24 часов.
POST /v1/livechat/reactРеакция на любое сообщение; пустой emoji её убирает.
POST /v1/livechat/typingВаш пользователь печатает: отправляйте on: 1 каждые несколько секунд и on: 0, когда он перестаёт.
curl -X POST https://api.sybox.io/v1/livechat/messages \
  -H "Authorization: Bearer $SYBOX_LC_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user":    { "id": "u42", "name": "Sara Ali", "email": "sara@example.com", "locale": "ar" },
    "message": { "client_id": "a1b2c3", "text": "Hi, is my order on its way?", "reply_to": "" }
  }'

Сообщение в истории и в ответах:

JSON
{
  "id": 990,
  "guid": "b81d…",
  "dir": "out",                       // "in" = your user wrote it, "out" = your team
  "type": "image",                    // text | image | audio | video | file
  "text": "Here is the tracking page",
  "media": { "url": "https://api.sybox.io/…", "mime": "image/png", "name": "tracking.png", "w": 1080, "h": 720 },
                                       // a voice note or a video also has "secs"
  "status": 3,                         // on your user's messages: 1 received, 3 read by an agent
  "reply_to": "7c9e…",
  "client": "a1b2c3",                  // on your user's messages: the client_id you sent
  "react_us": "",                      // your team's reaction on it
  "react_me": "👍",                    // your user's reaction on it
  "at": "2026-10-25T09:41:07Z",
  "edited": 0
}

История пользователя, который ещё ни разу не писал, возвращает 404 unknown_user: покажите пустой разговор.

Ссылки на медиафайлы подписаны и действуют один час; за новой ссылкой обратитесь повторно. SyBox никогда не загружает файл по присланному вами адресу: загрузите его сами. До 120 вызовов в минуту на один сайт.

Ответы на вашем сервере: ключ подписи ответов

Укажите «Адрес вашего сервера для ответов» в настройках сайта. Когда ваша команда отвечает, редактирует, удаляет или читает сообщения, SyBox отправляет туда POST с событием, подписанный ключом подписи ответов.

ЗаголовокОписание
x-sybox-signaturet=TIMESTAMP,v1=SIGNATURE — TIMESTAMP в секундах Unix, SIGNATURE — HMAC-SHA256 в hex от TIMESTAMP.BODY (исходного тела запроса), вычисленный с ключом подписи ответов.
x-sybox-eventmessage.created, message.updated, message.deleted или messages.read.
x-sybox-deliveryОдинаков при каждом повторе одного события: используйте его, чтобы пропускать повторы.

Тело содержит event, line_id, user_id и message — или message_id при удалении и read_id для messages.read: сообщения вашего пользователя прочитаны до этого id.

const crypto = require('crypto');

app.post('/sybox/livechat', express.raw({ type: 'application/json' }), (req, res) => {
  const parts = Object.fromEntries((req.get('x-sybox-signature') || '').split(',').map(p => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(401);   // older than five minutes

  const want = Buffer.from(crypto.createHmac('sha256', process.env.SYBOX_LC_REPLY_KEY)
    .update(t + '.').update(req.body).digest('hex'));
  const sent = Buffer.from(parts.v1 || '');
  if (sent.length !== want.length || !crypto.timingSafeEqual(sent, want)) return res.sendStatus(401);

  res.sendStatus(200);
  const delivery = req.get('x-sybox-delivery');   // seen it already? ignore
  const evt = JSON.parse(req.body);               // { event, line_id, user_id, message | message_id | read_id }
});

Проверяйте подпись по исходному телу, отклоняйте t старше пяти минут и быстро отвечайте кодом 2xx. При любом другом исходе попытка повторяется через 10 с, 30 с, 2 мин, 10 мин и 30 мин, после чего событие отбрасывается. SyBox обращается только к публичным адресам и никогда не следует перенаправлениям.

Коды причин

Каждый ответ онлайн-чата — это JSON: {"ok": 1, …} или {"ok": 0, "error": "CODE"} с кодом, а не с фразой:

КодСтатусОписание
unauthorized401Ключ сервера неверен или заменён.
invalid_json invalid_payload invalid_user_id400Запрос имеет не тот формат, который ожидается.
empty_message no_file empty_file400Нет ни текста, ни файла.
too_long400Отредактированный текст длиннее 4 096 символов.
not_an_emoji400emoji не является эмодзи.
not_found404Такого сообщения нет, или оно не принадлежит этому пользователю.
too_late409Время для редактирования или удаления истекло.
file_type_not_allowed400Файлы такого типа не принимаются.
file_too_large413Файл больше 16 MB.
blocked403Ваша команда заблокировала этого пользователя.
unknown_user404Для этого id пользователя пока нет разговора.
rate_limited429Слишком много вызовов: снизьте частоту и попробуйте ещё раз.
maintenance503SyBox обновляется: попробуйте ещё раз через Retry-After секунд.
server_error500Произошёл сбой на нашей стороне.

Замена ключа

Потеряли ключ или подозреваете утечку? На карточке сайта нажмите «Новый ключ подписи», «Новый ключ сервера» или «Новый ключ подписи ответов». После создания нового ключа сервера старый продолжает работать ещё сутки, чтобы вы могли переключиться без перерыва; остальные ключи перестают работать сразу.