Aller à la documentation
PHP

Envoyer un lot

`emails->sendBatch` : jusqu'à 100 messages, un résultat par élément.

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 prend une liste de tableaux de messages, chacun ayant exactement la forme du tableau que prend emails->send, et renvoie un OpenEmail\Result\BatchResult. Ses items contiennent un tableau par message, dans l'ordre, chacun soit ok avec son message, soit error avec l'enveloppe par laquelle ce message aurait été refusé, et une boucle sur le résultat les parcourt. Rien n'est annulé : un compteur failed supérieur à 0 est donc une liste sur laquelle agir, pas une raison de renvoyer le batch.

Une seule clé d'idempotence couvre le batch et le serveur l'étend pour chaque élément : un batch réessayé rejoue donc chaque message au lieu de les confondre avec le premier. Renvoyez la même liste dans le même ordre quand vous le réessayez : un élément qui a changé de place est lié à la clé d'une autre position et revient sous forme d'erreur idempotency_key_reuse.

Un message refusé ne lève pas d'exception. Seul un problème qui touche le batch dans son ensemble en lève une : une liste vide, plus de 100 messages, plus de 10 portant translate, un échec de clé ou de portée, ou une panne du serveur. Une panne du serveur en cours de route survient après le départ des éléments précédents, et le client réessaie avec la même clé, ce qui rejoue ces éléments au lieu de les envoyer deux fois.

Les éléments sont envoyés l'un après l'autre dans une seule requête : un gros batch d'envois immédiats prend donc nettement plus de temps qu'un seul send. Gardez un timeout: généreux sur le client.

Paramètres : emails->sendBatch

emailsarrayobligatoire
De 1 à 100 messages, envoyés sous la forme `{"emails": [...]}` et acceptés un par un dans l'ordre donné. Chacun passe par le même traitement que `emails->send` : un destinataire seul est enveloppé, un `DateTimeInterface` devient un instant, les octets des pièces jointes sont encodés, et une entrée qui n'est pas un tableau lève `InvalidArgumentException` avant tout envoi. Une liste vide, plus de 100 messages, ou plus de 10 messages portant `translate` font refuser tout l'appel avec une `validation_error` sur `emails`. Une portée `emails:send` manquante et un `idempotencyKey:` mal formé font aussi refuser tout l'appel, avant l'envoi du moindre message.
idempotencyKeystring
Déduplique le batch entre processus. Le client attache de toute façon une clé fraîchement générée à chaque appel, si bien que ses propres réessais n'envoient jamais deux fois, et le serveur étend la clé qu'il reçoit pour chaque élément en `key/0`, `key/1` et ainsi de suite, séparés par une barre oblique, un caractère que votre propre clé ne peut pas contenir : une seule clé portant sur cent messages ne peut donc pas les confondre avec le premier.
apiKeystring
Envoie le batch avec cette clé au lieu de celle du client.

Chaque message de emails

