Перейти к документации
PHP

Рассылки

`broadcasts->preview`, `send`, `list`, `listAll`, `iterate`, `get`, `listRecipients`, `listAllRecipients`, `iterateRecipients`, `getRecipient`, `stats`, `analytics` и `cancel`.

Все методы

broadcasts.php
use OpenEmail\Constants\BroadcastRecipientFilters;use OpenEmail\Constants\BroadcastStatuses; $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);echo $reach['recipients'], ' ', $reach['unsubscribed'], ' ', $reach['suppressed'], PHP_EOL; $broadcast = $client->broadcasts->send($draft); $latest = $client->broadcasts->get($broadcast['id']); while (in_array($latest['status'], [BroadcastStatuses::SCHEDULED, BroadcastStatuses::QUEUED, BroadcastStatuses::SENDING], true)) {    sleep(5);    $latest = $client->broadcasts->get($broadcast['id']);} foreach ($client->broadcasts->iterateRecipients($broadcast['id']) as $copy) {    echo $copy['email'], ' ', $copy['status'], ' ', $copy['opens'], ' ', $copy['clicks'], PHP_EOL;} $bounced = $client->broadcasts->listRecipients($broadcast['id'], filter: BroadcastRecipientFilters::BOUNCED); foreach ($bounced as $row) {    echo $row['emailId'], ' ', $row['email'], PHP_EOL;} $copy = $client->broadcasts->getRecipient($broadcast['id'], 'msg_01dad25067bc4dac966d515d');echo $copy['subject'], ' ', $copy['bouncedAt'] ?? 'not bounced', PHP_EOL; $stats = $client->broadcasts->stats($broadcast['id'], grain: 'day');echo $stats['totals']['opened'], ' ', $stats['totals']['clicked'], ' ', $stats['totals']['unsubscribed'], PHP_EOL; $lately = $client->broadcasts->stats($broadcast['id'], days: 1);echo $lately['window']['opened'] ?? 0, PHP_EOL; $later = $client->broadcasts->send([...$draft, 'scheduledAt' => 'P1D']);$client->broadcasts->cancel($later['id']); $history = $client->broadcasts->list(audienceId: $draft['audienceIds'][0]);echo $latest['status'], ' ', $latest['counts']['sent'], ' ', count($history), PHP_EOL; $month = $client->broadcasts->analytics(days: 30); foreach ($month['broadcasts'] as $row) {    echo $row['subject'], ' ', $row['sent'], ' ', $row['opened'], PHP_EOL;}

Рассылка отправляет одно сообщение всем в одной или нескольких аудиториях, отдельной копией каждому человеку. У каждой копии ровно один получатель и нет cc или bcc, поэтому никто не видит, кому ещё она ушла, и каждая копия является обычным письмом со своим id msg_, событиями, отслеживанием и вебхуками. listRecipients выводит их вместе с тем, что стало с каждой. Копии не складываются в папку «Отправленные», потому что записью служит сама рассылка.

send сразу возвращает рассылку в состоянии queued или scheduled, если тело содержит scheduledAt, а отправка идёт в фоне. send нужны emails:send и audiences:read, а preview нужна audiences:read. list, listAll, iterate, get, listRecipients, listAllRecipients, iterateRecipients, getRecipient, stats и analytics нужна emails:read, а cancel нужна emails:send.

Каждый send несёт Idempotency-Key, ваш через idempotencyKey: или созданный клиентом, поэтому повтор после сетевого сбоя отвечает рассылкой, созданной первой попыткой, с replayed, равным true, а не отправляет дважды. preview, get, cancel и все чтения безопасно повторять, и они повторяются.

schedule_broadcast.php
$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' => new \DateTimeImmutable('+1 hour'),], idempotencyKey: 'doors-open-2026-10'); echo $broadcast['id'], ' ', $broadcast['status'], ' ', $broadcast['replayed'] ? 'replayed' : 'new', PHP_EOL;

