Рассылки
`broadcasts.preview`, `send`, `list`, `list_all`, `iterate`, `get`, `list_recipients`, `list_all_recipients`, `iterate_recipients`, `get_recipient`, `stats`, `analytics` и `cancel`.
Все методы
draft = { audienceIds: ["aud_4c1b8e2a7d9f05c36b4e8a71"], from: "Acme <[email protected]>", subject: "{{firstName|Hello}}, the September release is out", html: "<p>Hi {{firstName|there}},</p><p>Here is what changed this month.</p>", text: "Hi {{firstName|there}}, here is what changed this month. Unsubscribe: {{unsubscribeUrl}}", tags: {campaign: "release-2026-09"}} reach = client.broadcasts.preview(draft)puts reach[:recipients], reach[:unsubscribed], reach[:suppressed] broadcast = client.broadcasts.send(draft) latest = client.broadcasts.get(broadcast[:id])while %w[scheduled queued sending].include?(latest[:status]) sleep 5 latest = client.broadcasts.get(broadcast[:id])end client.broadcasts.iterate_recipients(broadcast[:id]) do |copy| puts copy[:email], copy[:status], copy[:opens], copy[:clicks]end bounced = client.broadcasts.list_recipients(broadcast[:id], filter: "bounced")bounced.items.each { |row| puts "#{row[:emailId]} #{row[:email]}" } copy = client.broadcasts.get_recipient(broadcast[:id], "msg_01dad25067bc4dac966d515d")puts copy[:subject], copy[:bouncedAt] stats = client.broadcasts.stats(broadcast[:id], grain: "day")puts stats.dig(:totals, :opened), stats.dig(:totals, :clicked), stats.dig(:totals, :unsubscribed) lately = client.broadcasts.stats(broadcast[:id], days: 1)puts lately.dig(:window, :opened) later = client.broadcasts.send(draft, scheduledAt: "P1D")client.broadcasts.cancel(later[:id]) history = client.broadcasts.list(audience_id: draft[:audienceIds].first)puts latest[:status], latest.dig(:counts, :sent), history.items.size month = client.broadcasts.analytics(days: 30)month[:broadcasts].each do |row| puts row[:subject], row[:sent], row[:opened]endРассылка отправляет одно сообщение всем в одной или нескольких аудиториях, отдельной копией каждому человеку. У каждой копии ровно один получатель и нет cc или bcc, поэтому никто не видит, кому ещё она ушла, а каждая копия является обычным письмом со своим идентификатором msg_, событиями, трекингом и вебхуками. list_recipients перечисляет их вместе с тем, что случилось с каждой. Копии не попадают в папку «Отправленные», потому что записью служит сама рассылка.
send сразу возвращает рассылку в состоянии queued или scheduled, если вы передали scheduledAt:, а отправка идёт в фоне. send нужны emails:send и audiences:read, а preview нужна audiences:read. list, list_all, iterate, get, list_recipients, list_all_recipients, iterate_recipients, get_recipient, stats и analytics нужна emails:read, а cancel нужна emails:send.
Каждый send несёт Idempotency-Key, ваш через idempotency_key: или созданный гемом, поэтому повтор после сетевого сбоя отвечает рассылкой, созданной первой попыткой, с replayed, равным true, а не отправляет дважды. preview, get, cancel и все чтения безопасно повторять, и они повторяются.
broadcast = client.broadcasts.send( audienceIds: ["aud_4c1b8e2a7d9f05c36b4e8a71"], from: "Acme <[email protected]>", subject: "Doors open on Friday", text: "Hi {{firstName|there}}, doors open at nine. Unsubscribe: {{unsubscribeUrl}}", scheduledAt: Time.now + 3600, idempotency_key: "doors-open-2026-10") puts broadcast[:id], broadcast[:status], broadcast[:replayed]Поля рассылки передаются именованными аргументами или одним Hash и сохраняют имена API в camelCase (audienceIds:, scheduledAt:). idempotency_key: и api_key: являются параметрами вызова и никогда не отправляются как поля. Именованные аргументы, переданные рядом с Hash, сливаются с ним, поэтому send(draft, scheduledAt: "P1D") отправляет тот же черновик на день позже. scheduledAt: принимает Time, DateTime, строку ISO 8601 или длительность вроде PT2H, а Time уходит как момент в UTC. preview отправляет из переданного только audienceIds, поэтому принимает тот же Hash, что и send. Ответ является Hash с ключами типа Symbol, поэтому broadcast[:status] читает статус.
Поля слияния
subject, html и text заполняются для каждого человека из его контакта. {{firstName}} обозначает первое слово имени контакта, {{lastName}} остальную часть имени, {{name}} полное имя, {{email}} адрес, куда уходит копия, а {{unsubscribeUrl}} ссылку, которая его отписывает.
Каждое поле принимает запасное значение после черты, которое используется, если у контакта нет значения, поэтому {{firstName|there}} превращается в "there" для контакта без имени. Значения экранируются в html, а любое другое {{…}} остаётся ровно таким, как написано.
Передайте template: вместо html: и text:, чтобы отправить сохранённый шаблон, в виде Hash с id и необязательными version, props и slots. Те же пять значений доходят до него как props, но только те, которые шаблон объявляет, поэтому шаблон, объявивший firstName, его получит, а не объявивший никогда не будет из-за него отклонён. Всё, что есть в его props, одинаково уходит в каждую копию.
Отписка
Каждая копия несёт заголовки отписки в один щелчок, благодаря которым почтовый клиент показывает собственную кнопку отписки, чего крупные почтовые сервисы требуют от массовых рассылок. Текст html или text, который сам не ставит {{unsubscribeUrl}}, получает однострочный колонтитул со ссылкой. Шаблон отправляется ровно таким, как есть, поэтому поставьте {{unsubscribeUrl}} в шаблон.
Отписка помечает человека отписавшимся во всех аудиториях, которым ушла эта рассылка, и audiences.list_contacts показывает это в unsubscribedAt его строки, как описано на странице об аудиториях. Он остаётся в аудитории и в адресной книге, другие его аудитории не затрагиваются, а письма, отправляемые ему по одному, по-прежнему уходят. Если убрать его из аудитории и добавить снова, он снова становится подписанным.
Кого пропускают
Рассылка достигает каждого контакта хотя бы одной из audienceIds, один раз, в скольких бы аудиториях он ни был. Она пропускает контакт, отписавшийся от каждой из этих аудиторий, в которых состоит, и адрес из списка подавления после отказа или жалобы или потому, что его туда добавили. Контакт, добавленный в одну из аудиторий после send, но до того, как рассылка до него дошла, её получит.
preview возвращает те же числа без отправки: recipients, unsubscribed и suppressed. send, который никого бы не достиг, выбрасывает 422 no_recipients как OpenEmail::ValidationError.
Вся отправка проверяется по месячному лимиту отправок тарифа до того, как что-либо записано, поэтому рассылка, которую лимит не покрывает, выбрасывает 429 send_quota_exceeded как OpenEmail::RateLimitError и ничего после себя не оставляет. Каждая копия считается одной отправкой.
Статус и ход
get читает counts по копиям в реальном времени, поэтому опрашивайте его, пока рассылка отправляется, со sleep между вызовами, как в примере выше. status переходит из scheduled или queued в sending и останавливается на sent, когда каждая переданная копия ушла или завершилась сбоем. Он остаётся sending, пока копии ещё ждут, даже после того, как completedAt сообщает, что последний человек охвачен. failed означает, что вся рассылка остановилась, а lastError объясняет почему: с адреса from больше нельзя отправлять, шаблон перестал разрешаться, лимит тарифа закончился на полпути, сама отправка раз за разом не удавалась или не удалось записать ни одной копии.
cancel останавливает рассылку в состоянии scheduled, queued или sending. Больше никто не добавляется, и каждая ещё ждущая копия отменяется, а ушедшие копии отозвать нельзя. Когда ушли все копии, cancel выбрасывает 409 broadcast_not_cancellable как OpenEmail::ConflictError, а отмена уже отменённой рассылки возвращает её в текущем виде.
Кого она достигла
list_recipients возвращает одну OpenEmail::Page людей, которым ушла рассылка, по строке на копию, отсортированных по адресу, с items, has_more? и next_cursor. list_all_recipients собирает все страницы в один Array, а iterate_recipients передаёт копии по одной в блок, запрашивая следующую страницу, только когда цикл её просит. Без блока он возвращает Enumerator. limit: принимает значения от 1 до 200, по умолчанию 50, а cursor: передаётся обратно с теми же filter: и q:.
| `filter:` | Оставляет |
|---|---|
| pending | Копии, которые ещё в очереди, запланированы или отправляются. |
| sent | Копии, которые ушли. |
| delivered | Копии, принятые сервером получателя. |
| opened | Копии, открытые хотя бы раз. |
| not_opened | Копии, отправленные и ни разу не открытые. |
| clicked | Копии хотя бы с одним отслеженным кликом. |
| bounced | Копии, получившие отказ доставки. |
| complained | Копии, которые человек отметил как спам. |
| failed | Копии с ошибкой или отменённые. |
| unsubscribed | Люди, отписавшиеся после отправки рассылки. |
OpenEmail::BROADCAST_RECIPIENT_FILTERS перечисляет все фильтры, а q: ищет по адресу и имени без учёта регистра. Открытия и клики не учитывают прокси изображений и сканеры ссылок и остаются равными 0, если рассылка ушла с выключенным трекингом.
get_recipient(id, email_id) возвращает одну копию: ту же строку плюс subject, html и text ровно в том виде, в каком их получил этот человек, с заполненными полями подстановки и его собственной ссылкой для отписки. Передайте emailId строки как email_id. HTML взят до добавления трекинга открытий и кликов. email_id, который не является копией этой рассылки, выбрасывает 404 recipient_not_found, а неизвестная рассылка выбрасывает 404 broadcast_not_found, оба как OpenEmail::NotFoundError.
stats возвращает итоги и ряд. totals считает копии sent, delivered, bounced, complained и failed, с pending для тех, что ещё ждут, и людей, которые opened, clicked и unsubscribed, а opens и clicks являются счётчиками событий. series разрежен и идёт от старых к новым: по одному интервалу на каждый grain: (minute, hour или day, по умолчанию hour), в котором что-то произошло, с границами в часовом поясе, смещённом на offset_minutes: к востоку от UTC. Для местного пояса передайте Time.now.utc_offset / 60. Каждый человек учитывается один раз, в момент, когда с ним это случилось впервые, поэтому сумма совпадает с итогами.
Передайте days: или minutes: в stats, чтобы прочитать ещё и то, что произошло недавно. Тогда window считает, что было доставлено, вернулось с отказом, отмечено как спам, открыто, кликнуто и отписано внутри окна, а series оставляет только его интервалы, тогда как totals по-прежнему охватывает всю рассылку. Без них window равен nil.
Ключ, ограниченный определёнными адресами или доменами, видит только рассылки, отправленные с адреса или домена, которые ему выданы. list, list_all и iterate не показывают остальные, а get, методы получателей, stats и cancel выбрасывают для них 404 broadcast_not_found.
Ответ: рассылка
send, get и cancel возвращают по одной рассылке в виде Hash с ключами типа Symbol, а send добавляет replayed. list возвращает их OpenEmail::Page, сначала новые, а list_all и iterate обходят все страницы. preview возвращает Hash с audienceIds, recipients, unsubscribed и suppressed. list_recipients возвращает OpenEmail::Page строк получателей, get_recipient возвращает одну строку с содержимым, а stats возвращает Hash с broadcastId, grain, totals, window и series. analytics возвращает Hash с totals, series и по одной строке на рассылку в broadcasts. Время передаётся строками ISO 8601, которые разбирает Time.iso8601.
idString- Постоянный идентификатор: `brd_` и 24 шестнадцатеричных символа.
statusString- `scheduled`, `queued`, `sending`, `sent`, `cancelled` или `failed`. `OpenEmail::BROADCAST_STATUSES` перечисляет каждый.
modeString- `live` или `test`, по ключу, который её создал. Копии тестовой рассылки помечаются как отправленные и никому не доставляются.
sourceString- Откуда она запущена: `api` для ключа, `oauth` для подключённого приложения, `composer` для приложения, `mcp` для ассистента.
audienceIdsArray<String>- Аудитории, которым она ушла, каждая один раз.
fromString- Адрес, с которого отправляется каждая копия.
subjectString- Тема в том виде, как написана, вместе с полями слияния. Пусто, если тему задаёт шаблон.
countsHash- `recipients` содержит оценку, сделанную при `send`. `created` считает записанные копии, `skipped` считает людей, пропущенных потому, что к тому моменту их адрес был подавлен, а `failedToQueue` считает людей, чью копию не удалось записать. `queued`, `sending`, `sent`, `failed` и `cancelled` считают копии по состоянию, в котором каждая находится сейчас.
lastErrorString or nil- Почему рассылка завершилась сбоем, или последняя копия, которую не удалось записать, и причина. nil, пока ничего не пошло не так.
scheduledAtString or nil- ISO-8601 в UTC: когда должна начаться отправка. nil у рассылки, отправленной сразу.
startedAtString or nil- ISO-8601 UTC, когда рассылка достигла первых людей.
completedAtString or nil- ISO-8601 UTC, когда достигнут последний человек. После этого копии ещё могут ждать отправки.
cancelledAtString or nil- ISO-8601 UTC, когда её остановил `cancel`.
createdAtString- ISO-8601 UTC, когда был вызван `send`. Задаёт порядок в списке.
updatedAtString- ISO-8601 UTC, обновляется по мере продвижения отправки.
Ответ: строка получателя
Каждая строка list_recipients, list_all_recipients и iterate_recipients в виде Hash с ключами типа Symbol. Hash, который возвращает get_recipient, добавляет subject, html и text.
emailIdString- Идентификатор `msg_` копии этого человека. `get_recipient` читает её вместе с содержимым, а `emails.get` читает её как отправленное письмо, как описано на странице «Список и получение».
contactIdString or nil- Контакт, которому она ушла, или nil, если контакт с тех пор удалён.
emailString- Адрес, на который ушла копия.
nameString or nil- Имя в контакте.
statusString- Состояние копии: `queued`, `scheduled`, `sending`, `sent`, `failed` или `cancelled`.
sentAtString or nil- ISO-8601 UTC, когда копия ушла.
deliveredAtString or nil- ISO-8601 UTC, когда сервер получателя её принял, первое `email.delivered`.
bouncedAtString or nil- ISO-8601 UTC, когда она получила отказ доставки, первое `email.bounced`.
complainedAtString or nil- ISO-8601 UTC, когда человек отметил её как спам, первое `email.complained`.
failureString or nil- Почему копия завершилась ошибкой, если это случилось.
opensInteger- Зафиксированные открытия без тех, что создают прокси изображений и сканеры. 0, если отслеживание было выключено.
firstOpenAtString or nil- ISO-8601 UTC, первое открытие.
clicksInteger- Клики по отслеживаемым ссылкам, без сканеров.
firstClickAtString or nil- ISO-8601 UTC, первый клик.
unsubscribedAtString or nil- ISO-8601 в UTC: когда этот человек отписался от одной из аудиторий рассылки после её отправки, по её ссылке или иначе.