Нотификации

Что такое нотификации

Нотификация — это HTTP POST-запрос, который MassAccess отправляет на ваш сервер при наступлении события в системе. Когда изменяется статус сообщения, обновляется номер телефона клиента или клиент взаимодействует с ботом — ваш сервер получает JSON с данными события. Никаких дополнительных API-вызовов не требуется — нотификации отправляются автоматически.

Запросы отправляются на Webhook URL, настроенный в панели управления вашей компании (Настройки → Webhook URL). Каждый запрос содержит HMAC-SHA256 подпись (заголовок X-Signature), чтобы вы могли проверить его подлинность.

Как настроить нотификации

Чтобы начать получать нотификации, войдите в панель управления компании, перейдите в раздел «Настройки» и укажите URL вашего сервера в поле «Webhook URL». После сохранения MassAccess начнёт отправлять HTTP POST-запросы на этот URL для каждого события в вашей компании.

Кнопка «Проверить» отправляет один подписанный тестовый запрос с event = webhook.test и data.test = true. Запрос содержит те же прикладные заголовки доставки, что и бизнес-события: X-Signature, X-Webhook-Id, Idempotency-Key и X-Webhook-Attempt. Адрес считается доступным, если сервер отвечает кодом HTTP 2xx; 4xx означает, что сервер доступен, но отверг запрос, а 5xx, тайм-аут или сетевая ошибка — что endpoint недоступен. Проверка подтверждает только приём безопасного webhook.test, но не гарантирует приём каждого типа событий. Полная проверка выполняется реальной доставкой или через /api/v1/webhooks/replay.

Типы событий

MassAccess отправляет нотификации для следующих событий:

СобытиеОписание
message.statusОтправляется при переходе сообщения в терминальный статус: sent или failed. Поле data.state содержит текущий статус. Если data.state равен "failed", дополнительно включаются поля data.error_code и data.error_description.
client.phone.changedОтправляется при изменении номера телефона клиента через эндпоинт /api/v1/change_phone. Поле data.state равно "completed" при успехе. Если изменение не удалось, data.state равен "failed" с полями error_code и error_description.
client.checkОтправляется при проверке существования клиента по номеру телефона через эндпоинт /api/v1/client/check. data.exists указывает, найден ли клиент, а data.bots содержит список ботов, к которым он подключён.
client.registeredОтправляется при регистрации нового клиента через вашего бота в Telegram или MAX. Полезная нагрузка включает phone, bot_name и service. Это событие отправляется один раз при регистрации клиента.

Формат нотификации

Формат входящих webhook-уведомлений о статусе сообщений. Уведомления отправляются на URL из настроек компании.

Заголовки запроса

ЗаголовокОписание
X-SignatureHMAC-SHA256 подпись тела запроса
X-Webhook-IdСтабильный UUID события; совпадает с event_id в теле.
Idempotency-KeyКлюч дедупликации; совпадает с X-Webhook-Id.
X-Webhook-AttemptНомер попытки доставки, начиная с 1.
Content-Typeapplication/json
User-AgentMassAccess-Webhook/2.0

Структура полезной нагрузки

Каждое тело нотификации — это объект JSON с общей структурой. Поля верхнего уровня присутствуют всегда; содержимое поля data зависит от типа события. Ниже приведены примеры для каждого типа события.

Изменение статуса сообщения

Отправляется, когда сообщение успешно доставлено получателю. data.state = sent.

json
{
  "event": "message.status",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "state": "sent"
  }
}

Изменение статуса сообщения (error)

Отправляется при ошибке доставки. data.state = failed + error_code и error_description.

json
{
  "event": "message.status",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "state": "failed",
    "error_code": "NETWORK_ERROR",
    "error_description": "Network error"
  }
}

Смена номера телефона

Отправляется при успешной смене номера через API /api/v1/change_phone. data.state = completed.

json
{
  "event": "client.phone.changed",
  "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "state": "completed"
  }
}

Смена номера телефона (ошибка)

Отправляется при ошибке смены номера. data.state = failed + error_code и error_description.

json
{
  "event": "client.phone.changed",
  "event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "state": "failed",
    "error_code": "INVALID_PHONE",
    "error_description": "Invalid phone number format"
  }
}

Регистрация клиента в боте

Отправляется при регистрации нового клиента через бота (Telegram/MAX). Содержит phone, bot_name, service. Однократно.

json
{
  "event": "client.registered",
  "event_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "phone": "12345678901",
    "bot_name": "my_telegram_bot",
    "service": "telegram"
  }
}

Проверка клиента (exists = true)

Отправляется, когда клиент с указанным номером найден в компании. data.exists = true, data.bots содержит список ботов с полями name и service.

json
{
  "event": "client.check",
  "event_id": "3bd290b8-09b4-48c4-920b-85b93fc3859b",
  "timestamp": "2026-07-28T21:45:31Z",
  "data": {
    "exists": true,
    "bots": [
      {
        "name": "main_max",
        "service": "max"
      }
    ]
  }
}

Проверка клиента (exists = false)

Отправляется, когда клиент с указанным номером не найден. data.exists = false, data.bots пуст.

json
{
  "event": "client.check",
  "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "timestamp": "2026-07-28T21:45:31Z",
  "data": {
    "exists": false,
    "bots": []
  }
}

Поля нотификации