Поля рассылки являются ключами одного массива с именами API в camelCase (audienceIds, scheduledAt). idempotencyKey: и apiKey: являются именованными аргументами вызова и никогда не отправляются как поля. Если распаковать черновик в новый массив рядом с полем, отправится тот же черновик с одним этим изменением, поэтому send([...$draft, 'scheduledAt' => 'P1D']) отправляет его на день позже. scheduledAt принимает DateTimeInterface, строку ISO 8601 или длительность вроде PT2H, а DateTimeInterface уходит как момент в UTC. preview отправляет из переданного только audienceIds, поэтому принимает тот же массив, что и send. Ответ является массивом с ключами в camelCase, поэтому $broadcast['status'] читает статус.

Поля слияния

subject, html и text заполняются для каждого человека из его контакта. {{firstName}} обозначает первое слово имени контакта, {{lastName}} остальную часть имени, {{name}} полное имя, {{email}} адрес, куда уходит копия, а {{unsubscribeUrl}} ссылку, которая его отписывает.

Каждое поле принимает запасное значение после черты, которое используется, если у контакта нет значения, поэтому {{firstName|there}} превращается в "there" для контакта без имени. Значения экранируются в html, а любое другое {{…}} остаётся ровно таким, как написано.

Передайте template вместо html и text, чтобы отправить сохранённый шаблон, в виде массива с id и необязательными version, props и slots. Те же пять значений доходят до него как props, но только те, которые шаблон объявляет, поэтому шаблон, объявивший firstName, его получит, а не объявивший никогда не будет из-за него отклонён. Всё, что есть в его props, одинаково уходит в каждую копию.

Отписка

Каждая копия несёт заголовки отписки в один щелчок, благодаря которым почтовый клиент показывает собственную кнопку отписки, чего крупные почтовые сервисы требуют от массовых рассылок. Текст html или text, который сам не ставит {{unsubscribeUrl}}, получает однострочный колонтитул со ссылкой. Шаблон отправляется ровно таким, как есть, поэтому поставьте {{unsubscribeUrl}} в шаблон.

Отписка помечает человека отписавшимся во всех аудиториях, которым ушла эта рассылка, и audiences->listContacts показывает это в unsubscribedAt его строки, как описано на странице об аудиториях. Он остаётся в аудитории и в адресной книге, другие его аудитории не затрагиваются, а письма, отправляемые ему по одному, по-прежнему уходят. Если убрать его из аудитории и добавить снова, он снова становится подписанным.

Кого пропускают

Рассылка достигает каждого контакта хотя бы одной из audienceIds, один раз, в скольких бы аудиториях он ни был. Она пропускает контакт, отписавшийся от каждой из этих аудиторий, в которых состоит, и адрес из списка подавления после отказа или жалобы или потому, что его туда добавили. Контакт, добавленный в одну из аудиторий после send, но до того, как рассылка до него дошла, её получит.

preview возвращает те же числа без отправки: recipients, unsubscribed и suppressed. send, который никого бы не достиг, выбрасывает 422 no_recipients как ValidationException.

Вся отправка проверяется по месячному лимиту отправок тарифа до того, как что-либо записано, поэтому рассылка, которую лимит не покрывает, выбрасывает 429 send_quota_exceeded как RateLimitException и ничего после себя не оставляет. Каждая копия считается одной отправкой.

Статус и ход

get читает counts по копиям в реальном времени, поэтому опрашивайте его, пока рассылка отправляется, со sleep() между вызовами, как в примере выше. status переходит из scheduled или queued в sending и останавливается на sent, когда каждая переданная копия ушла или завершилась сбоем. Он остаётся sending, пока копии ещё ждут, даже после того, как completedAt сообщает, что последний человек охвачен. failed означает, что вся рассылка остановилась, а lastError объясняет почему: с адреса from больше нельзя отправлять, шаблон перестал разрешаться, лимит тарифа закончился на полпути, сама отправка раз за разом не удавалась или не удалось записать ни одной копии.

