Ir a la documentación
SDK

Enviar un lote

`emails.sendBatch`: hasta 100 mensajes, con resultados por elemento.

emails.sendBatch

send-batch.ts
const result = await openemail.emails.sendBatch(invoices.map(toMessage)) console.log(result.sent, 'sent,', result.failed, 'failed') for (const item of result.items) {  if (item.status === 'error') console.error(item.index, item.error.code, item.error.message)  else console.log(item.index, item.email.id)}

items contiene una entrada por cada mensaje de entrada, en orden, cada una ok con su mensaje o error con el sobre con el que se habría rechazado ese mensaje. Nada se revierte, así que failed > 0 es una lista sobre la que actuar y no un motivo para reenviar el lote.

Una sola clave de idempotencia cubre el lote y el servidor la extiende por elemento, así que un lote reintentado reproduce cada mensaje en lugar de colapsarlos sobre el primero.

Parámetros: emails.sendBatch

emailsEmailSend[]obligatorio
De uno a 100 mensajes, serializados como `{ "emails": [...] }` y aceptados de uno en uno en el orden dado. Un array vacío, más de 100, o más de 10 elementos que llevan `translate` rechazan la llamada entera con un `validation_error` en `emails`. Lo mismo ocurre si falta el scope `emails:send`, si el cuerpo no es un array ni `{ emails: [...] }`, y si el `Idempotency-Key` está mal formado, todo ello antes de que se envíe un solo mensaje.
options.idempotencyKeystring
Deduplica el lote entre procesos. El cliente adjunta de todos modos una clave recién generada en cada llamada, así que sus propios reintentos nunca envían por duplicado, y el servidor extiende la clave que reciba por elemento como `key/0`, `key/1`, etc., separadas por una barra, un carácter que tu propia clave no puede contener, de modo que una sola clave para cien mensajes no puede colapsarlos sobre el primero.
emails[].fromRecipientInputobligatorio
El remitente, como dirección escueta, `Name <addr@host>` o un objeto. No hay remitente de reserva y la clave debe tener permitida esta dirección; un rechazo hace fallar únicamente ese elemento, como un `permission_error` con el código `from_address_forbidden`.
emails[].toRecipientInput | RecipientInput[]obligatorio
Al menos un destinatario; si es uno solo, el cliente lo envuelve en un array. Como máximo 50 direcciones entre `to`, `cc` y `bcc` en conjunto, contadas por mensaje y no en todo el lote.
emails[].ccRecipientInput | RecipientInput[]
Por defecto ninguna, y cuenta para el mismo total de 50 direcciones que `to` y `bcc`.
emails[].bccRecipientInput | RecipientInput[]
Por defecto ninguna, y cuenta para el mismo total de 50 direcciones. `Bcc` es uno de los nombres que `headers` no puede establecer, así que esta es la única forma de enviar copia oculta. La variante como cabecera desharía el sobre por destinatario que mantiene oculta la dirección.
emails[].replyToRecipientInput
Adónde van las respuestas. Se aplica después de `headers`, así que sobrescribe un `Reply-To` que también hayas puesto ahí en lugar de añadir un segundo.
emails[].subjectstring
Como máximo 998 caracteres, el límite de línea de RFC 5322, y por defecto una cadena vacía. Un asunto vacío cede el paso al de la plantilla cuando `template` proporciona uno.
emails[].htmlstring
La parte HTML, de un millón de caracteres como máximo, y la parte que ven los destinatarios cuando se dan ambos cuerpos. Se requiere uno de `html`, `text`, `template` o `draftId`, y un elemento que no tenga ninguno falla como `validation_error` en `html`.
emails[].textstring
La parte de texto plano, de un millón de caracteres como máximo. Pueden enviarse ambas, y todos los transportes de esta ruta construyen un solo cuerpo a partir de una sola cadena, así que `html` gana cuando lo hay.
emails[].headersRecord<string, string>
Solo `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority y Feedback-ID; todo lo que el transporte establece por sí mismo (From, To, Bcc, Subject, Message-ID y las cabeceras DKIM y ARC) se rechaza como `reserved_header` en lugar de descartarse en silencio. Los valores tienen 998 caracteres como máximo y no pueden contener CR, LF ni NUL, porque una segunda línea es una segunda cabecera.
emails[].attachmentsAttachmentInput[]
Como máximo 20 archivos por mensaje, con los archivos en línea sumando 5 MB una vez descodificados, contados por mensaje y no por lote. `content` viaja en base64; pasa bytes y el cliente los codifica, que es el único punto donde un base64 hecho a mano desborda la pila de llamadas de forma fiable. Una entrada `{ fileId }` nombra un archivo que ya está en el espacio de trabajo y no cuenta para el límite en línea.
emails[].threadIdstring
Responde dentro de un hilo existente, con 256 caracteres como máximo. El transporte escribe In-Reply-To y References a partir de él, que es lo que hace que la respuesta aterrice en la conversación y no al lado.
emails[].draftIdstring
Envía el contenido de un borrador guardado bajo este sobre, con 256 caracteres como máximo. Los destinatarios, el asunto y las cabeceras construidos aquí son los que van por la red.
emails[].template{ id, version?, props?, slots? }
Renderiza una plantilla almacenada en el servidor, por id (`tpl_…`) o por slug, con `version` fijando una revisión y `props`/`slots` rellenándola. Se resuelve una sola vez, cuando se acepta el elemento, y se rechaza junto con `html`/`text` y junto con `draftId`, ya que cada uno de esos es una segunda respuesta a qué contiene el mensaje.
emails[].scheduledAtDate | string
Un `Date`, un instante ISO-8601 o una duración como `PT1H`; al menos un segundo en el futuro y a lo sumo 365 días por delante. Los elementos se programan de forma independiente, así que un lote puede contener cien horas de envío distintas.
emails[].cancellableForSecondsnumber
Una ventana de deshacer, en segundos, sobre un envío inmediato: un integer de 0 a 900, con 0 por defecto. Cualquier valor superior a 0 se rechaza junto con `scheduledAt` en el mismo elemento, ya que un mensaje programado se puede cancelar hasta que sale.
emails[].trackingTrackingRequest
`opens` y `clicks`, cada uno opcional por separado y cada uno sustituyendo la configuración solo para este mensaje. Un interruptor que omitas recae en la configuración de la dirección desde la que se envía el mensaje, o bien en la de Todas las direcciones, que está activada salvo que alguna de ellas la haya desactivado.
emails[].tagsRecord<string, string>
Como máximo 10 etiquetas, con claves de 1 a 64 caracteres tomados de `A-Za-z0-9_-` y valores de hasta 256. Se devuelven tal cual en el mensaje y nunca se interpretan: `emails.list` acepta `status`, `from`, `limit` y `cursor` y nada más, así que una etiqueta es algo que se lee de un mensaje que ya tienes y no una forma de encontrarlo.
emails[].translateSendTranslateOptions
Envía este elemento en otro idioma, resuelto en el momento de la aceptación para que las palabras aprobadas sean las que salen. Como máximo 10 elementos de un lote pueden llevarlo: cada uno consume varias llamadas al modelo y los elementos se ejecutan en orden, así que un lote mayor se interrumpiría a mitad de envío. Por encima de eso, la llamada entera se rechaza como `too_many_items` en `emails`, antes de enviar nada.

Respuesta: BatchResultResource

itemsBatchItemResource[]
Una entrada por cada mensaje enviado, en el orden en que los enviaste. Nada se revierte, así que esto es un registro de lo que pasó con cada mensaje y no un informe sobre una transacción. La API responde 207 tanto si se aceptaron todos los mensajes como si solo algunos o ninguno, así que la promesa se resuelve en cualquier caso y el `status` de cada elemento es lo que hay que evaluar.
sentnumber
Cuántos elementos se ACEPTARON, que no es lo mismo que cuántos salieron. Un elemento puede ser `ok` y aun así llevar un `email.status` de `failed` o `partial`, porque un transporte que rechaza el mensaje después de que exista la fila es un resultado de entrega y no una solicitud rechazada.
failednumber
Cuántas entradas llevan un `error`. `failed > 0` es una lista sobre la que actuar y no un motivo para reenviar el lote. Los mensajes aceptados ya han salido.
items[].indexnumber
La posición que ocupaba el mensaje de esta entrada en el array que enviaste. Se incluye como campo además de como orden, para que el código que filtra u ordena `items` pueda seguir diciendo qué entrada falló.
items[].status'ok' | 'error'
El discriminante de la unión: `ok` lleva `email`, `error` lleva `error`, y ninguna entrada lleva ambos.
items[].emailSentEmailResource
El mensaje aceptado, solo en una entrada `ok`, con la misma forma que devuelve un envío individual. No lleva la clave `tracking`, porque la interacción se informa más tarde y en el momento de la aceptación no hay nada que informar.
items[].email.replayedboolean
True cuando el `Idempotency-Key` derivado coincidió con un envío que ya existía, así que no se envió nada nuevo y este es el mensaje original.
items[].error{ type: string; code: string; message: string; param?: string }
Por qué se rechazó este mensaje concreto, solo en una entrada `error`. Es el sobre de error de la API menos `docUrl` y `requestId`: esos describen la solicitud, y la solicitud en su conjunto tuvo éxito.
items[].error.typestring
La categoría por la que un cliente puede ramificar: `validation_error`, `permission_error`, `not_found_error`, `conflict_error` y las demás. El conjunto está congelado y no crecerá, a diferencia de `code`.
items[].error.codestring
El fallo concreto: `from_address_forbidden`, `invalid_email_address`, `too_many_recipients`, `reserved_header`, `message_too_large`, `unknown_parameter`. Es abierto y aditivo, así que trata un código que no reconozcas según su `type`.
items[].error.messagestring
Una frase escrita para una persona, que nombra el valor problemático cuando lo hay. No es un identificador estable. Usa `code` para ramificar.
items[].error.paramstring
El campo que se rechazó, como ruta con puntos dentro de ESE mensaje: `to.0`, `from`, `attachments`. Ausente cuando el fallo no nombra ningún campo, y nunca lleva como prefijo la posición en el lote, para lo cual está `index`.