Перейти к основному содержанию

Руководство разработчика

Интеграция сторонних уведомлений

Текущий контракт аутентификации, запросов, идемпотентности, лимитов, результатов и безопасности.

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

В веб-панели создайте стороннее приложение для нужной компании. Скопируйте App ID и App Secret, а при необходимости итогового результата укажите необязательный Callback URL.

Engine создаёт записи для пользователей, выбранных необязательным audience (по умолчанию для всей подходящей компании), и пытается отправить системный Push только на подходящие зарегистрированные устройства.

Адрес и аутентификация

Среды API:

Каждый межсерверный JSON-запрос должен передавать Authorization: Basic Base64(KEY:SECRET) через HTTP Basic Authentication, где KEY — это App ID, а SECRET — App Secret:

Endpoint: POST /openapi/v1/notifications.

bash
curl --request POST 'https://<your-engine-host>/openapi/v1/notifications' \
  --header 'Authorization: Basic <Base64(AppID:AppSecret)>' \
  --header 'Content-Type: application/json' \
  --data '{"requestId":"order-20260819-001","title":"Заказ выполнен","body":"Ваш заказ выполнен.","imageUrls":["https://cdn.example.com/order/001-1.webp","https://cdn.example.com/order/001-2.webp"],"displayFields":[{"label":"Название магазина","value":"Лапшичная Сяо Хань (Ванцзин)"},{"value":"ORD-10086"}],"tags":["Выполнен","Главный магазин"]}'

Контракт запроса

Сводка полей

ПолеОбязательноТипЗначение
requestIdДаstringУникальный ID запроса от отправителя и ключ идемпотентности. Обрезается и не может быть пустым.
titleДаstringЗаголовок уведомления. Обрезается и не может быть пустым.
bodyДаstringТекст уведомления. Обрезается и не может быть пустым.
imageUrlsНетstring[]Изображения в деталях, в порядке показа.
displayFieldsНетobject[]Дополнительные текстовые поля в деталях.
tagsНетstring[]Теги plain text только в деталях, в заданном порядке.
audienceНетobjectОграничивает пользователей или роли; без поля получает вся подходящая компания.

Изображения: imageUrls

  • Не передавайте поле, используйте null или [], если изображений нет; иначе передайте 1-9 URL по порядку.
  • URL обрезается, должен быть уникальным и абсолютным HTTPS URL без имени пользователя и пароля.
  • Максимум 2048 символов Unicode на URL. Один неверный URL отклоняет весь запрос.
  • Engine хранит URL, но не загружает, не проверяет, не проксирует и не кэширует их.

Поля деталей: displayFields

ПодполеОбязательноОграничениеОтображение
labelНетОбрезается, не пустое, до 32 Unicode символовНазвание поля.
valueДаОбрезается, не пустое, до 256 Unicode символовЗначение поля.
json
{
  "displayFields": [
    { "label": "Магазин", "value": "Little Han Noodles (Wangjing)" },
    { "value": "ORD-10086" }
  ]
}
  • Отсутствие, null или [] означает отсутствие строк; максимум 10 объектов.
  • Без label или при label: null значение value занимает всю строку.
  • Содержимое всегда plain text; HTML, Markdown, ссылки, вложенные данные, типы и стили не поддерживаются.

Теги деталей: tags

  • Отсутствие, null или [] означает отсутствие тегов. Иначе передайте не более 8 strings в порядке показа.
  • Каждый тег обрезается и должен содержать 1-20 символов Unicode. Пустое, нестроковое, слишком длинное или повторное после обрезки значение отклоняет весь запрос с INVALID_REQUEST.
  • Теги — неинтерактивный plain text только в detail уведомления; порядок сохраняется.

Получатели: audience

typeСелекторЗначение ID
USERSuserIdsID участника компании
ROLESrolesCompanyRole ID; получают допустимые участники любой указанной роли.

Скопируйте ID участника компании из списка участников компании в Web Admin. Участник должен принадлежать компании, связанной со сторонним приложением.

json
{
  "audience": { "type": "USERS", "userIds": ["companyMemberId"] }
}
json
{
  "audience": { "type": "ROLES", "roles": ["roleId"] }
}
  • Если audience нет или передано null, получают все подходящие активные пользователи связанной компании.
  • Список содержит 1-100 исходных строк; ID обрезаются, пустые игнорируются, набор удаляет повторы и сортируется.
  • Несуществующие, отключённые, удалённые и чужие для компании ID игнорируются; остальные цели получают уведомление.
  • Допустимый селектор без целей принимается и завершается с recipientCount=0; отправки всей компании не будет.
  • Другой селектор можно опустить или задать null. Смешанные не-null поля, неизвестные type или поля, отсутствующие или пустые списки, нестроковые элементы и более 100 записей дают INVALID_REQUEST.

