Envoyer un e-mail
`emails.send` : un message, maintenant ou plus tard.
emails.send
const email = await openemail.emails.send({ from: { email: '[email protected]', name: 'Acme Billing' }, to: ['[email protected]', 'Grace <[email protected]>'], cc: '[email protected]', bcc: [{ email: '[email protected]' }], replyTo: '[email protected]', subject: 'Your September invoice', html: '<p>Invoice attached.</p>', text: 'Invoice attached.', headers: { 'X-Campaign': 'invoices' }, attachments: [{ filename: 'invoice.pdf', content: pdfBytes }], threadId: 'thread_…', scheduledAt: 'PT1H', tags: { order: '4021' }, tracking: { opens: true, clicks: true },})to, cc et bcc acceptent un destinataire ou plusieurs, et un destinataire seul est enveloppé pour vous. Chacun peut être une adresse nue, Name <addr@host>, ou { email, name }.
Paramètres
fromRecipientInputobligatoire- L'expéditeur. Une adresse nue, `Name <addr@host>`, ou un objet. Ce doit être une adresse depuis laquelle cette clé peut envoyer. Il n'y a pas d'expéditeur de repli, car le repli serait l'adresse par défaut de l'espace de travail, qui change au gré des adresses qui arrivent et qui partent.
toRecipientInput | RecipientInput[]obligatoire- Un destinataire ou plusieurs ; un destinataire seul est enveloppé pour vous. 50 au maximum pour to, cc et bcc réunis.
ccRecipientInput | RecipientInput[]- Compte dans la limite de 50 destinataires.
bccRecipientInput | RecipientInput[]- Jamais nommé dans les octets que reçoivent les autres, car une enveloppe est transmise par destinataire.
replyToRecipientInput- Une seule adresse, envoyée comme en-tête Reply-To.
subjectstring- 998 caractères au maximum, la limite de ligne RFC 5322. Vide par défaut.
htmlstring- L'un de html, text, draftId ou template est obligatoire. C'est le HTML que voient les destinataires quand html et text sont tous deux fournis.
textstring- La partie en texte brut.
template{ id, version?, props?, slots? }- Rend un template stocké côté serveur. `version` épingle une révision ; omettez-le pour utiliser ce qui est publié au moment où la requête est acceptée. Une prop inconnue ou manquante donne un 422 plutôt qu'un blanc dans le message.
draftIdstring- Envoie un brouillon enregistré sous cette enveloppe.
headersRecord<string, string>- `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority et Feedback-Id. Tout ce que le transport définit lui-même est refusé plutôt qu'ignoré en silence.
attachmentsAttachmentInput[]- `{ filename, content, contentType? }`, ou `{ fileId }` désignant un fichier déjà présent dans le workspace. Transmettez des octets pour content et ils sont encodés en base64 pour vous. 20 fichiers, les fichiers inline étant plafonnés à 5 Mo au total une fois décodés. Un fichier stocké peut être plus gros et voyage sous forme de lien de téléchargement.
attachmentDeliveryAttachmentDeliveryMode- `mime`, `link` ou `auto`. `auto` transporte les fichiers sous forme de liens de téléchargement dès qu'ils dépassent 2 Mo sur un domaine disposant d'un domaine de fichiers actif, et à l'intérieur du message sinon. Omis, c'est le réglage de la boîte qui s'applique, et il vaut `auto` par défaut.
threadIdstring- Répond dans un thread existant. Le transport écrit In-Reply-To et References.
scheduledAtDate | string- Un Date, un instant ISO-8601, ou une durée comme `PT1H`. Jusqu'à un an à l'avance, jamais dans le passé. Ne peut pas être combiné avec cancellableForSeconds.
cancellableForSecondsnumber- De 0 à 900. Une fenêtre d'annulation sur un envoi immédiat : le mécanisme d'annulation du composer, exposé plutôt que codé en dur.
tagsRecord<string, string>- Jusqu'à 10 libellés, renvoyés tels quels et filtrables. Jamais interprétés.
signatureboolean- Indique si ce message porte la signature de l'adresse depuis laquelle il est envoyé, à savoir la signature propre à cette adresse ou, à défaut, celle définie pour Toutes les adresses. true par défaut, car une signature appartient à l'adresse et non au client qui a envoyé le message. Mettez `false` pour le courrier qu'un programme envoie au nom de quelqu'un — un reçu, une réinitialisation de mot de passe, un récapitulatif —, dont aucun ne veut la signature d'une personne en dessous.
tracking{ opens?, clicks? }- Indique s'il faut ajouter un pixel d'ouverture et réécrire les liens pour ce message. Activé sauf si le propriétaire du workspace a désactivé le suivi pour l'adresse d'expédition ou pour Toutes les adresses ; chacun des deux champs indiqué ici tranche pour ce seul message, quel que soit le réglage de l'adresse.
translate{ to, from?, subject?, includeOriginal? }- L'envoie dans la langue du destinataire. `to` accepte un code, un nom anglais ou le nom de la langue dans sa propre langue ; `subject` et `includeOriginal` valent true par défaut. Résolu au moment où la requête est acceptée, pour qu'un message planifié porte les mots qui ont été approuvés. Refusé en présence de `draftId`.
Réponse
idstring- L'id de l'envoi, `msg_…`. Utilisez-le pour `get`, `cancel`, `reschedule` et `getTracking`.
statusEmailStatus- queued, scheduled, sending, sent, partial, cancelled ou failed. Lisez ceci plutôt que le fait que la promesse ait été résolue. `partial` est un état à part entière : certains destinataires l'ont reçu et on ne peut pas le leur reprendre, réessayer est donc une erreur et annoncer un échec est un mensonge.
mode'live' | 'test'- Quel type de clé l'a envoyé. Un envoi de test est enregistré et jamais transmis.
fromstring- L'adresse réellement autorisée et placée sur le réseau, qui n'est pas toujours celle demandée.
subjectstring | null- Tel qu'envoyé.
messageIdstring | null- Le Message-ID RFC 5322. Null tant que le MIME n'existe pas. Le service d'envoi réécrit l'en-tête au départ : aucun bounce ni rapport de distribution ne porte donc cette valeur. C'est sur `id` qu'un événement revient.
threadIdstring | null- Le thread dans lequel il a atterri.
transportstring | null- Par où le message est parti. Null jusqu'à l'expédition.
attemptsnumber- Combien de fois l'expédition a été tentée.
lastErrorstring | null- Pourquoi la dernière tentative a échoué, mot pour mot.
scheduledAtstring | null- L'instant ISO auquel il doit partir.
cancellableUntilstring | null- Tant que l'heure actuelle est antérieure, cancel fonctionne encore.
sentAtstring | null- L'instant ISO auquel il est parti.
tagsRecord<string, string>- Ce que vous avez envoyé, renvoyé tel quel.
sourceEmailSource- composer, api, mcp, ai ou queue : quelle surface a demandé l'envoi. `api`, c'est ce client.
createdAtstring- L'instant ISO auquel l'enregistrement a été écrit.
replayedboolean- Vrai quand une Idempotency-Key correspondait à un envoi qui existait déjà. Rien de nouveau n'a été envoyé, et voici le message d'origine.
translationEmailTranslationResource | undefined- Présent uniquement sur un message qui a été traduit, et uniquement là où toute la requête stockée est transportée : cette réponse et `get`. `{ language, languageName, detectedSourceLanguage, subject, includeOriginal }`, des codes plutôt que des lignes de langue. Une ligne de liste ne le porte jamais : son absence là-bas ne dit rien, ni dans un sens ni dans l'autre.
Dans la langue du destinataire
translate rédige le message dans la langue de quelqu'un d'autre avant son départ. Le corps, et l'objet sauf si vous le désactivez, est traduit au moment où l'API accepte la requête, et ce qui en est sorti est ce qui part : une traduction qui n'a pas pu être produite fait refuser l'envoi plutôt que d'expédier le message dans la langue où vous l'avez écrit.
const email = await openemail.emails.send({ from: '[email protected]', to: '[email protected]', subject: 'Your September invoice', html: '<p>Invoice attached. Payment is due on the 14th.</p>', translate: { to: 'de' },}) console.log(email.translation)// { language: 'de', languageName: 'German', detectedSourceLanguage: 'en', subject: true, includeOriginal: true }Personne n'a relu ce texte avant son départ. emails.translate est le même aller-retour, arrêté une étape plus tôt. Montrez-le à une personne, laissez-la le modifier, puis envoyez ce qu'elle a approuvé sans aucun translate sur l'appel. Le repasser traduirait une deuxième fois et jetterait ses corrections.
const preview = await openemail.emails.translate({ subject: 'Your September invoice', html: '<p>Invoice attached. Payment is due on the 14th.</p>', to: 'de',}) console.log(preview.language.native, preview.detectedSourceLanguage) const approved = await showToSomebody(preview) await openemail.emails.send({ from: '[email protected]', to: '[email protected]', subject: approved.subject, html: approved.html,})import { LANGUAGES, isRtlLanguage, languageByCode, openemail, resolveLanguage } from '@openemail/sdk' LANGUAGES.length // 200 const current = await openemail.languages.list() resolveLanguage('Deutsch')?.code // 'de'resolveLanguage('zh-TW')?.code // 'zh-Hant'languageByCode('DE')?.native // 'Deutsch'isRtlLanguage('ar') // trueLa table est embarquée dans le package, dans l'ordre du sélecteur, pour qu'un sélecteur puisse être rempli avant la première requête. languages.list() renvoie les mêmes lignes depuis le réseau sous forme d'array simple, pour un appelant qui préfère les lignes actuelles à celles livrées avec cette version. resolveLanguage accepte un code, un nom anglais, un endonyme ou un alias (zh-TW est l'alias d'un code qui n'est plus listé), languageByCode fait correspondre un code exact sans tenir compte de la casse, et seize des lignes s'écrivent de droite à gauche. Cherchez dans native, label et code à la fois, affichez native en premier, et stockez le code.
emails.translate n'est pas réessayé automatiquement. Il consomme des appels au modèle et n'écrit rien : il n'y a donc rien à rendre idempotent, et une retentative après une requête restée sans réponse ne ferait qu'acheter deux fois la même réponse.
- Une langue qui ne se résout à rien donne un
validation_errorsurtranslate.to, avant que quoi que ce soit ne soit envoyé. translation_too_longau-delà de 30 000 caractères,translation_not_configuredquand l'installation n'a aucune IA configurée,translation_failedquand le fournisseur n'a pas répondu. Aucun d'eux n'envoie le message non traduit en guise de repli.- Fonctionne avec
template: c'est la sortie RENDUE qui est traduite, si bien qu'un seul corps stocké sert toutes les langues que lisent vos clients. Un template qui rend un document entier conserve son doctype, ses blocs<style>et ses règles@font-face: seul le corps part vers le modèle et le reste est remis autour. Son<title>est laissé tel quel, et rien ne l'affiche de toute façon. - Une retentative ne coûte rien de plus. La traduction ne fait pas partie de l'empreinte d'idempotence (la requête, elle, en fait partie,
translatecompris) : réessayer un envoi resté sans réponse avec la mêmeIdempotency-Keyrejoue donc le message qui existe déjà au lieu d'en traduire et d'en envoyer un second. - Un message traduit en file ou planifié est figé quant à sa formulation.
emails.reschedulele déplace toujours ; changer ce qu'il dit suppose de l'annuler et de l'envoyer à nouveau.
Pièces jointes
content circule en base64. Transmettez des octets et ils sont encodés pour vous.
attachments: [ { filename: 'invoice.pdf', content: pdfBytes, contentType: 'application/pdf' },]toBase64 est exporté si vous en avez besoin ailleurs. Il procède par blocs, ce que btoa(String.fromCharCode(...bytes)) ne fait pas. Celui-ci échoue au-delà d'environ 100 ko, et il échoue sur le vrai fichier, pas sur celui avec lequel vous avez testé.