ИмяТипОбязателенОписание
eventstringДаТип события: message.status | client.phone.changed | client.registered
event_idstring (UUID)ДаУникальный идентификатор сообщения (UUID)
timestampstring (ISO 8601)ДаВремя события в ISO 8601 UTC
data.statestringНетСтатус сообщения: new, processing, sent, failed
data.error_codestringНетКод ошибки (только при ошибке)
data.error_descriptionstringНетЧеловекочитаемое описание ошибки (только при ошибке)
data.client_idintegerНетClient identifier (for client.* events)
data.phonestringНетClient phone number (for client.* events)
data.bot_namestringНетBot name that triggered the event (for client.* events)
data.existsbooleanНетWhether the client exists (client.check event)
data.botsarrayНетList of bots the client is connected to (client.check event). Each bot has name (string) and service (string) fields.

Архитектура отправки

Ответ вашего сервера

Для подтверждения получения уведомления ваш сервер должен вернуть HTTP 200 OK. Если ответ не 200 — MassAccess будет повторять отправку.

Ваш сервер должен ответить в течение 10 секунд. Если сервер возвращает статус, отличный от 2xx, или время истекло, MassAccess повторяет доставку с экспоненциальной задержкой. После 5 неудачных попыток нотификация отбрасывается.

HTTP StatusОписание
200 OKНотификация доставлена успешно — MassAccess прекращает повторные попытки.
4xx / 5xxДоставка нотификации не удалась — MassAccess повторит попытку с экспоненциальной задержкой (задержки: ~1с, ~5с, ~25с, ~125с, ~625с).

Проверка подписи

Заголовок X-Signature содержит HMAC-SHA256 подпись сырого тела запроса (до парсинга JSON). Чтобы проверить, что нотификация пришла от MassAccess:

  1. Получите сырое тело HTTP-запроса как байты (не парсите JSON сразу)
  2. Вычислите HMAC-SHA256 от сырых байтов тела, используя ваш API-ключ в качестве секрета
  3. Сравните полученную HEX-строку с заголовком X-Signature. Используйте специальную функцию безопасного сравнения из стандартной библиотеки вашего языка (например: compare_digest в Python, hash_equals в PHP, timingSafeEqual в Node.js), а не обычный оператор ==. Безопасное сравнение всегда занимает одинаковое время — это не позволяет злоумышленнику подбирать подпись по времени ответа сервера. Если строки не совпали — отклоните запрос, он поддельный.

Проверяйте заголовок X-Signature (HMAC-SHA256) чтобы убедиться в подлинности уведомления:

python
import hmac
import hashlib

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    """Verify webhook HMAC-SHA256 signature."""
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Коды ошибок нотификаций

Справочник кодов ошибок, которые могут передаваться в поле error_code webhook-нотификаций (message.status, client.phone.changed и др.). Для каждого кода указан мессенджер, в котором он может возникнуть: Telegram, MAX, оба, либо «—» для ошибок, не связанных с конкретным мессенджером.

Доставка

КодОписаниеМессенджер
TOKEN_INVALIDТокен бота недействителен или отозван мессенджером. Проверьте токен в настройках бота.Telegram · MAX
BAD_REQUESTМессенджер отклонил запрос из-за некорректных параметров сообщения.Telegram · MAX
RATE_LIMITПревышен лимит частоты отправки мессенджера. Сообщение будет повторено с задержкой.Telegram · MAX
WEBHOOK_CONFLICTКонфликт webhook Telegram: другой сервер уже установил webhook для этого бота.Telegram
NETWORK_ERRORСетевая ошибка при обращении к API мессенджера (обрыв соединения, недоступность).Telegram · MAX
DNS_ERRORНе удалось разрешить доменное имя API мессенджера.Telegram · MAX
SSL_ERRORОшибка TLS/SSL при установке защищённого соединения с API мессенджера.Telegram · MAX
TIMEOUTПревышено время ожидания ответа от API мессенджера.Telegram · MAX
CONNECTION_CLOSEDСоединение с API мессенджера было закрыто до получения ответа.Telegram · MAX
PROXY_ERRORОшибка прокси-релея (промежуточного сервера доставки).Telegram · MAX
CONFIG_MISSINGНе задан URL webhook мессенджера в настройках платформы.Telegram · MAX
DELIVERY_FAILEDОбщая ошибка доставки сообщения. Причина не классифицирована или неизвестна.Telegram · MAX

Получатель

КодОписаниеМессенджер
USER_BLOCKED_BOTПолучатель заблокировал бота. Telegram: HTTP 403 «bot was blocked by the user»; MAX: chat.denied. Доставка завершается без повторных попыток.Telegram · MAX
USER_NOT_FOUNDПолучатель не найден (не зарегистрирован в мессенджере).Telegram · MAX
USER_INACTIVEПолучатель неактивен или недоступен в мессенджере.Telegram · MAX
DIALOG_SUSPENDEDДиалог приостановлен: MAX вернул error.dialog.suspended (получатель заблокировал бота или действует ограничение модератора).MAX

Смена номера

КодОписаниеМессенджер
PHONE_NEVER_EXISTEDНомер никогда не был зарегистрирован ни в одном мессенджере.
PHONE_ALREADY_INACTIVEНомер уже неактивен.
PHONE_MULTIPLE_CLIENTSНомер привязан к нескольким разным учётным записям.
PHONE_NOT_LINKEDНомер не привязан ни к одной учётной записи.
PHONE_NOT_IN_BOTНовый номер никогда не был зарегистрирован ни в одном мессенджере.
PHONE_NOT_ACTIVEНовый номер неактивен.
MIGRATION_SAME_CLIENTСтарый и новый номера уже привязаны к одной учётной записи.