Aller à la documentation
SDK

Envoyer un batch

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

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 contient une entrée par élément transmis, dans l'ordre, chacune soit ok avec son message, soit error avec l'enveloppe qui aurait servi à refuser ce message. Rien n'est annulé : failed > 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 par élément : un batch réessayé rejoue donc chaque message au lieu de les réduire tous au premier.

Paramètres : emails.sendBatch

emailsEmailSend[]obligatoire
De un à 100 messages, sérialisés en `{ "emails": [...] }` et acceptés un par un dans l'ordre donné. Un array vide, plus de 100 éléments, ou plus de 10 éléments portant `translate` font refuser tout l'appel avec un `validation_error` sur `emails`. Il en va de même pour une portée `emails:send` manquante, un corps qui n'est ni un array ni `{ emails: [...] }`, et un `Idempotency-Key` malformé, tous refusés avant qu'un seul message ne soit envoyé.
options.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 retentatives n'envoient jamais deux fois, et le serveur étend la clé qu'il reçoit par é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 réduire tous au premier.
emails[].fromRecipientInputobligatoire
L'expéditeur, sous forme d'adresse nue, de `Name <addr@host>` ou d'un object. 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`.
emails[].toRecipientInput | RecipientInput[]obligatoire
Au moins un destinataire ; un destinataire seul est enveloppé dans un array 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.
emails[].ccRecipientInput | RecipientInput[]
Vide par défaut, et compte dans le même total de 50 adresses que `to` et `bcc`.
emails[].bccRecipientInput | RecipientInput[]
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.
emails[].replyToRecipientInput
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.
emails[].subjectstring
998 caractères au maximum, la limite de ligne RFC 5322, et une chaîne vide par défaut. Un objet vide laisse la place à celui du template quand `template` en fournit un.
emails[].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`.
emails[].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.
emails[].headersRecord<string, string>
`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.
emails[].attachmentsAttachmentInput[]
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` circule en base64 ; transmettez des octets et le client les encode, ce qui est le seul endroit où un base64 fait main fait régulièrement déborder la pile d'appels. Une entrée `{ fileId }` désigne un fichier déjà présent dans le workspace et n'est pas décomptée du plafond inline.
emails[].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é.
emails[].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.
emails[].template{ id, version?, props?, slots? }
Rend un template stocké côté serveur, par id (`tpl_…`) ou par slug, `version` épinglant une révision et `props`/`slots` le remplissant. Résolu une seule fois, au moment où l'élément est accepté, et refusé en présence de `html`/`text` comme de `draftId`, puisque chacun d'eux est une deuxième réponse à la question du contenu du message.
emails[].scheduledAtDate | string
Un `Date`, un instant ISO-8601, ou une durée comme `PT1H` ; au moins une seconde dans le futur et au plus 365 jours à l'avance. Les éléments se planifient indépendamment : un même batch peut donc contenir cent heures d'envoi différentes.
emails[].cancellableForSecondsnumber
Une fenêtre d'annulation en secondes sur un envoi immédiat, un integer 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.
emails[].trackingTrackingRequest
`opens` et `clicks`, chacun optionnel indépendamment et chacun surchargeant le réglage pour ce seul message. Un interrupteur omis retombe sur le réglage de l'adresse depuis laquelle le message part, ou sinon sur celui de Toutes les adresses, qui est activé sauf si l'un des deux l'a désactivé.
emails[].tagsRecord<string, string>
10 libellés au maximum, 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` accepte `status`, `from`, `limit` et `cursor` 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.
emails[].translateSendTranslateOptions
Envoie cet élément dans une autre langue, résolu 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 que quoi que ce soit ne soit envoyé.

Réponse : BatchResultResource

itemsBatchItemResource[]
Une entrée par élément transmis, 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 : la promesse est donc résolue dans tous les cas et c'est le `status` de chaque élément qui doit guider le code.
sentnumber
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.status` `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.
failednumber
Combien d'entrées portent un `error`. `failed > 0` est une liste sur laquelle agir, pas une raison de renvoyer le batch. Les messages acceptés sont déjà partis.
items[].indexnumber
La position qu'occupait le message de cette entrée dans l'array que vous avez envoyé. Porté comme champ en plus de l'ordre, pour qu'un code qui filtre ou trie `items` puisse toujours dire quel élément d'entrée a échoué.
items[].status'ok' | 'error'
Le discriminant de l'union : `ok` porte `email`, `error` porte `error`, et aucune entrée ne porte les deux.
items[].emailSentEmailResource
Le message accepté, uniquement sur une entrée `ok`, dans la même forme que celle renvoyée par un envoi unitaire. Il ne porte pas de clé `tracking`, car l'engagement est rapporté plus tard et il n'y a rien à rapporter au moment de l'acceptation.
items[].email.replayedboolean
Vrai quand l'`Idempotency-Key` dérivée correspondait à un envoi qui existait déjà : rien de nouveau n'a été envoyé et voici le message d'origine.
items[].error{ type: string; code: string; message: string; param?: string }
Pourquoi ce message précis a été refusé, uniquement sur une entrée `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.
items[].error.typestring
La catégorie sur laquelle un client peut 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`.
items[].error.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`.
items[].error.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`.
items[].error.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`.