cancel останавливает рассылку в состоянии scheduled, queued или sending. Больше никто не добавляется, и каждая ещё ждущая копия отменяется, а ушедшие копии отозвать нельзя. Когда ушли все копии, cancel выбрасывает 409 broadcast_not_cancellable как ConflictException, а отмена уже отменённой рассылки возвращает её в текущем виде.

Кого она достигла

listRecipients возвращает одну OpenEmail\Result\Page людей, которым ушла рассылка, по строке на копию, отсортированных по адресу, с items, hasMore и nextCursor. listAllRecipients собирает все страницы в один массив, а iterateRecipients возвращает Generator, который выдаёт копии по одной и запрашивает следующую страницу, только когда цикл её просит. limit: принимает значения от 1 до 200, по умолчанию 50, а cursor: передаётся обратно с теми же filter: и q:.

`filter:`Оставляет
pendingКопии, которые ещё в очереди, запланированы или отправляются.
sentКопии, которые ушли.
deliveredКопии, принятые сервером получателя.
openedКопии, открытые хотя бы раз.
not_openedКопии, отправленные и ни разу не открытые.
clickedКопии хотя бы с одним отслеженным кликом.
bouncedКопии, получившие отказ доставки.
complainedКопии, которые человек отметил как спам.
failedКопии с ошибкой или отменённые.
unsubscribedЛюди, отписавшиеся после отправки рассылки.

OpenEmail\Constants\BroadcastRecipientFilters перечисляет все фильтры, а q: ищет по адресу и имени без учёта регистра. Открытия и клики не учитывают прокси изображений и сканеры ссылок и остаются равными 0, если рассылка ушла с выключенным трекингом.

getRecipient($id, $emailId) возвращает одну копию: ту же строку плюс subject, html и text ровно в том виде, в каком их получил этот человек, с заполненными полями подстановки и его собственной ссылкой для отписки. Передайте emailId строки вторым аргументом. HTML взят до добавления трекинга открытий и кликов. emailId, который не является копией этой рассылки, выбрасывает 404 recipient_not_found, а неизвестная рассылка выбрасывает 404 broadcast_not_found, оба как NotFoundException.

stats возвращает итоги и ряд. totals считает копии sent, delivered, bounced, complained и failed, с pending для тех, что ещё ждут, и людей, которые opened, clicked и unsubscribed, а opens и clicks являются счётчиками событий. series разрежен и идёт от старых к новым: по одному интервалу на каждый grain: (minute, hour или day, по умолчанию hour), в котором что-то произошло, с границами в часовом поясе, смещённом на offsetMinutes: к востоку от UTC. Для местного пояса передайте intdiv((int) date('Z'), 60). Каждый человек учитывается один раз, в момент, когда с ним это случилось впервые, поэтому сумма совпадает с итогами.

Передайте days: или minutes: в stats, чтобы прочитать ещё и то, что произошло недавно. Тогда window считает, что было доставлено, вернулось с отказом, отмечено как спам, открыто, кликнуто и отписано внутри окна, а series оставляет только его интервалы, тогда как totals по-прежнему охватывает всю рассылку. Без них window равен null.

Ключ, ограниченный определёнными адресами или доменами, видит только рассылки, отправленные с адреса или домена, которые ему выданы. list, listAll и iterate не показывают остальные, а get, методы получателей, stats и cancel выбрасывают для них 404 broadcast_not_found.

Ответ: рассылка

send, get и cancel возвращают по одной рассылке в виде массива с ключами в camelCase, а send добавляет replayed. list возвращает их OpenEmail\Result\Page, сначала новые, listAll возвращает их все одним массивом, а iterate возвращает Generator по ним. preview возвращает массив с audienceIds, recipients, unsubscribed и suppressed. listRecipients возвращает Page строк получателей, getRecipient возвращает одну строку с содержимым, а stats возвращает массив с broadcastId, grain, totals, window и series. analytics возвращает массив с totals, series и по одной строке на рассылку в broadcasts. Время передаётся строками ISO 8601, которые читает new \DateTimeImmutable().

