Enviar um lote
`emails.send_batch`: até 100 mensagens, resultados por item.
emails.send_batch
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)}" endendsend_batch aceita um Array de Hashes de mensagem, cada um com exatamente a forma do corpo de emails.send, e devolve um OpenEmail::BatchResult. O seu items contém um Hash por cada mensagem, pela mesma ordem, cada um ok com a sua mensagem ou error com o envelope com que essa mensagem teria sido recusada. Nada é revertido, pelo que uma contagem failed acima de 0 é uma lista sobre a qual agir e não um motivo para reenviar o lote.
Uma só chave de idempotência cobre o lote e o servidor estende-a para cada item, por isso um lote repetido reproduz cada mensagem em vez de as reduzir à primeira. Envie o mesmo Array pela mesma ordem quando o repetir: um item que mudou de lugar fica ligado à chave de outra posição e volta como um erro idempotency_key_reuse.
Uma mensagem recusada não lança exceção. Só um problema com o lote no seu conjunto lança: um Array vazio, mais de 100 mensagens, mais de 10 com translate, uma falha de chave ou de âmbito, ou uma falha do servidor. Uma falha do servidor a meio chega depois de os itens anteriores terem saído, e o cliente repete-a com a mesma chave, o que reproduz esses itens em vez de os enviar duas vezes.
Os itens são enviados um a seguir ao outro dentro de um único pedido, por isso um lote grande de envios imediatos demora bastante mais do que um único send. Mantenha generoso o timeout: do cliente.
Parâmetros: emails.send_batch
emailsArray<Hash>obrigatório- De 1 a 100 mensagens, enviadas como `{"emails": [...]}` e aceites uma de cada vez pela ordem indicada. Cada uma passa pelo mesmo tratamento que `emails.send`, por isso um destinatário único é envolvido, um Time torna-se um instante e os bytes dos anexos são codificados. Um Array vazio, mais de 100, ou mais de 10 mensagens com `translate` fazem recusar a chamada inteira com um `validation_error` em `emails`. A falta do âmbito `emails:send` e um `idempotency_key:` mal formado também fazem recusar a chamada inteira, antes de ser enviada uma única mensagem.
idempotency_keyString- Elimina duplicados do lote entre processos. O cliente anexa sempre uma chave acabada de gerar em cada chamada, pelo que as suas próprias repetições nunca enviam em duplicado, e o servidor estende a chave que receber para cada item como `key/0`, `key/1` e assim por diante, separadas por uma barra, um carácter que a sua própria chave não pode conter, pelo que uma chave para cem mensagens não as pode reduzir à primeira.
api_keyString- Envia o lote com esta chave em vez da do cliente.
Cada mensagem em emails
fromString or Hashobrigatório- O remetente, como endereço simples, `Name <addr@host>` ou um Hash com `email` e `name`. Não existe remetente alternativo e a chave tem de ter permissão para este endereço. Uma recusa faz falhar apenas esse item, como `permission_error` com o código `from_address_forbidden`.
toString, Hash or Arrayobrigatório- Pelo menos um destinatário, e um único é envolvido num Array pelo cliente. No máximo 50 endereços em `to`, `cc` e `bcc` combinados, contados por mensagem e não em todo o lote.
ccString, Hash or Array- Por predefinição, nenhum, e conta para o mesmo total de 50 endereços que `to` e `bcc`.
bccString, Hash or Array- Por predefinição, nenhum, e conta para o mesmo total de 50 endereços. `Bcc` é um dos nomes que `headers` não pode definir, pelo que esta é a única forma de enviar uma cópia oculta. A forma de cabeçalho anularia o envelope por destinatário que mantém o endereço oculto.
replyToString or Hash- Para onde vão as respostas. É aplicado depois de `headers`, pelo que substitui um `Reply-To` que também tenha definido aí em vez de acrescentar um segundo.
subjectString- No máximo 998 caracteres, o limite de linha do RFC 5322, e a predefinição é uma String vazia. Um assunto vazio dá lugar ao do próprio modelo quando `template` fornece um.
htmlString- A parte HTML, com no máximo um milhão de caracteres, e a parte que os destinatários veem quando ambos os corpos são fornecidos. É obrigatório um de `html`, `text`, `template` ou `draftId`, e um item sem nenhum deles falha com um `validation_error` em `html`.
textString- A parte em texto simples, com no máximo um milhão de caracteres. Podem ser enviadas ambas, e cada transporte neste percurso constrói um corpo a partir de uma String, pelo que `html` prevalece quando existe.
headersHash- Apenas `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority e Feedback-ID. Tudo o que o transporte define por si próprio (From, To, Bcc, Subject, Message-ID, os cabeçalhos DKIM e ARC) é recusado como `reserved_header` em vez de ser descartado silenciosamente. Os valores têm no máximo 998 caracteres e não podem conter CR, LF nem NUL, porque uma segunda linha é um segundo cabeçalho.
attachmentsArray<Hash>- No máximo 20 ficheiros por mensagem, com os ficheiros inline a totalizarem 5 MB depois de descodificados, contados por mensagem e não por lote. `content` segue em base64 pela rede. Passe os bytes como String binária, IO ou Pathname e o cliente codifica-os. Um Hash só com `fileId` refere um ficheiro que já está no espaço de trabalho e não conta para o limite inline.
threadIdString- Responder numa conversa existente, com no máximo 256 caracteres. O transporte escreve In-Reply-To e References a partir dele, e é isso que faz a resposta ficar na conversa e não ao lado dela.
draftIdString- Enviar o conteúdo de um rascunho guardado com este envelope, com no máximo 256 caracteres. Os destinatários, o assunto e os cabeçalhos construídos aqui são o que é efetivamente enviado.
templateHash- Renderizar um modelo guardado no servidor, por id (`tpl_…`) ou slug, com `version` a fixar uma revisão e `props` e `slots` a preenchê-lo. Resolvido uma vez, quando o item é aceite, e recusado em conjunto com `html` ou `text` e com `draftId`, já que cada um desses é uma segunda resposta ao que a mensagem contém.
scheduledAtTime, DateTime or String- Um Time ou um DateTime, um instante ISO 8601 ou uma duração como `PT1H`, pelo menos um segundo no futuro e no máximo 365 dias à frente. Uma Date do Ruby significa meia-noite UTC desse dia. Os itens são agendados de forma independente, pelo que um lote pode conter cem horas de envio diferentes.
cancellableForSecondsInteger- Uma janela para anular, em segundos, num envio imediato, de 0 a 900 e com 0 por predefinição. Qualquer valor acima de 0 é recusado em conjunto com `scheduledAt` no mesmo item, já que uma mensagem agendada já pode ser cancelada até sair.
trackingHash- `opens` e `clicks`, cada um opcional e cada um a substituir a definição apenas para esta mensagem. Uma chave omitida segue o endereço a partir do qual a mensagem é enviada (ou o catch-all que o apanhou), em que o rastreio está inativo a menos que esse endereço o tenha ativado.
tagsHash- No máximo 10 etiquetas, com chaves de 1 a 64 caracteres de `A-Za-z0-9_-` e valores até 256. Devolvidas na mensagem e nunca interpretadas: `emails.list` filtra por `status:`, `from:`, `broadcast_id:` e pela janela de agendamento e nada mais, pelo que uma etiqueta é algo a ler numa mensagem que já tem e não uma forma de a encontrar.
translateHash- Enviar este item noutra língua, resolvido no momento da aceitação para que as palavras aprovadas sejam as palavras que saem. No máximo 10 itens num lote podem incluí-lo: cada um gasta várias chamadas ao modelo e os itens são executados por ordem, pelo que um lote maior seria interrompido a meio do envio. Acima disso, a chamada inteira é recusada como `too_many_items` em `emails`, antes de qualquer envio.
Resposta: OpenEmail::BatchResult
itemsArray<Hash>- Um Hash por cada mensagem, pela ordem em que as enviou. Nada é revertido, pelo que isto é um registo do que aconteceu a cada mensagem e não um relatório sobre uma transação. A API responde 207 quer todas as mensagens tenham sido aceites, quer algumas, quer nenhuma, pelo que a chamada devolve o resultado em qualquer caso e é o `status` de cada item que deve orientar a lógica.
sentInteger- Quantos itens foram ACEITES, o que não é o mesmo que quantos saíram. Um item pode ser `ok` e ter ainda assim um `email` cujo `status` é `failed` ou `partial`, porque um transporte que recusa a mensagem depois de a linha existir é um resultado de entrega e não um pedido rejeitado.
failedInteger- Quantos itens têm um `error`. Uma contagem acima de 0 é uma lista sobre a qual agir e não um motivo para reenviar o lote. As mensagens aceites já foram enviadas.
Cada item
indexInteger- A posição que a mensagem deste item ocupava no Array que enviou. Transmitida como chave e também pela ordem, para que o código que filtra ou ordena `items` consiga ainda saber que mensagem falhou.
statusString- `ok` ou `error`. `ok` traz `email`, `error` traz `error`, e nenhum item traz ambos.
emailHash- A mensagem aceite, apenas num item `ok`, com a mesma forma que um envio individual devolve. O seu `replayed` é true quando a `Idempotency-Key` derivada correspondeu a um envio que já existia, por isso nada de novo foi enviado e esta é a mensagem original. Não inclui a chave `tracking`, porque o envolvimento é reportado mais tarde e não há nada a reportar no momento da aceitação.
errorHash- Porque é que esta mensagem foi recusada, apenas num item `error`. É o envelope de erro da API sem `docUrl` nem `requestId`: esses descrevem o pedido, e o pedido no seu conjunto foi bem-sucedido.
O erro de um item
typeString- A categoria em que basear a lógica: `validation_error`, `permission_error`, `not_found_error`, `conflict_error` e as restantes. O conjunto está fixo e não vai crescer, ao contrário de `code`.
codeString- A falha específica: `from_address_forbidden`, `invalid_email_address`, `too_many_recipients`, `reserved_header`, `message_too_large`, `unknown_parameter`. Aberto e cumulativo, por isso trate um código que não reconheça de acordo com o seu `type`.
messageString- Uma frase escrita para uma pessoa, que indica o valor problemático quando existe. Não é um identificador estável. Baseie a lógica em `code`.
paramString- O campo que foi recusado, como caminho com pontos dentro DESSA mensagem: `to.0`, `from`, `attachments`. Ausente quando a falha não se refere a nenhum campo, e nunca prefixado com a posição no lote, já que é para isso que serve `index`.