Список и получение
`emails.list`, `emails.list_all`, `emails.iterate`, `emails.get` и `emails.list_events`.
emails.list
from openemail import openemail first = openemail.emails.list(status=['queued', 'scheduled'], from_='[email protected]', limit=50) if first['nextCursor']: second = openemail.emails.list( status=['queued', 'scheduled'], from_='[email protected]', limit=50, cursor=first['nextCursor'], )Страница имеет вид {'items': [...], 'hasMore': ..., 'nextCursor': ...}. Передайте nextCursor обратно как cursor, с теми же фильтрами, чтобы получить следующую страницу.
emails.iterate и emails.list_all
import sys from openemail import openemail for email in openemail.emails.iterate(status='failed'): print(email['id'], email['lastError'], file=sys.stderr) failures = openemail.emails.list_all(status='failed', from_='[email protected]')Оба следуют за nextCursor за вас. iterate является генератором, который запрашивает страницу, только когда цикл до неё доходит, так что выход из цикла прекращает запросы, а list_all обходит все страницы, прежде чем вернуть один список, поэтому дайте ему фильтр, у которого есть конец. В обоих случаях пагинация keyset, так что сообщение, пришедшее посреди обхода, не заставит пропустить строку, как это было бы со смещением.
emails.get и emails.list_events
from openemail import openemail email = openemail.emails.get('msg_…')print(email['status'], email['recipients']) events = openemail.emails.list_all_events('msg_…')for event in events: print(event['type'], event['createdAt'])get является единственным вызовом, возвращающим recipients, по строке на адрес. Список из пятидесяти сообщений, каждое со своими получателями, был бы страницей отчёта, о которой никто не просил.
Параметры
statusEmailStatus | Sequence[EmailStatus]- Один статус или несколько (`queued`, `scheduled`, `sending`, `sent`, `partial`, `bounced`, `cancelled`, `failed`), совпадение по любому из перечисленных. SDK отправляет список одним значением через запятую, потому что сервер разбивает по запятым; значение вне этого набора даёт 422 с указанием неизвестного.
broadcast_idstr- Только копии одной рассылки, id `brd_` из `broadcasts.send`. Каждый, кого достигает рассылка, получает своё письмо, поэтому здесь видно, кому она ушла и что стало с каждой копией. `broadcasts.list_recipients` выводит тех же людей с их открытиями, кликами и отписками.
from_str- Точное совпадение по адресу отправки в том виде, в каком он записан, то есть голый `addr@host` в нижнем регистре. Строка пишется с отброшенным отображаемым именем, так что угловая форма вроде `Acme <[email protected]>` не совпадёт ни с чем. Ваше значение приводится к нижнему регистру перед сравнением, и это именно равенство, а не совпадение по префиксу или домену. Завершающее подчёркивание нужно потому, что `from` является ключевым словом Python.
limitint- Строк на странице, от 1 до 100, по умолчанию 25. Значение вне диапазона отклоняется как 422, а не обрезается до границы.
cursorstr- Идентификатор сообщения (`msg_…`), от которого листать. Keyset, а не смещение: строки приходят строго старше, чем `createdAt` того сообщения, так что отправки, случившиеся посреди страницы, не протолкнут строку мимо вас. Идентификатор, не называющий сообщения в этом рабочем пространстве, даёт 400.
scheduled_fromdatetime | str- Только сообщения, запланированные на этот момент или позже: `datetime` или момент в ISO-8601 с часовым поясом. Сообщение без `scheduledAt` исключается, так что вместе с `scheduled_to` и `status=['queued', 'scheduled']` это выводит то, что ждёт отправки в заданном окне.
scheduled_todatetime | str- Только сообщения, запланированные на этот момент или раньше. `scheduled_from`, который позже этого значения, даёт 422 `invalid_parameter` на `scheduledTo`.
Ответ: Page[EmailResource]
itemslist[EmailResource]- Одна страница сообщений, новые сверху по `createdAt`, извлечённая из конверта `data` у API. Строки списка никогда не несут разбивки `recipients` по адресам. Она есть в `get`.
hasMorebool- Есть ли за этой страницей ещё строки, подходящие под фильтр. Отвечается выборкой на одну строку больше, чем `limit`, а не вторым запросом с подсчётом.
nextCursorstr | None- Идентификатор, который надо передать обратно как `cursor`, и null на последней странице. `iterate` и `list_all` останавливаются, когда он null или `hasMore` равно false, поскольку страница, заявляющая о продолжении, но не называющая курсора, зациклилась бы навсегда.
items[].objectLiteral['email']- Всегда `'email'` в строке этого списка.
items[].idstr- Собственный идентификатор этого API, `msg_…`. Именно его принимает любой другой эндпоинт emails и именно его называет курсор.
items[].statusEmailStatus- Где сообщение находится в своей жизни. `partial` является самостоятельным состоянием, а не разновидностью failed: у части получателей оно уже есть и его нельзя «разотправить», так что повтор будет ошибкой. `bounced` значит, что после отправки письмо вернулось от каждого получателя, так что его нет ни у кого, а каждый получатель в `get` указывает причину.
items[].modeApiKeyMode- `live` или `test`, берётся из ключа, которым отправлено. Тестовая отправка записывается здесь и никогда не передаётся.
items[].fromstr- Адрес, под которым была авторизована отправка, хранимый голым и в нижнем регистре, так что отображаемое имя, переданное в `from`, всё равно уходит в сеть, но здесь не сохраняется. Обычная строка, а не словарь, потому что это авторизованная идентичность: адрес вне области отправки ключа, не принадлежащий ни одному его домену и не названный в нём, отклоняется с 403, а не подменяется тихо тем, который ключу разрешён.
items[].subjectstr | None- Тема как сохранена. Null у сообщения, записанного без неё.
items[].messageIdstr | None- Message-ID по RFC 5322, а не наш идентификатор. Null, пока не существует MIME, и переписывается сервисом отправки на выходе, так что позднейший отбой или DSN несёт другой идентификатор и соотносится по `items[].id`.
items[].threadIdstr | None- Цепочка, которой принадлежит сообщение, если она была задана или назначена. Иначе null.
items[].transportEmailTransport | str | None- Как ушли байты. Null до отправки, и тип открытый, чтобы транспорт, которого этот SDK ещё не называет, не был ломающим изменением: сохранённые записи могут называть и те, что уже не используются.
items[].attemptsint- Сколько попыток отправки было у сообщения, 0 до первой.
items[].lastErrorstr | None- Самая свежая ошибка отправки, написанная для человека. Null, пока ничего не падало.
items[].scheduledAtstr | None- Когда сообщение должно уйти, как момент ISO-8601. Null только у немедленной отправки без окна отмены: окно является всего лишь короткой задержкой, так что `cancellableForSeconds` тоже заполняет это поле, у строки, чей `status` равен `queued`, а не `scheduled`.
items[].cancellableUntilstr | None- Момент, когда сообщение должно уйти, несущий то же значение, что и `scheduledAt`, у любой отложенной отправки и null у неотложенной. Это отметка времени для показа, а не проверка, которую делает сервер: `cancel` ветвится по `status` и останавливает сообщение, только пока оно `queued` или `scheduled`.
items[].sentAtstr | None- Когда оно ушло. Null, пока отправка не завершена, поэтому ветвиться нужно по `status`, а не по этому полю.
items[].tagsdict[str, str]- Метки, переданные при отправке, возвращаются как есть и никак не интерпретируются. Всегда словарь (`{}`, если их не задали, никогда не null), и только возвращаются: этот вызов фильтрует по `status`, `from_`, `broadcast_id`, `scheduled_from` и `scheduled_to`, поэтому тег можно прочитать у письма, но нельзя найти по нему письмо.
items[].broadcastIdstr | None- Рассылка `brd_`, копией которой является это письмо, или null для письма, отправленного отдельно.
items[].sourceEmailSource | str- Какая поверхность запросила отправку: `composer`, `api`, `mcp`, `ai`, `oauth` или `form`. `api` означает этот клиент с API-ключом, а `oauth` означает этот клиент с токеном доступа.
items[].createdAtstr- Когда была записана запись об отправке, то есть до самой отправки. Именно по этому полю упорядочен список и именно с ним сравнивается курсор.
items[].trackingNotRequired[EmailTrackingSummary]- Сводка по вовлечённости, присутствует только в строке, чьё сообщение отслеживалось, и отсутствует в остальных. Именно отсутствие отвечает на вопрос «отслеживалось ли это», тогда как `openCount: 0` читался бы как «никто не открыл».
items[].tracking.opensbool- Ушло ли это сообщение с пикселем. Это то, что было применено к данному сообщению, а не то, что говорит настройка аккаунта сейчас.
items[].tracking.clicksbool- Были ли переписаны ссылки этого сообщения. False, когда в теле не было ссылок для переписывания, поскольку тогда ничего не менялось.
items[].tracking.openedbool- Было ли зафиксировано хоть одно засчитанное открытие, выводится из `openCount > 0`.
items[].tracking.clickedbool- Был ли зафиксирован хоть один засчитанный клик, выводится из `clickCount > 0`.
items[].tracking.openCountint- Открытия, которые считаются вызванными человеком, просуммированные по всем копиям сообщения. Сканеры и прокси приватности фиксируются, но исключаются, а повторные загрузки в течение тридцати секунд схлопываются в одну.
items[].tracking.clickCountint- Засчитанные клики, просуммированные по копиям. Дедуплицируются по ссылке, а не по сообщению, потому что переход по двум ссылкам с разницей в секунды считается двумя действиями, а не повтором.
items[].tracking.firstOpenAtstr | None- Самое раннее засчитанное открытие среди копий, и null, пока его нет. Машинные обращения его не сдвигают.
items[].translationNotRequired[EmailTranslationResource]- Никогда не присутствует в строке списка: запись о переводе живёт в сохранённом запросе, который список намеренно не подтягивает. Её отсутствие здесь ничего не говорит о том, переводилось ли сообщение. Спросите `get`.