Перейти к содержанию
Версия контракта: 0.3.3 — спека

Массовые рассылки

Страница для интегратора: как отправить тысячу сообщений так, чтобы потом можно было объяснить каждое.

Пакет

POST /v1/messages/batch — до 1000 сообщений за раз. Больше — 422 batch_too_large.

{
  "messages": [
    {"to": "+79012223344", "class": "service", "template": "visit-reminder",
     "variables": {"time": "18:30"}, "client_ref": "visit-84213"},
    {"to": "+79012223355", "class": "service", "template": "visit-reminder",
     "variables": {"time": "19:00"}, "client_ref": "visit-84214"}
  ]
}

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

Ответ

Пакет не «уходит целиком или никак»: каждый элемент получает свой результат.

Результат Что означает
accepted принято, дальше живёт как обычное сообщение
duplicate такой client_ref уже был; в ответе — ссылка на созданное ранее сообщение
rejected отказ на приёме: номер, шаблон, переменные, лимит. Причина — обычный объект ошибки

Отказ одного элемента не отменяет остальные. Пакет с тысячей элементов, где десять номеров кривые, примет девятьсот девяносто.

Элемент-одноразовый код, который не удалось поставить в очередь отправки (брокер не подтвердил публикацию), получает rejected с кодом provider_unavailable: код, текст которого мы не храним, иначе закончился бы expired, так и не уйдя. Такой элемент повторяется — тем же пакетом, с тем же Idempotency-Key: принятые вернутся duplicate, отвергнутый примется.

Повтор

Повторите пакет с теми же client_ref — вторых сообщений не будет: элементы вернутся как duplicate со ссылками на созданные. Это главное свойство, ради которого client_ref и обязателен: джоба, упавшая на середине и перезапущенная, не удвоит рассылку.

По этой же причине client_ref должен быть вашим идентификатором записи (visit-84213), а не случайным числом: при повторе он обязан совпасть.

Отслеживание

  • GET /v1/batches/{id} — счётчики: сколько принято, доставлено, не доставлено, отклонено, и закрыт ли пакет;
  • GET /v1/messages?batch=bat_… — сами сообщения, страницами;
  • событие batch.completed — все сообщения пакета достигли терминального статуса. Приходит ровно один раз: повторная отправка пакета его не переиздаёт.

Сколько это будет стоить

POST /v1/messages/estimate считает число частей и цену до отправки — по тому же шаблону и тем же значениям.

Считать стоит всегда, когда текст меняется. Латиница помещается в 160 символов на часть, кириллица — в 70: одно и то же сообщение на русском занимает вдвое больше частей и стоит вдвое дороже. Две ссылки в кириллическом тексте почти гарантированно дают две части.

Если объём важнее вида — транслит или сокращение текста; если важнее вид — считайте заранее и закладывайте в бюджет.

Темп

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

Ключ ограничен по числу запросов в секунду, поэтому подавать пакеты подряд без пауз не нужно: см. Лимиты и квоты.

Laravel: чанкование

Разбивайте на пакеты по несколько сотен и ставьте по джобе на пакет. client_ref — идентификатор вашей записи; тогда повтор джобы после сбоя безопасен, а не создаёт вторую рассылку.

foreach ($recipients->chunk(500) as $chunk) {
    SendSmsBatch::dispatch($chunk->pluck('id')->all());
}

Внутри джобы собирайте элементы из своих записей по идентификаторам и отправляйте одним пакетом. Повтор джобы Horizon'ом — обычное дело; вторых сообщений он не создаст.