Ограничения и идемпотентность

  • Полный body ограничен 256 KiB. Для requestId, title, body максимум 128, 100 и 1000 Unicode символов.
  • Для App ID действует лимит 10 запросов в минуту и 100 в час.
  • Повтор requestId с эквивалентным нормализованным содержимым возвращает сохранённый результат. Порядок важен для изображений, полей и tags, но не для audience.
  • Другое содержимое с тем же requestId даёт IDEMPOTENCY_CONFLICT. Итоговый status: SUCCESS, PARTIAL_SUCCESS или FAILED.

Ответ о принятии

Корректный запрос возвращает HTTP 202:

json
{
  "requestId": "order-20260819-001",
  "notificationId": "00000000-0000-0000-0000-000000000001",
  "status": "ACCEPTED"
}

ACCEPTED означает, что Engine сохранил запрос для отправки, но не подтверждает показ уведомления через APNs/FCM на устройстве.

Результат Callback

Если настроен Callback URL, Engine отправляет итоговый результат:

json
{
  "requestId": "order-20260819-001",
  "status": "PARTIAL_SUCCESS",
  "recipientCount": 8,
  "pushAcceptedCount": 1,
  "pushFailedCount": 1
}

recipientCount — число пользователей-получателей в центре уведомлений. pushAcceptedCount и pushFailedCount — количество пакетов Push Outbox, а не пользователей или устройств и не подтверждение показа системного баннера.

Проверка Callback

Каждая проверка Callback из панели администратора отправляет отдельное тестовое событие:

json
{
  "requestId": "callback-test-<unique-id>",
  "status": "SUCCESS",
  "recipientCount": 1,
  "pushAcceptedCount": 1,
  "pushFailedCount": 0,
  "test": true
}

Если test равно true, проверьте структуру и верните 2xx, но не сопоставляйте тестовое событие с отправленным запросом уведомления и не изменяйте состояние бизнес-доставки. Счётчики содержат фиксированные тестовые значения, а не число реальных получателей или пакетов Push Outbox.

Итоговый status: SUCCESS, PARTIAL_SUCCESS или FAILED. Подтвердите получение любым ответом 2xx.

Иначе повторы выполняются через 1 минуту, 5 минут, 30 минут, 2 часа, 6 часов и 24 часа. Сейчас HMAC-подписи нет: используйте HTTPS, сверяйте requestId для нетестовых результатов, ограничивайте доступ и обрабатывайте Callback идемпотентно.

Ошибки

HTTPCodeЗначение
400INVALID_REQUESTОшибка Content-Type, JSON, размера или полей.
401INVALID_APP_CREDENTIALSНеверные App ID или App Secret.
403APPLICATION_INACTIVEПриложение или компания неактивны.
409IDEMPOTENCY_CONFLICTrequestId повторён с другим содержимым.
429RATE_LIMITEDПревышен лимит приложения.
503NOTIFICATION_UNAVAILABLEСервис временно недоступен.

Безопасность и область доставки

  • App ID и App Secret предназначены только для связи сервер-сервер. Не помещайте их в браузер, мобильное приложение, открытый репозиторий, URL или клиентское хранилище. При утечке сбросьте App Secret.
  • Приватные ID целей никогда не записываются в logs приложения. Приватный selector и его ID также не попадают в GraphQL, Push, Callback, realtime или navigation.
  • Без audience включаются все подходящие активные пользователи компании; с audience — только допустимые найденные цели. Каждый допустимый получатель получает запись и учитывается в recipientCount.
  • Системный Push выполняется только для активного PushInstallation, разрешающего категорию BUSINESS, и не гарантирует системный баннер на каждом устройстве.
  • Полный список imageUrls доступен только в авторизованном detail. Engine может отправить первый URL из imageUrls в APNs/FCM для системного баннера. displayFields и tags не входят в Push или Callback и остаются неинтерактивным plain text; не передавайте секреты, токены доступа, платёжные данные и другие чувствительные значения.
  • title и body должны оставаться полными и понятными без изображений. Ошибка загрузки или отображения изображения не изменяет статус accepted, Callback или Push.
  • Mobile client принимает не более 5 MiB на изображение, а тайм-аут всей операции загрузки составляет 10 секунд. Client не следует редиректам 3xx, поэтому каждый URL должен возвращать изображение напрямую.
  • Mobile client загружает изображения напрямую из публичного CDN. Используйте URL без аутентификации и Cookie, сохраняйте их доступными не менее 90 дней и учитывайте, что CDN получает IP пользователя, время запроса и User-Agent.
  • Для стабильной истории используйте immutable object URL.