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

Отправка пакета

`emails.send_batch`: до 100 сообщений, результат по каждому элементу.

emails.send_batch

send_batch.rb
invoices = [  {number: "INV-1042", email: "[email protected]"},  {number: "INV-1043", email: "[email protected]"}] messages = invoices.map do |invoice|  {from: "[email protected]", to: invoice[:email], subject: "Invoice #{invoice[:number]}", text: "Your invoice is attached."}end result = client.emails.send_batch(messages, idempotency_key: "invoices:2026-09") puts "#{result.sent} sent, #{result.failed} failed" result.items.each do |item|  if item[:status] == "error"    warn "#{item[:index]} #{item.dig(:error, :code)} #{item.dig(:error, :message)}"  else    puts "#{item[:index]} #{item.dig(:email, :id)}"  endend

send_batch принимает Array из Hash сообщений, каждый в точности такой же формы, как тело emails.send, и возвращает OpenEmail::BatchResult. Его items содержат по одному Hash на каждое сообщение, по порядку: либо ok с сообщением, либо error с конвертом, с которым это сообщение было бы отклонено. Ничего не откатывается, поэтому счётчик failed больше 0 является списком для разбора, а не поводом отправить пакет заново.

Один ключ идемпотентности покрывает весь пакет, а сервер расширяет его для каждого элемента, поэтому повторённый пакет воспроизводит каждое сообщение, а не сводит их все к первому. При повторе отправляйте тот же Array в том же порядке: элемент, сменивший место, привязывается к ключу другой позиции и возвращается с ошибкой idempotency_key_reuse.

Отклонённое сообщение не выбрасывает исключение. Исключение выбрасывает только проблема с пакетом в целом: пустой Array, больше 100 сообщений, больше 10 сообщений с translate, сбой ключа или области либо сбой сервера. Сбой сервера посередине происходит после того, как более ранние элементы уже ушли, и клиент повторяет пакет с тем же ключом, что воспроизводит эти элементы, а не отправляет их дважды.

Элементы отправляются один за другим внутри одного запроса, поэтому большой пакет немедленных отправок выполняется заметно дольше, чем один send. Задавайте клиенту timeout: с запасом.

Параметры: emails.send_batch

emailsArray<Hash>обязательно
От 1 до 100 сообщений, которые отправляются как `{"emails": [...]}` и принимаются по одному в указанном порядке. Каждое обрабатывается так же, как в `emails.send`: одиночный получатель оборачивается, Time становится моментом времени, а байты вложений кодируются. Пустой Array, больше 100 сообщений или больше 10 сообщений с `translate` приводят к отклонению всего вызова с `validation_error` для `emails`. Отсутствие области `emails:send` и неправильно сформированный `idempotency_key:` тоже отклоняют весь вызов, ещё до отправки первого сообщения.
idempotency_keyString
Устраняет дубликаты пакета между процессами. Клиент в любом случае добавляет к каждому вызову свежесгенерированный ключ, поэтому его собственные повторы никогда не отправляют дважды, а сервер расширяет полученный ключ для каждого элемента как `key/0`, `key/1` и так далее, через косую черту, которую ваш собственный ключ содержать не может, поэтому один ключ на сотню сообщений не сведёт их все к первому.
api_keyString
Отправляет пакет с этим ключом вместо ключа клиента.

Каждое сообщение в emails

