REST API для рассылок и отчётности
Клиентское REST API позволяет запускать рассылки по шаблонам WhatsApp Business API и забирать статистику доставки из своих систем: CRM, BI-панели, скрипты автоматизации.
API работает по модели «запрос — ответ»: вы сами обращаетесь к сервису тогда, когда вам нужны данные. Исходящих вебхуков на вашу сторону сервис не отправляет.
По умолчанию в ответах приходят метаданные сообщения, значения параметров шаблона и статусы доставки — без содержимого переписки. Тексты сообщений передаются, если вы явно запросите их параметром include_text; это сделано для того, чтобы уже написанная интеграция не начала получать переписку без вашего ведома.
Список методов
| Метод | Назначение |
|---|---|
messages.send.template | Отправить одно сообщение по шаблону |
messages.status.get | Статусы доставки по идентификаторам сообщений или по номеру |
messages.history.list | История сообщений линии за период с фильтрами и пагинацией |
messages.stats.get | Счётчики за период: отправлено, доставлено, прочитано, ошибки, ответы |
templates.list | Шаблоны линии, одобренные провайдером |
templates.analytics.get | Аналитика провайдера по шаблонам рядом с нашими счётчиками |
campaigns.create | Создать черновик рассылки |
campaigns.recipients.add | Загрузить получателей в черновик |
campaigns.start | Запустить рассылку |
campaigns.cancel | Остановить выполняющуюся рассылку |
campaigns.list | Список рассылок портала |
campaigns.stats.get | Воронка по рассылке |
campaigns.recipients.list | Построчный отчёт по получателям рассылки |
line.status.get | Состояние одной линии |
portal.status.get | Состояние всех линий портала |
Пустой метод (обращение к базовому адресу) отдаёт этот же список в машинном виде — с обязательными и необязательными параметрами каждого метода и действующими лимитами.
Получение токена
Токен выдаётся в приложении Олчат в Битрикс24: «Настройки приложения» — вкладка «REST API» — кнопка «Выдать / перевыпустить токен». Рядом показывается базовый адрес API.
Выдать, посмотреть или отозвать токен может только администратор портала Битрикс24: право проверяется в самом Битрикс24 в момент запроса. У остальных сотрудников вкладка показывает надпись «Токен доступен администратору портала».
Токен принадлежит порталу целиком, а не отдельной линии: одним токеном вы работаете со всеми подключёнными номерами портала, указывая нужный в параметре line_number.
Повторное нажатие «Выдать / перевыпустить токен» выдаёт новое значение и одновременно обесценивает прежнее — все интеграции на старом токене сразу начнут получать ошибку 401. Кнопка «Отозвать» отключает доступ полностью.
Формат запросов
Базовый адрес:
https://waba.olchat.io/rest/client/v2/<метод>/Токен передаётся заголовком OLChat-Api-Token. В адресе и в query-строке токен не принимается.
Поддерживаются GET и POST. Простые параметры можно передавать query-строкой или формой, составные (списки получателей, параметры шаблона) — только JSON-телом с Content-Type: application/json.
Пример:
curl -X POST 'https://waba.olchat.io/rest/client/v2/messages.stats.get/' \
-H 'OLChat-Api-Token: ВАШ_ТОКЕН' \
-H 'Content-Type: application/json' \
-d '{"line_number": 12, "date_from": "2026-07-01", "date_to": "2026-07-31"}'Даты принимаются в формате ISO-8601 (2026-07-01 или 2026-07-01T10:00:00+03:00) либо числом секунд epoch. Все даты в ответах — ISO-8601.
Список доступных методов и действующие лимиты отдаёт сам сервис по базовому адресу без указания метода.
Ограничения
| Ограничение | Значение |
|---|---|
| Частота запросов | 5 запросов за 3 секунды на пару «токен + метод» |
| Глубина статистики | 92 дня |
| Размер страницы | до 500 записей, по умолчанию 100 |
| Получателей в одном запросе | до 1000 |
| Аналитика провайдера | до 90 дней, обновляется раз в сутки |
| Одновременно выполняющихся рассылок | до 5 на портал |
| Окно ожидания ответа | от 1 до 720 часов |
Лимит частоты считается отдельно для каждого метода: обращение к messages.stats.get не расходует лимит campaigns.start. Ответ 429 означает, что запрос не выполнен — его нужно повторить, а не считать неудачей операции.
Отправка сообщений
Одно сообщение по шаблону
messages.send.template отправляет одно сообщение по одобренному шаблону. Заводить ради одного получателя рассылку не нужно.
| Параметр | Тип | Описание |
|---|---|---|
| line_number* | int | Номер линии |
| phone* | str | Номер абонента в любой записи |
| template_name* | str | Имя шаблона |
| template_language | str | Язык шаблона, если одно имя используется в нескольких языках |
| template_params | list | Значения подстановок в порядке их следования в шаблоне |
В ответе — блок message в том же виде, что и в истории: идентификатор сообщения, номер, время создания, имя и язык шаблона, значения подстановок и блок статусов. Идентификатор из ответа используйте дальше в messages.status.get.
Число переданных подстановок должно совпадать с числом подстановок в шаблоне, иначе метод ответит ошибкой template_params_mismatch и сообщение отправлено не будет. Отправка на линию с просроченной оплатой или без подключения отклоняется с line_not_active.
Отчётность
История сообщений
messages.history.list — сообщения линии за период с пагинацией и фильтрами.
| Параметр | Тип | Описание |
|---|---|---|
| line_number* | int | Номер линии |
| date_from* | date | Начало периода |
| date_to* | date | Конец периода |
| direction | str | incoming, outgoing или all |
| phone | str | Номер абонента |
| template_name | str | Имя шаблона |
| sent, delivered, read | bool | Фильтр по статусу |
| errors_only | bool | Только сообщения с ошибкой |
| page, page_size | int | Страница и её размер |
| include_text | bool | Добавить текст сообщения и описание вложения |
Параметр include_text: true добавляет к каждому сообщению его текст. Работает и для входящих, и для исходящих. У сообщения с вложением приходит блок attachment с типом вложения и именем файла; само содержимое файла и ссылка на скачивание через API не передаются. Без этого параметра поля text и attachment в ответе отсутствуют.
В ответе — total, page, page_size и массив messages. Каждое сообщение содержит message_id, line_number, direction, phone, created_at, имя, язык и значения параметров шаблона, блок status со значениями sent/delivered/read и временем каждого, status_source, текст ошибки, признак тарификации и модель тарификации.
Статус конкретных сообщений
messages.status.get — состояние доставки по идентификаторам либо по паре «номер абонента + период».
| Параметр | Тип | Описание |
|---|---|---|
| line_number* | int | Номер линии |
| message_ids | str | Идентификаторы через запятую |
| phone | str | Номер абонента, если идентификаторы не известны |
| date_from, date_to | date | Период, обязателен при запросе по номеру |
| include_text | bool | Добавить текст сообщения и описание вложения |
Идентификатор, не принадлежащий линиям вашего портала, просто отсутствует в ответе — запрос при этом успешен. За один раз принимается не более 500 идентификаторов: на большем количестве метод отвечает ошибкой too_many_message_ids, а не отдаёт часть молча.
Агрегированная статистика
messages.stats.get — счётчики за период: отправлено, доставлено, прочитано, ошибок, ответов.
Обратите внимание на базы счётчиков: sent, delivered, read, errors и total считаются по всем исходящим сообщениям линии — операторским, роботным и рассылочным. replied считается только по получателям рассылок: ответы отслеживаются в окне ответа кампании. Чтобы доля ответов была осмысленной, рядом отдаётся campaign_sent — база именно для replied. Оба поля есть при group_by=total.
| Параметр | Тип | Описание |
|---|---|---|
| line_number* | int | Номер линии |
| date_from*, date_to* | date | Период, не длиннее 92 дней |
| group_by | str | total (по умолчанию), day или template |
Счётчик replied считается по получателям рассылок: сообщения вне рассылок в него не попадают.
Состояние линий
line.status.get (параметр line_number) и portal.status.get (без параметров) отдают номер телефона, статус подключения, признак активности, признак демонстрационного режима и дату оплаты. Ключи доступа к провайдеру в ответах не передаются.
Список шаблонов
templates.list (параметр line_number) — доступные шаблоны линии: имя, идентификатор, язык, статус согласования, категория и число параметров.
Список берётся из того же источника, против которого проверяется отправка, поэтому шаблон из этого метода гарантированно пригоден для рассылки. Если линия не подключена к провайдеру, метод отвечает line_not_connected.
Рассылки
Рассылка проходит четыре шага: создание черновика, загрузка получателей, запуск, наблюдение за воронкой.
Создание рассылки
campaigns.create
| Параметр | Тип | Описание |
|---|---|---|
| line_number* | int | Номер линии |
| template_name* | str | Имя шаблона |
| name | str | Название рассылки |
| template_language | str | Язык шаблона, по умолчанию ru |
| template_params | list | Значения параметров шаблона по умолчанию |
| reply_window_hours | int | Окно ожидания ответа, по умолчанию 72 часа |
Число значений в template_params проверяется по составу шаблона: несовпадение отклоняется с ошибкой template_params_mismatch.
Загрузка получателей
campaigns.recipients.add — до 1000 получателей за запрос, черновик можно наполнять несколькими запросами.
| Параметр | Тип | Описание |
|---|---|---|
| campaign_id* | int | Идентификатор рассылки |
| recipients* | list | Список получателей |
Персональные параметры получателя передаются списком в том же порядке, что и подстановки шаблона. Значение другого типа (например объект \{"1": "Иван"\}) отклоняется с причиной invalid_params: раньше такой получатель принимался и получал текст с параметрами кампании вместо своих.
Получатель — либо номер строкой, либо объект:
{
"campaign_id": 17,
"recipients": [
"79001234567",
{"phone": "79007654321", "external_id": "CRM-4821", "params": ["Иван", "12 августа"]}
]
}Персональные значения params замещают значения рассылки по умолчанию. Поле external_id возвращается обратно в отчётах — по нему удобно сопоставлять получателей со своими записями.
Ответ содержит число принятых и список отклонённых с причиной: invalid_phone (номер не похож на телефон) или duplicate (номер уже есть в этой рассылке). Повторная загрузка того же номера дубля не создаёт, поэтому запрос можно безопасно повторить после обрыва связи.
Запуск и отмена
campaigns.start (параметр campaign_id) переводит рассылку в работу. Перед запуском проверяются подключение и оплата линии; повторный запуск отвечает 409.
Отправка идёт порциями в фоне, поэтому метод возвращает управление сразу, не дожидаясь конца рассылки.
campaigns.cancel (параметры campaign_id, reason) останавливает рассылку: неотправленные получатели переводятся в skipped, следующая порция не запускается. Уже отправленные сообщения отменить нельзя — они у абонентов.
В ответе кроме skipped приходит in_flight — число получателей, по которым обращение к провайдеру было разрешено до того, как отмена дошла. Только по ним сообщение ещё может уйти: отозвать отправленный по сети запрос нельзя. Получатели, просто взятые обработчиком в работу, но не дошедшие до отправки, в это число не входят — они переводятся в skipped без обращения к провайдеру. Если in_flight больше нуля, окончательные статусы этих получателей смотрите в campaigns.recipients.list через минуту-другую.
Наблюдение
campaigns.stats.get (параметр campaign_id) отдаёт воронку: total, pending, sent, delivered, read, replied, errors, skipped, no_whatsapp, unknown, а также статус рассылки и время запуска и завершения.
campaigns.recipients.list (параметры campaign_id, status, page, page_size) — построчный отчёт: номер, external_id, статус, ошибка, идентификатор сообщения, время отправки, доставки, прочтения и ответа.
campaigns.list (параметры line_number, status, page, page_size) — список рассылок портала.
Статусы рассылки: draft, running, completed, cancelled.
Статусы получателя: pending (ожидает), processing (в обработке), success (отправлено), error (ошибка), skipped (пропущен при отмене), no_whatsapp (на номере нет WhatsApp), unknown (результат неизвестен).
Статус unknown появляется, если обработчик прервался между отправкой и получением ответа провайдера. Автоматически такой номер повторно не отправляется: сообщение могло уйти и уже быть оплачено, а повтор означал бы второе сообщение абоненту и второй счёт. Решение по таким номерам принимаете вы.
Аналитика шаблонов
templates.analytics.get показывает значения провайдера рядом с нашими за один и тот же период.
| Параметр | Тип | Описание |
|---|---|---|
| line_number* | int | Номер линии |
| date_from*, date_to* | date | Период |
| template_name | str | Один шаблон вместо всех |
Данные провайдера выгружаются фоновой задачей раз в сутки, поэтому за сегодняшний день их может ещё не быть. Признак has_provider_data и поле fetched_at показывают, есть ли выгрузка и насколько она свежая; наши счётчики отдаются в любом случае.
Фоновая выгрузка забирает у провайдера последние 30 дней, а запросить можно период до 92 дней. Чтобы частичное покрытие не выглядело полным, в ответе есть блок provider_coverage с фактическими границами дат, за которые выгрузка у нас есть. Наши счётчики покрывают весь запрошенный период.
Небольшое расхождение между колонками — норма: провайдер считает по своим суткам и включает переходы по кнопкам, которых в нашей статистике нет.
Коды ошибок
Ошибка возвращается телом \{"error": "<код>"\} с соответствующим HTTP-статусом.
| Код | Статус | Причина |
|---|---|---|
| unauthorized | 401 | Токен отсутствует, неизвестен или отозван |
| rate_limit_exceeded | 429 | Превышена частота запросов |
| unknown_method | 404 | Метод не существует |
| line_not_found | 404 | Линия не найдена или принадлежит другому порталу |
| campaign_not_found | 404 | Рассылка не найдена или принадлежит другому порталу |
| line_number_required | 400 | Не передан номер линии |
| period_required | 400 | Не передан период |
| invalid_period | 400 | Начало периода позже его конца |
| period_too_long | 400 | Период длиннее 92 дней |
| invalid_datetime | 400 | Дата не разобрана |
| invalid_direction | 400 | Недопустимое значение direction |
| invalid_group_by | 400 | Недопустимое значение group_by |
| invalid_pagination | 400 | Недопустимые page или page_size |
| message_ids_or_phone_required | 400 | Не передан ни идентификатор, ни номер |
| line_not_connected | 400 | Линия не подключена к провайдеру |
| template_not_found | 400 | Шаблон не найден среди доступных линии |
| template_params_mismatch | 400 | Число значений не совпадает с числом параметров шаблона |
| recipients_required | 400 | Не передан список получателей |
| batch_too_large | 400 | Больше 1000 получателей в одном запросе |
| no_recipients | 400 | Запуск рассылки без получателей |
| line_not_paid | 400 | Линия не оплачена |
| campaign_not_draft | 409 | Получателей можно добавлять только в черновик |
| campaign_already_started | 409 | Рассылка уже запущена |
| campaign_not_running | 409 | Отменить можно только запущенную рассылку |
| too_many_running_campaigns | 409 | Больше пяти одновременно выполняющихся рассылок на портал |
| invalid_reply_window | 400 | Окно ожидания ответа вне диапазона 1–720 часов |
| invalid_params | 400 | Параметры шаблона переданы не списком |
| phone_required | 400 | Не передан номер абонента |
| template_name_required | 400 | Не передано имя шаблона |
| invalid_phone | 400 | Номер не распознан |
| template_params_mismatch | 400 | Число подстановок не совпадает с шаблоном |
| template_not_found | 404 | Шаблон не найден среди одобренных на линии |
| line_not_active | 403 | Линия не подключена или оплата просрочена |
| templates_unavailable | 503 | Список шаблонов у провайдера временно недоступен, повторите позже |
| provider_error | 502 | Провайдер отклонил отправку |
| send_failed | 502 | Сбой связи с провайдером |
| too_many_message_ids | 400 | Больше 500 идентификаторов в одном запросе статусов |
| invalid_datetime | 400 | Дата не разобрана: нужен ISO‑8601 или epoch в секундах |
| internal_error | 500 | Внутренняя ошибка сервиса |
Линия или рассылка другого портала неотличима от несуществующей — это сделано намеренно, чтобы по ответам API нельзя было выяснить чужие настройки.
О чём стоит знать заранее
Время статусов. С 3 августа 2026 года время доставки и прочтения записывается таким, каким его сообщил провайдер, а не временем обработки события на нашей стороне. У каждого сообщения есть поле status_source: provider — время провайдера, internal — время обработки. Сообщения, отправленные до этой даты, остались со значением internal; при сравнении периодов «до» и «после» это стоит учитывать.
Сопоставление ответа с рассылкой. Ответ абонента засчитывается получателю рассылки, если он пришёл после отправки и внутри окна ожидания (по умолчанию 72 часа). Если один и тот же номер участвует в нескольких рассылках с пересекающимися окнами, ответ засчитывается ближайшей по времени отправке — разделить, на какую именно рассылку ответил абонент, по одному входящему сообщению невозможно.
Приостановка рассылки. Отдельного метода паузы нет. Останавливает рассылку только campaigns.cancel, и возобновить отменённую нельзя — оставшихся получателей переносят в новую рассылку.
Фильтр по имени шаблона и старая переписка. Отбор истории и группировка статистики по template_name опираются на структурированные данные сообщения. Сообщения старше 10 июля 2024 года хранятся в прежнем формате и под такой отбор не попадают: за периоды до этой даты запрашивайте историю без фильтра по шаблону.
Срок хранения истории. Сообщения хранятся год. Данные старше года удаляются, поэтому история и статистика доступны за последние 12 месяцев. Если вам нужна более длинная история, выгружайте её к себе регулярно.
Формат номера и дат. Номер в фильтрах принимается в любой записи — +7 999 123-45-67 и 79991234567 дают один результат. Даты принимаются в ISO‑8601 (2026-08-01, 2026-08-01T10:00:00Z), в сжатом виде (20260801) и как epoch в секундах.