fromstring or arrayobligatoire
L'expéditeur, sous forme d'adresse nue, de `Name <addr@host>` ou d'un tableau avec `email` et `name`. Il n'y a pas d'expéditeur de repli et la clé doit être autorisée sur cette adresse. Un refus fait échouer ce seul élément, sous la forme d'un `permission_error` avec le code `from_address_forbidden`.
tostring or arrayobligatoire
Au moins un destinataire, et un destinataire seul est enveloppé dans une liste par le client. 50 adresses au maximum pour `to`, `cc` et `bcc` réunis, comptées par message et non sur l'ensemble du batch.
ccstring or array
Vide par défaut, et compte dans le même total de 50 adresses que `to` et `bcc`.
bccstring or array
Vide par défaut, et compte dans le même total de 50 adresses. `Bcc` fait partie des noms que `headers` ne peut pas définir : c'est donc le seul moyen de mettre en copie cachée. La forme en-tête annulerait l'enveloppe par destinataire qui garde l'adresse cachée.
replyTostring or array
Où vont les réponses. Appliqué après `headers`, il écrase donc un `Reply-To` que vous y auriez également défini au lieu d'en ajouter un deuxième.
subjectstring
998 caractères au maximum, la limite de ligne de la RFC 5322, et une chaîne vide par défaut. Un objet vide laisse la place à celui du modèle quand `template` en fournit un.
htmlstring
La partie HTML, un million de caractères au maximum, et celle que voient les destinataires quand les deux corps sont fournis. `html`, `text`, `template` ou `draftId` : l'un des quatre est obligatoire, et un élément qui n'en a aucun échoue avec un `validation_error` sur `html`.
textstring
La partie en texte brut, un million de caractères au maximum. Les deux peuvent être envoyées, et tout transport sur ce chemin construit un seul corps à partir d'une seule chaîne : `html` l'emporte donc quand il y en a un.
headersarray
`X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority et Feedback-ID uniquement. Tout ce que le transport définit lui-même (From, To, Bcc, Subject, Message-ID, les en-têtes DKIM et ARC) est refusé avec `reserved_header` plutôt qu'ignoré en silence. Les valeurs font au maximum 998 caractères et ne peuvent porter ni CR, ni LF, ni NUL, car une deuxième ligne est un deuxième en-tête.
attachmentsarray
20 fichiers au maximum par message, les fichiers inline totalisant 5 Mo une fois décodés, comptés par message et non par batch. `content` est en base64 sur le réseau. Passez un flux issu de `fopen`, un `SplFileInfo` ou un flux PSR-7 et le client le lit et l'encode, ou une chaîne déjà en base64. Un tableau avec seulement `fileId` désigne un fichier déjà présent dans l'espace de travail et n'est pas compté dans le plafond inline.
threadIdstring
Répond dans un thread existant, 256 caractères au maximum. Le transport en déduit In-Reply-To et References, ce qui fait atterrir la réponse dans la conversation plutôt qu'à côté.
draftIdstring
Envoie le contenu d'un brouillon enregistré sous cette enveloppe, 256 caractères au maximum. Ce sont les destinataires, l'objet et les en-têtes construits ici qui partent sur le réseau.
templatearray
Rend un modèle stocké côté serveur, par id (`tpl_…`) ou par slug, `version` figeant une révision et `props` et `slots` le remplissant. Fixé une seule fois, au moment où l'élément est accepté, et refusé en présence de `html` ou `text` comme de `draftId`, puisque chacun d'eux est une deuxième réponse à la question du contenu du message.
scheduledAtDateTimeInterface or string
Un `DateTimeInterface`, un instant ISO 8601, ou une durée comme `PT1H`, au moins une seconde dans le futur et au plus 365 jours à l'avance. Une chaîne de date sans heure signifie minuit UTC ce jour-là. Les éléments se planifient indépendamment : un même batch peut donc contenir cent heures d'envoi différentes.
cancellableForSecondsint
Une fenêtre d'annulation en secondes sur un envoi immédiat, de 0 à 900, 0 par défaut. Toute valeur supérieure à 0 est refusée en présence de `scheduledAt` sur le même élément, puisqu'un message planifié est déjà annulable jusqu'à son départ.
trackingarray
`opens` et `clicks`, chacun optionnel et chacun surchargeant le réglage pour ce seul message. Une clé omise suit l'adresse depuis laquelle le message part (ou le catch-all qui l'a attrapée), dont le réglage est désactivé sauf si cette adresse l'a activé.
tagsarray
10 libellés au maximum, avec des clés de 1 à 64 caractères pris dans `A-Za-z0-9_-` et des valeurs jusqu'à 256. Renvoyés tels quels sur le message et jamais interprétés : `emails->list` filtre sur `status:`, `from:`, `broadcastId:` et la fenêtre de planification, et rien d'autre. Un tag est donc une chose à lire sur un message que vous détenez déjà, pas un moyen de le retrouver.
translatearray
Envoie cet élément dans une autre langue, fixé au moment de l'acceptation pour que les mots approuvés soient les mots qui partent. 10 éléments au maximum peuvent le porter dans un même batch : chacun consomme plusieurs appels au modèle et les éléments s'exécutent dans l'ordre, si bien qu'un batch plus grand serait interrompu en plein envoi. Au-delà, tout l'appel est refusé avec `too_many_items` sur `emails`, avant tout envoi.

Réponse : OpenEmail\Result\BatchResult

Le résultat est en lecture seule, IteratorAggregate sur items et Countable : foreach ($result as $item) parcourt donc les éléments et count($result) les compte.

itemsarray
Un tableau par message, dans l'ordre où vous les avez envoyés. Rien n'est annulé : c'est donc le relevé de ce qui est arrivé à chaque message, pas le compte rendu d'une transaction. L'API répond 207 que tous les messages aient été acceptés, quelques-uns seulement ou aucun : l'appel retourne donc dans tous les cas, et c'est le `status` de chaque élément qui doit guider le code.
sentint or null
Combien d'éléments ont été ACCEPTÉS, ce qui n'est pas la même chose que combien sont partis. Un élément peut être `ok` et porter malgré tout un `email` dont le `status` vaut `failed` ou `partial`, car un transport qui refuse le message une fois la ligne créée est un résultat de distribution, pas une requête rejetée. null seulement quand la réponse ne portait aucun décompte.
failedint or null
Combien d'éléments portent un `error`. Un compteur supérieur à 0 est une liste sur laquelle agir, pas une raison de renvoyer le batch. Les messages acceptés sont déjà partis.

Chaque élément

indexint
La position qu'occupait le message de cet élément dans la liste que vous avez envoyée. Portée comme clé en plus de l'ordre, pour qu'un code qui filtre ou trie `items` puisse toujours dire quel message a échoué.
statusstring
`ok` ou `error`. `ok` porte `email`, `error` porte `error`, et aucun élément ne porte les deux.
emailarray
Le message accepté, uniquement sur un élément `ok`, sous la même forme que celle que renvoie un envoi unique. Son `replayed` vaut true quand l'`Idempotency-Key` dérivée correspondait à un envoi qui existait déjà : rien de nouveau n'a alors été envoyé et c'est le message d'origine. Il ne porte pas de clé `tracking`, car l'engagement est signalé plus tard et il n'y a rien à signaler au moment de l'acceptation.
errorarray
Pourquoi ce message précis a été refusé, uniquement sur un élément `error`. C'est l'enveloppe d'erreur de l'API, moins `docUrl` et `requestId` : ceux-ci décrivent la requête, et la requête dans son ensemble a réussi.

L'erreur d'un élément

typestring
La catégorie sur laquelle se brancher : `validation_error`, `permission_error`, `not_found_error`, `conflict_error` et les autres. L'ensemble est figé et ne s'agrandira pas, contrairement à `code`.
codestring
L'échec précis : `from_address_forbidden`, `invalid_email_address`, `too_many_recipients`, `reserved_header`, `message_too_large`, `unknown_parameter`. Ouvert et additif : traitez donc un code que vous ne reconnaissez pas comme son `type`. Il s'appelle ici `code` parce qu'il s'agit de l'enveloppe décodée, alors qu'une exception porte la même valeur sous le nom `errorCode`.
messagestring
Une phrase écrite pour un humain, qui nomme la valeur fautive quand il y en a une. Ce n'est pas un identifiant stable. Branchez-vous sur `code`.
paramstring
Le champ qui a été refusé, sous forme de chemin pointé à l'intérieur de CE message : `to.0`, `from`, `attachments`. Absent quand l'échec ne nomme aucun champ, et jamais préfixé par la position dans le batch, ce à quoi sert `index`.