Python
Отправка пакета
`emails.send_batch`: до 100 сообщений, результат по каждому.
emails.send_batch
import sys from openemail import openemailfrom openemail.types import EmailSend invoices = {'[email protected]': 'INV-4021', '[email protected]': 'INV-4022'} messages: list[EmailSend] = [ {'from': '[email protected]', 'to': to, 'subject': f'Invoice {number}', 'text': 'Attached.'} for to, number in invoices.items()] result = openemail.emails.send_batch(messages) print(result['sent'], 'sent,', result['failed'], 'failed') for item in result['items']: if item['status'] == 'error': print(item['index'], item['error']['code'], item['error']['message'], file=sys.stderr) else: print(item['index'], item['email']['id'])items содержит по одной записи на каждый вход, по порядку, каждая либо ok со своим сообщением, либо error с конвертом, которым это сообщение было бы отклонено. Ничего не откатывается, так что failed > 0 даёт список, с которым надо работать, а не повод переслать пакет заново.
Один ключ идемпотентности покрывает пакет, а сервер расширяет его по каждому элементу, так что повторённый пакет воспроизводит каждое сообщение, а не схлопывает их в первое.
Параметры: emails.send_batch
emailsSequence[EmailSend]обязательно- От одного до 100 сообщений, сериализуемых как `{ "emails": [...] }` и принимаемых по одному в заданном порядке. Пустой список, больше 100 либо больше 10 элементов с `translate` отклоняют весь вызов с `validation_error` на `emails`. То же делают отсутствующая область доступа `emails:send` и некорректный `idempotency_key`, и всё это до отправки хотя бы одного сообщения.
idempotency_keystr- Дедуплицирует пакет между процессами. Клиент в любом случае прикрепляет свежесозданный ключ при каждом вызове, так что его собственные повторы никогда не отправляют дважды, а сервер расширяет полученный ключ по каждому элементу как `key/0`, `key/1` и так далее (через слеш, символ, которого не может быть в вашем собственном ключе), так что один ключ на сотню сообщений не может схлопнуть их в первое.
emails[].fromRecipientInputобязательно- Отправитель: голый адрес, `Name <addr@host>` или словарь. Отправителя по умолчанию нет, и ключу должен быть разрешён этот адрес; отказ проваливает только этот элемент, как `permission_error` с кодом `from_address_forbidden`.
emails[].toRecipientInput | list[RecipientInput]обязательно- Хотя бы один получатель, а одиночного клиент сам оборачивает в список. Не более 50 адресов суммарно по `to`, `cc` и `bcc`, считая на сообщение, а не на весь пакет.
emails[].ccRecipientInput | list[RecipientInput]- По умолчанию отсутствует и засчитывается в те же 50 адресов, что `to` и `bcc`.
emails[].bccRecipientInput | list[RecipientInput]- По умолчанию отсутствует и засчитывается в те же 50 адресов. `Bcc` входит в число имён, которые `headers` задавать не может, так что это единственный способ отправить скрытую копию. Форма заголовка свела бы на нет отдельный конверт для каждого получателя, который и делает адрес скрытым.
emails[].replyToRecipientInput- Куда идут ответы. Применяется после `headers`, так что он перезаписывает `Reply-To`, который вы задали и там, а не добавляет второй.
emails[].subjectstr- Не длиннее 998 символов (предел строки по RFC 5322), по умолчанию пустая строка. Пустая тема переходит к собственной теме шаблона, если `template` её даёт.
emails[].htmlstr- Часть HTML, не длиннее миллиона символов, и именно её видят получатели, когда заданы оба тела. Требуется одно из `html`, `text`, `template` или `draftId`, и элемент без них проваливается как `validation_error` на `html`.
emails[].textstr- Текстовая часть, не длиннее миллиона символов. Можно передать обе, но каждый транспорт на этом пути собирает одно тело из одной строки, так что при наличии `html` побеждает он.
emails[].headersdict[str, str]- Только `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority и Feedback-ID; всё, что транспорт ставит сам (From, To, Bcc, Subject, Message-ID, заголовки DKIM и ARC), отклоняется как `reserved_header`, а не отбрасывается тихо. Значения не длиннее 998 символов и не могут содержать CR, LF или NUL, потому что вторая строка становится вторым заголовком.
emails[].attachmentslist[AttachmentInput]- Не более 20 файлов на сообщение, причём встроенные файлы в сумме не больше 5 МБ после декодирования, считая на сообщение, а не на пакет. `content` передаётся в base64; передайте байты, и клиент закодирует их сам. Запись `{'fileId': ...}` называет файл, уже находящийся в рабочем пространстве, и в лимит встроенных файлов не засчитывается.
emails[].threadIdstr- Ответить в существующую цепочку, не длиннее 256 символов. Транспорт пишет из этого In-Reply-To и References, и именно это помещает ответ в переписку, а не рядом с ней.
emails[].draftIdstr- Отправить содержимое сохранённого черновика под этим конвертом, не длиннее 256 символов. На провод уходят получатели, тема и заголовки, собранные здесь.
emails[].templateEmailSendTemplate- Отрисовать сохранённый шаблон на сервере по идентификатору (`tpl_…`) или slug, где `version` закрепляет ревизию, а `props`/`slots` её заполняют. Разрешается один раз, при приёме элемента, и отклоняется вместе с `html`/`text` и вместе с `draftId`, поскольку каждое из них даёт второй ответ на вопрос, что содержит сообщение.
emails[].scheduledAtdatetime | str- `datetime`, момент в ISO-8601 или длительность вроде `PT1H`; не меньше секунды в будущем и не дальше 365 дней. Элементы планируются независимо, так что один пакет может содержать сотню разных моментов отправки.
emails[].cancellableForSecondsint- Окно отмены в секундах для немедленной отправки, целое от 0 до 900, по умолчанию 0. Любое значение выше 0 отклоняется вместе с `scheduledAt` в том же элементе, поскольку запланированное сообщение и так можно отменить до его ухода.
emails[].trackingTrackingRequest- `opens` и `clicks`, каждое независимо необязательно и каждое переопределяет настройку только для этого сообщения. Опущенный переключатель следует адресу, с которого отправляется сообщение (или catch-all, который его поймал), и он выключен, если этот адрес его не включил.
emails[].tagsdict[str, str]- Не более 10 меток: ключи от 1 до 64 символов из набора `A-Za-z0-9_-` и значения до 256. Возвращаются вместе с сообщением и никогда не интерпретируются: `emails.list` фильтрует по `status`, `from_`, `broadcast_id`, `scheduled_from` и `scheduled_to` и ни по чему больше, так что метку читают у уже имеющегося сообщения, а не используют как способ его найти.
emails[].translateSendTranslateOptions- Отправить этот элемент на другом языке, разрешается в момент приёма, чтобы ушли именно те слова, которые были одобрены. Не более 10 элементов одного пакета могут его нести: каждый тратит несколько вызовов модели, а элементы обрабатываются по порядку, так что более крупный пакет был бы прерван на середине отправки. Сверх этого весь вызов отклоняется как `too_many_items` на `emails`, до того как что-либо отправлено.
Ответ: BatchResultResource
itemslist[BatchItemResource]- По одной записи на каждый вход, в том порядке, в котором вы их отправили. Ничего не откатывается, так что это протокол того, что случилось с каждым сообщением, а не отчёт о транзакции. API отвечает 207 независимо от того, приняты ли все сообщения, часть или ни одного, так что вызов в любом случае возвращает результат, а ветвиться нужно по `status` каждого элемента.
sentint- Сколько элементов было ПРИНЯТО, что не то же самое, сколько ушло. Элемент может быть `ok` и при этом нести `email.status` со значением `failed` или `partial`, потому что, если транспорт отказывает в сообщении после появления строки, это исход доставки, а не отклонённый запрос.
failedint- Сколько записей несут `error`. `failed > 0` даёт список, с которым надо работать, а не повод переслать пакет заново. Принятые сообщения уже ушли.
items[].indexint- Позиция, которую сообщение этой записи занимало в отправленном вами списке. Передаётся и как поле, и как порядок, чтобы код, фильтрующий или сортирующий `items`, всё равно мог сказать, какой вход провалился.
items[].statusLiteral['ok', 'error']- Дискриминант объединения: `ok` несёт `email`, `error` несёт `error`, и ни одна запись не несёт оба.
items[].emailSentEmailResource- Принятое сообщение (только в записи `ok`) в той же форме, что возвращает одиночная отправка. Оно не несёт ключа `tracking`, потому что вовлечённость сообщается позже, и в момент приёма сообщать нечего.
items[].email.replayedbool- True, когда выведенный `Idempotency-Key` совпал с уже существовавшей отправкой, так что нового ничего не отправлено и это исходное сообщение.
items[].errorBatchItemResourceErrorError- Почему именно это сообщение было отклонено (только в записи `error`). Это конверт ошибки API минус `docUrl` и `requestId`: они описывают запрос, а запрос в целом удался.
items[].error.typestr- Категория, по которой клиент может ветвиться: `validation_error`, `permission_error`, `not_found_error`, `conflict_error` и остальные. Набор заморожен и расти не будет, в отличие от `code`.
items[].error.codestr- Конкретный сбой: `from_address_forbidden`, `invalid_email_address`, `too_many_recipients`, `reserved_header`, `message_too_large`, `unknown_parameter`. Набор открытый и пополняемый, так что неизвестный вам код трактуйте как его `type`.
items[].error.messagestr- Одно предложение, написанное для человека, называющее ошибочное значение, если оно есть. Не стабильный идентификатор. Переключайтесь по `code`.
items[].error.paramNotRequired[str]- Поле, которое было отклонено, как путь через точку ВНУТРИ ТОГО сообщения: `to.0`, `from`, `attachments`. Отсутствует, когда сбой не называет поля, и никогда не снабжается позицией в пакете, для которой есть `index`.