Ir a la documentación
PHP

Enviar un lote

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

emails->sendBatch

send_batch.php
$invoices = [    ['number' => 'INV-1042', 'email' => '[email protected]'],    ['number' => 'INV-1043', 'email' => '[email protected]'],]; $messages = []; foreach ($invoices as $invoice) {    $messages[] = [        'from' => '[email protected]',        'to' => $invoice['email'],        'subject' => 'Invoice ' . $invoice['number'],        'text' => 'Your invoice is attached.',    ];} $result = $client->emails->sendBatch($messages, idempotencyKey: 'invoices:2026-09'); echo $result->sent, ' sent, ', $result->failed, ' failed', PHP_EOL; foreach ($result as $item) {    if ($item['status'] === 'error') {        error_log($item['index'] . ' ' . $item['error']['code'] . ' ' . $item['error']['message']);    } else {        echo $item['index'], ' ', $item['email']['id'], PHP_EOL;    }}

sendBatch acepta una lista de arrays de mensaje, cada uno con exactamente la forma del array que acepta emails->send, y devuelve un OpenEmail\Result\BatchResult. Su items contiene un array por cada mensaje, en orden, cada uno ok con su mensaje o error con el sobre con el que se habría rechazado ese mensaje, y recorrer el resultado en un bucle los recorre. Nada se revierte, así que un recuento failed mayor que 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 para cada elemento, así que un lote reintentado reproduce cada mensaje en lugar de colapsarlos sobre el primero. Envía la misma lista en el mismo orden cuando lo reintentes: un elemento que se movió queda ligado a la clave de otra posición y vuelve como un error idempotency_key_reuse.

Un mensaje rechazado no lanza ninguna excepción. Solo la lanza un problema con el lote en su conjunto: una lista vacía, más de 100 mensajes, más de 10 con translate, un fallo de clave o de ámbito, o un fallo del servidor. Un fallo del servidor a mitad de camino llega después de que hayan salido los elementos anteriores, y el cliente lo reintenta con la misma clave, que reproduce esos elementos en lugar de enviarlos dos veces.

Los elementos se envían uno tras otro dentro de una sola solicitud, así que un lote grande de envíos inmediatos tarda bastante más que un solo send. Mantén generoso el timeout: del cliente.

Parámetros: emails->sendBatch

emailsarrayobligatorio
De 1 a 100 mensajes, enviados como `{"emails": [...]}` y aceptados de uno en uno en el orden dado. Cada uno pasa por el mismo tratamiento que `emails->send`, así que un destinatario solo se envuelve, un `DateTimeInterface` se convierte en un instante, los bytes de los adjuntos se codifican, y una entrada que no es un array lanza `InvalidArgumentException` antes de enviar nada. Una lista vacía, más de 100, o más de 10 mensajes con `translate` rechazan la llamada entera con un `validation_error` en `emails`. La falta del ámbito `emails:send` y un `idempotencyKey:` mal formado también rechazan la llamada entera, antes de enviar un solo mensaje.
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.
apiKeystring
Envía el lote con esta clave en lugar de la del cliente.

Cada mensaje de emails

fromstring or arrayobligatorio
El remitente, como dirección escueta, `Name <addr@host>` o un array con `email` y `name`. 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`.
tostring or arrayobligatorio
Al menos un destinatario; si es uno solo, el cliente lo envuelve en una lista. Como máximo 50 direcciones entre `to`, `cc` y `bcc` en conjunto, contadas por mensaje y no en todo el lote.
ccstring or array
Por defecto ninguna, y cuenta para el mismo total de 50 direcciones que `to` y `bcc`.
bccstring or array
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.
replyTostring or array
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.
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.
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`.
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.
headersarray
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.
attachmentsarray
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 un stream de `fopen`, un `SplFileInfo` o un stream PSR-7 y el cliente lo lee y lo codifica, o una cadena que ya esté en base64. Un array con solo `fileId` nombra un archivo que ya está en el espacio de trabajo y no cuenta para el límite en línea.
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.
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.
templatearray
Renderiza una plantilla almacenada en el servidor, por id (`tpl_…`) o por slug, con `version` fijando una revisión y `props` y `slots` rellenándola. Se resuelve una sola vez, cuando se acepta el elemento, y se rechaza junto con `html` o `text` y junto con `draftId`, ya que cada uno de esos es una segunda respuesta a qué contiene el mensaje.
scheduledAtDateTimeInterface or string
Un `DateTimeInterface`, 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. Una cadena de fecha sin hora significa medianoche UTC de ese día. Los elementos se programan de forma independiente, así que un lote puede contener cien horas de envío distintas.
cancellableForSecondsint
Una ventana de deshacer, en segundos, sobre un envío inmediato, de 0 a 900 y con 0 por defecto. Cualquier valor superior a 0 se rechaza junto con `scheduledAt` en el mismo elemento, ya que un mensaje programado ya se puede cancelar hasta que sale.
trackingarray
`opens` y `clicks`, cada uno opcional y cada uno sustituyendo la configuración solo para este mensaje. Una clave que omitas sigue a la dirección desde la que se envía el mensaje (o al catch-all que la recogió), que está desactivado salvo que esa dirección lo haya activado.
tagsarray
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` filtra por `status:`, `from:`, `broadcastId:` y la ventana de programación 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.
translatearray
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: OpenEmail\Result\BatchResult

El resultado es de solo lectura, IteratorAggregate sobre items y Countable, así que foreach ($result as $item) recorre los elementos y count($result) los cuenta.

itemsarray
Un array por cada mensaje, 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 llamada devuelve en cualquier caso y el `status` de cada elemento es lo que hay que evaluar.
sentint or null
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` cuyo `status` es `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. Es null solo cuando la respuesta no traía ningún recuento.
failedint or null
Cuántos elementos llevan un `error`. Un recuento mayor que 0 es una lista sobre la que actuar y no un motivo para reenviar el lote. Los mensajes aceptados ya han salido.

Cada elemento

indexint
La posición que ocupaba el mensaje de este elemento en la lista que enviaste. Se incluye como clave además de como orden, para que el código que filtra u ordena `items` pueda seguir diciendo qué mensaje falló.
statusstring
`ok` o `error`. `ok` lleva `email`, `error` lleva `error`, y ningún elemento lleva ambos.
emailarray
El mensaje aceptado, solo en un elemento `ok`, con la misma forma que devuelve un envío individual. Su `replayed` es 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. 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.
errorarray
Por qué se rechazó este mensaje concreto, solo en un elemento `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.

El error de un elemento

typestring
La categoría por la que 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`.
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`. Aquí se llama `code` porque este es el sobre decodificado, mientras que una excepción lleva el mismo valor como `errorCode`.
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.
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`.