Список и получение
`emails->list`, `emails->listAll`, `emails->iterate`, `emails->get` и `emails->listEvents`.
emails->list
$filters = ['status' => ['queued', 'scheduled'], 'from' => '[email protected]']; $first = $client->emails->list(...$filters, limit: 50);$second = $first->hasMore ? $client->emails->list(...$filters, limit: 50, cursor: $first->nextCursor) : null; echo count($first), ' ', $second === null ? 0 : count($second), PHP_EOL;Страница является OpenEmail\Result\Page с items, hasMore и nextCursor. Чтобы получить следующую страницу, передайте nextCursor обратно как cursor: с теми же фильтрами. Если распаковывать один массив фильтров в каждый вызов, как это делает ...$filters, фильтры останутся одинаковыми.
emails->iterate и emails->listAll
foreach ($client->emails->iterate(status: 'failed') as $email) { error_log($email['id'] . ' ' . ($email['lastError'] ?? ''));} $failures = $client->emails->listAll(status: 'failed', from: '[email protected]');echo count($failures), PHP_EOL;Оба следуют за nextCursor за вас. iterate возвращает Generator, который запрашивает страницу только тогда, когда обход до неё доходит, поэтому break из foreach останавливает запросы, а listAll обходит все страницы, прежде чем вернуть один массив, поэтому дайте ему фильтр, у которого есть конец. В обоих случаях используется пагинация по ключу (keyset), поэтому сообщение, пришедшее посреди обхода, не может заставить пропустить строку, как это случилось бы со смещением.
emails->get и emails->listEvents
$email = $client->emails->get('msg_3f9a1c07d2b84e6a9c5b1f20');echo $email['status'], PHP_EOL;print_r($email['recipients']); $events = $client->emails->listAllEvents('msg_3f9a1c07d2b84e6a9c5b1f20'); foreach ($events as $event) { echo $event['type'], ' ', $event['createdAt'], PHP_EOL;}get является единственным вызовом, который возвращает recipients, по одному массиву на адрес с собственными status, error и deliveredAt. Список из пятидесяти сообщений, каждое со своими получателями, был бы страницей отчёта, о которой никто не просил.
listEvents читает журнал событий одной отправки, от старых к новым: email.accepted, email.queued, email.sent, email.delivered, email.bounced, email.opened и остальные, каждое с массивом data, форма которого зависит от его type. listAllEvents и iterateEvents обходят весь журнал за вас. Вебхуки доставляют подмножество тех же событий по мере их возникновения, поэтому если вебхук был пропущен, смотрите сюда.
Параметры
statusstring or array- Один статус или несколько (`queued`, `scheduled`, `sending`, `sent`, `partial`, `bounced`, `cancelled`, `failed`), совпадение с любым из заданных. `bounced` означает, что отказ пришёл от всех получателей сообщения, а сообщение, которое у одних вернулось с отказом, а до остальных дошло, имеет статус `partial`. Клиент отправляет массив как одно значение через запятую, потому что сервер разбивает по запятым, а значение вне набора даёт 422 с названием незнакомого значения.
broadcastIdstring- Только копии одной рассылки, идентификатор `brd_` из `broadcasts->send`. Каждый человек, до которого доходит рассылка, получает собственное сообщение, поэтому так видно, кому она ушла и что случилось с каждой копией. `broadcasts->listRecipients` перечисляет тех же людей с их открытиями, кликами и отписками.
fromstring- Точное совпадение с адресом отправителя в том виде, в каком он записан, то есть голым `addr@host` в нижнем регистре. Запись сохраняется без отображаемого имени, поэтому адрес в угловых скобках вроде `Acme <[email protected]>` ни с чем не совпадёт. Ваше значение приводится к нижнему регистру перед сравнением, и это проверка на равенство, а не на префикс или домен.
scheduledFromDateTimeInterface or string- Только сообщения, запланированные на этот момент или позже. Вместе с `scheduledTo:` и `status: ['scheduled', 'queued']` показывает, что ждёт отправки в определённом окне, как это делает календарь приложения. Сообщение без `scheduledAt` не попадает в выдачу. Передавайте `DateTimeInterface`, который отправляется как момент в UTC, или момент ISO 8601 со смещением: строку с датой без времени эти два фильтра отклоняют.
scheduledToDateTimeInterface or string- Только сообщения, запланированные на этот момент или раньше. `scheduledFrom:` позже `scheduledTo:` даёт 422 `invalid_parameter`.
limitint- Число строк на этой странице, от 1 до 100, по умолчанию 25. Значение вне этого диапазона отклоняется с 422, а не подрезается. В `listAll` и `iterate` это размер каждой запрашиваемой страницы.
cursorstring- Идентификатор сообщения (`msg_…`), с которого продолжать выдачу. По ключу, а не по смещению: возвращаются строки строго старше `createdAt` этого сообщения, поэтому отправки, пришедшие посреди страницы, не могут вытолкнуть строку мимо вас. Идентификатор, который не называет ни одного сообщения в этом рабочем пространстве, даёт 400 `invalid_cursor`.
apiKeystring- Запрашивает список с этим ключом вместо ключа клиента.
Ключ, суженный до некоторых адресов, читает только сообщения, отправленные с охваченных им адресов, а страница обрезается после этого фильтра, поэтому каждая страница, кроме последней, всё равно содержит limit строк. from:, который ключ не охватывает, возвращает пустую последнюю страницу, а не 403.
Ответ: OpenEmail\Result\Page
itemsarray- Одна страница сообщений, новые сверху по `createdAt`, извлечённая из конверта `data` у API. Строки списка никогда не несут разбивки `recipients` по адресам. Она есть в `get`.
hasMorebool- Есть ли за этой страницей ещё строки, подходящие под фильтр. Отвечается выборкой на одну строку больше, чем `limit`, а не вторым запросом с подсчётом.
nextCursorstring or null- Идентификатор, который нужно передать обратно как `cursor:`, и null на последней странице. `iterate` и `listAll` останавливаются, когда он null или `hasMore` равно false, поскольку страница, которая заявляет о продолжении, но не называет курсора, зациклилась бы навсегда.
Каждый элемент
objectstring- В строке этого списка всегда `email`.
idstring- Собственный идентификатор этого API, `msg_…`. Именно его принимает любой другой вызов emails и именно его называет курсор.
statusstring- Где сообщение находится в своей жизни. `partial` является самостоятельным состоянием, а не разновидностью failed: у части получателей оно уже есть и его нельзя «разотправить», так что повтор будет ошибкой. `bounced` значит, что после отправки письмо вернулось от каждого получателя, так что его нет ни у кого, а каждый получатель в `get` указывает причину.
modestring- `live` или `test`, берётся из ключа, которым отправлено. Тестовая отправка записывается здесь и никогда не передаётся.
fromstring- Адрес, под которым была авторизована отправка, сохранённый голым и в нижнем регистре, так что отображаемое имя, переданное в `from`, всё равно уходит по сети, но здесь не хранится. Обычная строка, а не массив, потому что это авторизованная идентичность: адрес вне области отправки ключа, не на его домене и не названный в нём, отклоняется с 403 и никогда молча не заменяется на разрешённый.
subjectstring or null- Тема в сохранённом виде. null у сообщения, записанного без темы.
messageIdstring or null- Message-ID по RFC 5322, а не наш идентификатор. null, пока не существует MIME, и переписывается сервисом отправки на выходе, поэтому более поздний отказ или DSN несёт другой идентификатор и сопоставляется по `id`.
threadIdstring or null- Цепочка, к которой относится это сообщение, если она была указана или назначена. Иначе null.
transportstring or null- Каким путём ушли байты. null до отправки. Сохранённые записи могут называть транспорты, которые больше не используются, поэтому считайте незнакомое значение информацией, а не ошибкой.
attemptsint- Сколько попыток отправки было у сообщения, 0 до первой.
lastErrorstring or null- Последняя ошибка отправки, написанная для человека. null, пока ничего не сломалось.
scheduledAtstring or null- Когда сообщение должно уйти, в виде момента ISO 8601. null только при немедленной отправке без окна отмены: окно является короткой задержкой и ничем больше, поэтому `cancellableForSeconds` тоже заполняет это поле, в строке, у которой `status` равен `queued`, а не `scheduled`.
cancellableUntilstring or null- Момент, когда сообщение должно уйти, несущий то же значение, что и `scheduledAt`, у любой отложенной отправки и null у неотложенной. Это отметка времени для показа, а не проверка, которую делает сервер: `cancel` ветвится по `status` и останавливает сообщение, только пока оно `queued` или `scheduled`.
sentAtstring or null- Когда оно ушло. null до завершения отправки, поэтому ветвиться нужно по `status`, а не по этому полю.
tagsarray- Метки, переданные при отправке, возвращаются как есть и никогда не интерпретируются. Всегда массив, пустой, если меток не задали, и никогда не null, и только возвращаются: этот список фильтрует по `status`, `from`, `broadcastId` и окну расписания, поэтому метку можно прочитать у сообщения, но не использовать для поиска.
broadcastIdstring or null- Рассылка `brd_`, копией которой является это письмо, или null для письма, отправленного отдельно.
sourcestring- Какая поверхность запросила отправку: `composer`, `api`, `mcp`, `ai` или `queue`. `api` означает данный клиент.
createdAtstring- Когда была записана запись об отправке, то есть до самой отправки. Именно по этому полю упорядочен список и именно с ним сравнивается курсор.
trackingarray- Сводка вовлечённости. Присутствует только в строке, сообщение которой отслеживалось, и отсутствует в остальных. Отсутствие отвечает на вопрос «отслеживалось ли это», тогда как `openCount`, равный 0, читался бы как «никто не открыл», поэтому читайте его через `?? null`, а не рассчитывайте на наличие ключа.
translationarray- Никогда не присутствует в строке списка: запись о переводе живёт в сохранённом запросе, который список намеренно не подтягивает. Её отсутствие здесь ничего не говорит о том, переводилось ли сообщение. Спросите `get`.
Трекинг элемента
opensbool- Ушло ли это сообщение с пикселем. Это то, что было применено к данному сообщению, а не то, что говорит настройка аккаунта сейчас.
clicksbool- Были ли переписаны ссылки этого сообщения. False, если в теле не было ссылок для переписывания, поскольку тогда ничего не менялось.
openedbool- Было ли записано хотя бы одно учтённое открытие. Выводится из `openCount` больше 0.
clickedbool- Был ли записан хотя бы один учтённый клик. Выводится из `clickCount` больше 0.
openCountint- Открытия, которые считаются вызванными человеком, просуммированные по всем копиям сообщения. Сканеры и прокси приватности фиксируются, но исключаются, а повторные загрузки в течение тридцати секунд схлопываются в одну.
clickCountint- Засчитанные клики, просуммированные по копиям. Дедуплицируются по ссылке, а не по сообщению, потому что переход по двум ссылкам с разницей в секунды считается двумя действиями, а не повтором.
firstOpenAtstring or null- Самое раннее засчитанное открытие среди копий, и null, пока его нет. Машинные обращения его не сдвигают.