idstring
Постоянный идентификатор: `brd_` и 24 шестнадцатеричных символа.
statusstring
`scheduled`, `queued`, `sending`, `sent`, `cancelled` или `failed`. `OpenEmail\Constants\BroadcastStatuses` перечисляет каждый.
modestring
`live` или `test`, по ключу, который её создал. Копии тестовой рассылки помечаются как отправленные и никому не доставляются.
sourcestring
Откуда она запущена: `api` для ключа, `oauth` для подключённого приложения, `composer` для приложения, `mcp` для ассистента.
audienceIdsarray
Аудитории, которым она ушла, каждая один раз.
fromstring
Адрес, с которого отправляется каждая копия.
subjectstring
Тема в том виде, как написана, вместе с полями слияния. Пусто, если тему задаёт шаблон.
countsarray
`recipients` содержит оценку, сделанную при `send`. `created` считает записанные копии, `skipped` считает людей, пропущенных потому, что к тому моменту их адрес был подавлен, а `failedToQueue` считает людей, чью копию не удалось записать. `queued`, `sending`, `sent`, `failed` и `cancelled` считают копии по состоянию, в котором каждая находится сейчас.
lastErrorstring or null
Почему рассылка завершилась сбоем, или последняя копия, которую не удалось записать, и причина. null, пока ничего не пошло не так.
scheduledAtstring or null
ISO-8601 в UTC: когда должна начаться отправка. null у рассылки, отправленной сразу.
startedAtstring or null
ISO-8601 UTC, когда рассылка достигла первых людей.
completedAtstring or null
ISO-8601 UTC, когда достигнут последний человек. После этого копии ещё могут ждать отправки.
cancelledAtstring or null
ISO-8601 UTC, когда её остановил `cancel`.
createdAtstring
ISO-8601 UTC, когда был вызван `send`. Задаёт порядок в списке.
updatedAtstring
ISO-8601 UTC, обновляется по мере продвижения отправки.

Ответ: строка получателя

Каждая строка listRecipients, listAllRecipients и iterateRecipients в виде массива с ключами в camelCase. Массив, который возвращает getRecipient, добавляет subject, html и text.

emailIdstring
Идентификатор `msg_` копии этого человека. `getRecipient` читает её вместе с содержимым, а `emails->get` читает её как отправленное письмо, как описано на странице «Список и получение».
contactIdstring or null
Контакт, которому она ушла, или null, если контакт с тех пор удалён.
emailstring
Адрес, на который ушла копия.
namestring or null
Имя в контакте.
statusstring
Состояние копии: `queued`, `scheduled`, `sending`, `sent`, `failed` или `cancelled`.
sentAtstring or null
ISO-8601 UTC, когда копия ушла.
deliveredAtstring or null
ISO-8601 UTC, когда сервер получателя её принял, первое `email.delivered`.
bouncedAtstring or null
ISO-8601 UTC, когда она получила отказ доставки, первое `email.bounced`.
complainedAtstring or null
ISO-8601 UTC, когда человек отметил её как спам, первое `email.complained`.
failurestring or null
Почему копия завершилась ошибкой, если это случилось.
opensint
Зафиксированные открытия без тех, что создают прокси изображений и сканеры. 0, если отслеживание было выключено.
firstOpenAtstring or null
ISO-8601 UTC, первое открытие.
clicksint
Клики по отслеживаемым ссылкам, без сканеров.
firstClickAtstring or null
ISO-8601 UTC, первый клик.
unsubscribedAtstring or null
ISO-8601 в UTC: когда этот человек отписался от одной из аудиторий рассылки после её отправки, по её ссылке или иначе.