Zapnoty — POST /v1/send — отправка

Документация API

REST API для уведомлений через Telegram и Max. Подписчики, OTP, рассылки, формы, helpdesk.

POST /v1/send

Отправка персонального уведомления конкретному подписчику.

Параметры

subscriber_id string (UUID)

UUID подписчика (из списка подписчиков)

text string

Текст сообщения (до 4000 символов). Обязателен, если не указан template

format string

Формат текста: plain (по умолчанию), markdown или html. Взаимоисключающее с entities — если переданы entities, format должен быть пустым.

entities array

Структурированное форматирование (рекомендуемый формат). Массив [{type, offset, length, ...}], offset/length в UTF-16 единицах. Типы: bold, italic, underline, strikethrough, code, pre, blockquote, spoiler, mention, text_link (с полем url). Защищает от HTML/Markdown injection.

external_id string

Альтернатива subscriber_id: свой ID клиента в его системе. Резолвит все активные подписки этого клиента (multi-channel one-shot отправка). Mutually exclusive с subscriber_id.

channels array

Фильтр каналов при resolve по external_id, массив строк (например, ["telegram","max"]). Если не задан — все active подписки.

channel string

Одиночный канал доставки (telegram/max) — legacy-альтернатива массиву channels, действует как channels: [channel].

media object

Объект медиа: {type, url}. Типы: photo, video, document

buttons array

Массив рядов кнопок: [[{text, url}]] или [[{text, callback_data}]]

template string

Slug шаблона вместо text

vars object

Переменные для шаблона: {key: value}

permission string

Фильтр по разрешению: отправить только подписчикам с этим ключом

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

POST /v1/send
 
{
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"text": "Заказ #1042 отправлен!",
"format": "markdown",
"buttons": [[{
"text": "Отследить",
"url": "https://example.com/track/1042"
}]]
}

Ответ

Ответ — агрегированный: sent / failed — счётчики, details — массив по каждому получателю. Поле delivery_id — id записи в журнале доставок проекта (для ссылки в саппорт / аудита, виден в дашборде → Доставки); есть и у успешных, и у неуспешных. Статус всегда 200, даже если часть отправок failed — смотрите на details.

HTTP 200
{
"sent": 1,
"failed": 0,
"details": [
{
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"channel": "telegram",
"success": true,
"error": null,
"delivery_id": "7c9e6a1b-..."
}
]
}

Идемпотентность

Чтобы повторный запрос (ретрай при таймауте) не создал дубль, передавайте заголовок Idempotency-Key — строка 8–128 символов [A-Za-z0-9_-], уникальная на каждую операцию.

  • Тот же ключ + то же тело → возвращается тот же ответ (статус 200) с заголовком X-Zapnoty-Idempotent-Replay: true. Повторная отправка не выполняется.
  • Тот же ключ + другое тело → 409 idempotency_conflict.
  • Запрос с тем же ключом ещё обрабатывается → 425 idempotency_in_progress, повторите чуть позже.
POST /v1/send
Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890
{ "subscriber_id": "...", "text": "..." }
# Повтор с тем же ключом и тем же телом → тот же ответ (200):
HTTP 200
X-Zapnoty-Idempotent-Replay: true

Entities — структурированное форматирование

Рекомендуемая замена format=html/markdown. Вместо парсинга разметки клиент передаёт массив диапазонов: позиция, длина и тип форматирования. Защищает от HTML/Markdown injection — текст никогда не интерпретируется, ставится «как есть».

offset и length считаются в UTF-16 code units (как Telegram). Для большинства языков 1 символ = 1 единица; emoji (👋) занимает 2 единицы. Entities должны быть отсортированы по offset и не пересекаться.

Типы: bold, italic, underline, strikethrough, code, pre, blockquote, spoiler, mention, text_link (с полем url, проходит SSRF-проверку приватных IP). Spoiler и mention в Max игнорируются (нет аналога).

POST /v1/send
 
{
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"text": "Заказ #1042 готов к выдаче",
"entities": [
{"type": "bold", "offset": 0, "length": 11},
{"type": "text_link", "offset": 16, "length": 11, "url": "https://shop.ru/orders/1042"}
]
}

Шаблоны

Шаблоны позволяют переиспользовать текст с переменными. Создаются в дашборде или через API.

Использование: передайте template и vars вместо text в /v1/send.

Переменные в шаблоне обозначаются {{имя}}. Например: «Заказ {{order_id}} доставлен».

POST /v1/send
 
{
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"template": "order_delivered",
"vars": {
"order_id": "1042",
"customer": "Иван"
}
}

Медиа и кнопки

К уведомлениям можно прикрепить медиа-файлы и inline-кнопки.

Типы медиа: photo, video, document. Передайте URL файла.

Кнопки — двумерный массив: внешний массив — ряды, внутренний — кнопки в ряду.

  • Кнопка с URL: {"text": "Открыть", "url": "https://..."}
  • Кнопка с callback: {"text": "Да", "callback_data": "confirm_123"}
POST /v1/send
 
{
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"text": "Ваш заказ готов",
"media": {
"type": "photo",
"url": "https://example.com/photo.jpg"
},
"buttons": [[
{"text": "Подробнее", "url": "https://..."},
{"text": "Отмена", "callback_data": "cancel_123"}
]]
}

1 медиа + кнопки — поддерживается. Несколько медиа + кнопки — не поддерживается (ограничение Telegram). Caption с медиа: до 1024 символов (Telegram) / до 4000 (Max). Текст без медиа — до 4000 символов.

Связанные разделы