fromString or Hashобязательно
Отправитель в виде голого адреса, `Name <addr@host>` или Hash с `email` и `name`. Запасного отправителя нет, и ключу должен быть разрешён этот адрес. Отказ проваливает только этот элемент, как `permission_error` с кодом `from_address_forbidden`.
toString, Hash or Arrayобязательно
Хотя бы один получатель, а одиночного клиент оборачивает в Array. Не более 50 адресов в сумме по `to`, `cc` и `bcc`, считая на каждое сообщение, а не на весь пакет.
ccString, Hash or Array
По умолчанию отсутствует и засчитывается в те же 50 адресов, что `to` и `bcc`.
bccString, Hash or Array
По умолчанию отсутствует и засчитывается в те же 50 адресов. `Bcc` входит в число имён, которые `headers` задавать не может, так что это единственный способ отправить скрытую копию. Форма заголовка свела бы на нет отдельный конверт для каждого получателя, который и делает адрес скрытым.
replyToString or Hash
Куда идут ответы. Применяется после `headers`, так что он перезаписывает `Reply-To`, который вы задали и там, а не добавляет второй.
subjectString
Не более 998 символов, предел строки по RFC 5322, по умолчанию пустая String. При пустой теме используется собственная тема шаблона, если её предоставляет `template`.
htmlString
Часть HTML, не длиннее миллиона символов, и именно её видят получатели, когда заданы оба тела. Требуется одно из `html`, `text`, `template` или `draftId`, и элемент без них проваливается как `validation_error` на `html`.
textString
Текстовая часть, не более миллиона символов. Можно отправить обе части, но каждый транспорт на этом пути собирает одно тело из одной String, поэтому, если есть `html`, побеждает он.
headersHash
Только `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, потому что вторая строка становится вторым заголовком.
attachmentsArray<Hash>
Не более 20 файлов на сообщение, а встроенные файлы в сумме не более 5 МБ после декодирования, считая на сообщение, а не на пакет. `content` передаётся по сети в base64. Передайте байты как двоичную String, IO или Pathname, и клиент их закодирует. Hash только с `fileId` называет файл, уже находящийся в рабочем пространстве, и не учитывается в пределе для встроенных файлов.
threadIdString
Ответить в существующую цепочку, не длиннее 256 символов. Транспорт пишет из этого In-Reply-To и References, и именно это помещает ответ в переписку, а не рядом с ней.
draftIdString
Отправить содержимое сохранённого черновика под этим конвертом, не более 256 символов. По сети уходят получатели, тема и заголовки, собранные здесь.
templateHash
Отрисовать сохранённый шаблон на сервере по идентификатору (`tpl_…`) или слагу: `version` закрепляет ревизию, а `props` и `slots` заполняют его. Фиксируется один раз, при принятии элемента, и отклоняется вместе с `html` или `text`, а также вместе с `draftId`, поскольку каждый из них является вторым ответом на вопрос о том, что содержит сообщение.
scheduledAtTime, DateTime or String
Time или DateTime, момент ISO 8601 или длительность вроде `PT1H`, не раньше чем через секунду и не позже чем через 365 дней. Date из Ruby означает полночь UTC в этот день. Элементы планируются независимо, поэтому один пакет может содержать сто разных времён отправки.
cancellableForSecondsInteger
Окно отмены в секундах для немедленной отправки, от 0 до 900, по умолчанию 0. Любое значение больше 0 отклоняется вместе с `scheduledAt` в том же элементе, поскольку запланированное сообщение и так можно отменить, пока оно не ушло.
trackingHash
`opens` и `clicks`, оба необязательные, и каждый переопределяет настройку только для этого сообщения. Ключ, который вы не указали, следует настройке адреса, с которого отправляется сообщение (или catch-all, который его поймал), а она выключена, если этот адрес её не включил.
tagsHash
Не более 10 меток: ключи от 1 до 64 символов из набора `A-Za-z0-9_-` и значения до 256. Возвращаются вместе с сообщением и никогда не интерпретируются: `emails.list` фильтрует только по `status:`, `from:`, `broadcast_id:` и окну расписания, так что метку можно прочитать у сообщения, которое у вас уже есть, но не использовать для поиска.
translateHash
Отправить этот элемент на другом языке. Перевод фиксируется в момент принятия, поэтому уходит именно одобренный текст. Его могут нести не более 10 элементов одного пакета: каждый тратит несколько вызовов модели, а элементы выполняются по порядку, поэтому пакет побольше был бы прерван посреди отправки. Сверх этого весь вызов отклоняется как `too_many_items` для `emails` ещё до отправки.

Ответ: OpenEmail::BatchResult

itemsArray<Hash>
По одному Hash на каждое сообщение в том порядке, в каком вы их отправили. Ничего не откатывается, поэтому это запись о том, что случилось с каждым сообщением, а не отчёт о транзакции. API отвечает 207 независимо от того, были ли приняты все сообщения, часть или ни одного, поэтому вызов возвращает результат в любом случае, а ветвиться нужно по `status` каждого элемента.
sentInteger
Сколько элементов было ПРИНЯТО, что не то же самое, сколько ушло. Элемент может быть `ok` и при этом нести `email`, у которого `status` равен `failed` или `partial`, потому что отказ транспорта от сообщения после создания записи является результатом доставки, а не отклонённым запросом.
failedInteger
Сколько элементов несут `error`. Значение больше 0 является списком для разбора, а не поводом отправить пакет заново. Принятые сообщения уже ушли.

Каждый элемент

indexInteger
Позиция сообщения этого элемента в Array, который вы отправили. Передаётся и как ключ, и как порядок, поэтому код, который фильтрует или сортирует `items`, всё равно может сказать, какое сообщение не прошло.
statusString
`ok` или `error`. `ok` несёт `email`, `error` несёт `error`, и ни один элемент не несёт оба.
emailHash
Принятое сообщение, только в элементе `ok`, в той же форме, что возвращает одиночная отправка. Его `replayed` равно true, когда производный `Idempotency-Key` совпал с уже существующей отправкой: тогда ничего нового не отправлено, а это исходное сообщение. Оно не несёт ключа `tracking`, потому что о вовлечённости сообщается позже, а в момент принятия сообщать нечего.
errorHash
Почему было отклонено именно это сообщение, только в элементе `error`. Это конверт ошибки API без `docUrl` и `requestId`: они описывают запрос, а запрос в целом прошёл успешно.

Ошибка элемента

typeString
Категория для ветвления: `validation_error`, `permission_error`, `not_found_error`, `conflict_error` и остальные. Этот набор заморожен и не будет расти, в отличие от `code`.
codeString
Конкретный сбой: `from_address_forbidden`, `invalid_email_address`, `too_many_recipients`, `reserved_header`, `message_too_large`, `unknown_parameter`. Набор открытый и пополняемый, так что неизвестный вам код трактуйте как его `type`.
messageString
Одно предложение, написанное для человека, называющее ошибочное значение, если оно есть. Не стабильный идентификатор. Переключайтесь по `code`.
paramString
Поле, которое было отклонено, как путь через точку ВНУТРИ ТОГО сообщения: `to.0`, `from`, `attachments`. Отсутствует, когда сбой не называет поля, и никогда не снабжается позицией в пакете, для которой есть `index`.