Envoyer un e-mail
`emails.send` : un message, maintenant ou plus tard.
emails.send
email = client.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: Pathname("invoice.pdf")}], threadId: "CAHk7pQ2x9LmZ4-mail.example.com", scheduledAt: "PT1H", tags: {order: "4021"}, tracking: {opens: true, clicks: true}) puts email[:id], email[:status]to, cc et bcc acceptent un destinataire ou un Array de destinataires, et un destinataire seul est enveloppé pour vous. Chacun peut être une adresse nue, Name <addr@host>, ou un Hash avec email et name.
Le message se passe en arguments nommés, ou en un seul Hash. Des arguments nommés à côté d'un Hash y sont fusionnés et l'emportent quand les deux nomment un champ : client.emails.send(message, subject: "Re: your invoice") change donc un champ d'un message construit plus tôt. Les clés gardent les noms de l'API, c'est pourquoi replyTo et scheduledAt restent en camelCase, alors que idempotency_key: et api_key: sont des options de l'appel et ne font jamais partie du message.
Paramètres
fromString or Hashobligatoire- L'expéditeur. Une adresse nue, `Name <addr@host>`, ou un Hash avec `email` et `name`. Ce doit être une adresse depuis laquelle cette clé peut envoyer, sinon l'appel lève un 403 `from_address_forbidden`. Il n'y a pas d'expéditeur de repli : un envoi nomme donc toujours l'adresse depuis laquelle il part.
toString, Hash or Arrayobligatoire- Un destinataire ou un Array de destinataires, et un destinataire seul est enveloppé pour vous. 50 au maximum pour `to`, `cc` et `bcc` réunis, et au-delà c'est un 422 `too_many_recipients`.
ccString, Hash or Array- Compte dans la limite de 50 destinataires.
bccString, Hash or Array- Jamais nommé dans les octets que reçoivent les autres, car une enveloppe est transmise par destinataire. Compte aussi dans les 50.
replyToString or Hash- Une seule adresse, envoyée comme en-tête Reply-To.
subjectString- 998 caractères au maximum, la limite de ligne de la RFC 5322. Vide par défaut, et un objet vide se rabat sur celui du modèle ou du brouillon.
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. 1 000 000 de caractères au maximum.
textString- La partie en texte brut, 1 000 000 de caractères au maximum.
templateHash- Rend un modèle stocké côté serveur : un Hash avec `id`, qui accepte un id ou un slug, et optionnellement `version` (un Integer), `props` et `slots`. `version` fige une révision. Omettez-la 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, tel qu'il a été écrit. Ne peut pas être combiné avec `template` ou `translate`.
headersHash- Nom d'en-tête vers valeur String, limité à `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority et Feedback-ID. Tout ce que le transport définit lui-même est refusé avec un 422 `reserved_header` plutôt qu'ignoré en silence.
attachmentsArray<Hash>- Chacun est un Hash avec `filename`, `content` et un `contentType` optionnel, ou un Hash avec seulement `fileId`, désignant un fichier déjà présent dans l'espace de travail, comme un fichier issu de `files.upload`. Passez 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.
attachmentDeliveryString- `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.
scheduledAtTime, DateTime or String- Un Time ou un DateTime, envoyé comme un instant ISO 8601 en UTC, un instant ISO 8601 sous forme de String, ou une durée comme `PT1H`. Jusqu'à un an à l'avance, jamais dans le passé. Ne peut pas être combiné avec `cancellableForSeconds`. Une Date Ruby est envoyée comme une date nue, que l'API lit comme minuit UTC ce jour-là : passez donc un Time quand l'heure compte.
cancellableForSecondsInteger- 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.
tagsHash- Jusqu'à 10 libellés, avec des clés de 1 à 64 lettres, chiffres, `_` ou `-` et des valeurs String jusqu'à 256 caractères. Renvoyés tels quels à chaque lecture et jamais interprétés.
signatureBoolean- Indique si ce message porte la signature de l'adresse d'envoi : la sienne, sinon celle du catch-all pour une adresse captée par un catch-all, sinon le pied de page OpenEmail, sauf si cette adresse l'a désactivé. Sans valeur, un corps `html` part exactement tel qu'écrit, sans signature, et un corps uniquement `text` la porte. Indiquez `false` pour le courrier qu'un programme envoie au nom de quelqu'un, comme un reçu, une réinitialisation de mot de passe ou un récapitulatif, dont aucun ne veut de la signature d'une personne. Les envois de modèles et les envois chiffrés n'en portent jamais.
trackingHash- Un Hash avec les Boolean optionnels `opens` et `clicks` : indique s'il faut ajouter un pixel d'ouverture et réécrire les liens pour ce message. Désactivé sauf si le suivi a été activé pour l'adresse d'expédition (ou le catch-all qui l'a attrapée), et chacune des deux clés indiquée ici tranche pour ce seul message, quel que soit le réglage de l'adresse.
translateHash- L'envoie dans la langue du destinataire : un Hash avec `to` et optionnellement `from`, `subject` et `includeOriginal`. `to` accepte un code, un nom anglais ou le nom de la langue dans sa propre langue, et `subject` et `includeOriginal` valent true par défaut. Fixé 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`.
idempotency_keyString- Votre propre clé pour cet envoi, de 1 à 255 caractères parmi les lettres, les chiffres, `_`, `.`, `:` ou `-`. Sans elle, le client génère une clé pour chaque appel, afin que ses propres réessais n'envoient jamais deux fois, et avec elle, un envoi qui s'exécute de nouveau dans un autre processus est rejoué au lieu d'être répété.
api_keyString- Envoie avec cette clé au lieu de celle du client, pour un processus qui envoie pour le compte de plusieurs espaces de travail.
Réponse
Un Hash à clés Symbol : email[:status] lit donc le statut.
idString- L'id de l'envoi, `msg_` suivi de 24 caractères hexadécimaux. Utilisez-le pour `get`, `cancel`, `reschedule` et `get_tracking`.
statusString- queued, scheduled, sending, sent, partial, bounced, cancelled ou failed. Lisez ceci plutôt que le fait que l'appel a retourné : un envoi immédiat est expédié pendant la requête et revient généralement en `sent`, `partial` ou `failed`, et un envoi retenu revient en `queued` ou `scheduled`. `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.
modeString- `live` ou `test` : le type de clé qui l'a envoyé. Un envoi de test est enregistré et jamais transmis. Il affiche `sent`, avec `transport` à `test` : faites donc vos assertions sur la réponse et non sur une boîte de réception.
fromString- L'adresse réellement autorisée et placée sur le réseau, qui n'est pas toujours celle demandée.
subjectString or nil- Tel qu'envoyé.
messageIdString or nil- Le Message-ID RFC 5322. nil 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 or nil- Le thread dans lequel il a atterri.
transportString or nil- Par où le message est parti. nil jusqu'à l'expédition.
attemptsInteger- Combien de fois l'expédition a été tentée.
lastErrorString or nil- Pourquoi la dernière tentative a échoué, mot pour mot.
scheduledAtString or nil- L'instant ISO 8601 auquel il doit partir.
cancellableUntilString or nil- Tant que l'heure actuelle est antérieure, `cancel` fonctionne encore.
sentAtString or nil- L'instant ISO 8601 auquel il est parti.
tagsHash- Ce que vous avez envoyé, renvoyé tel quel.
sourceString- composer, api, mcp, ai ou queue : quelle surface a demandé l'envoi. `api`, c'est ce client.
createdAtString- L'instant ISO 8601 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 dans son état actuel.
translationHash- 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`. Il contient `language`, `languageName`, `detectedSourceLanguage`, `subject` et `includeOriginal`, avec des codes plutôt que des lignes de langue complètes. 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.
email = client.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"}) p email[:translation]email[:translation] vaut alors {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.
preview = client.emails.translate( subject: "Your September invoice", html: "<p>Invoice attached. Payment is due on the 14th.</p>", to: "de") puts preview.dig(:language, :native), preview[:subject], preview[:html]print "Send it as it is? [y/N] " if $stdin.gets.to_s.strip.casecmp?("y") client.emails.send( from: "[email protected]", to: "[email protected]", subject: preview[:subject], html: preview[:html] )endp OpenEmail::LANGUAGES.size current = client.languages.listp current.size p OpenEmail.resolve_language("Deutsch")&.fetch(:code)p OpenEmail.resolve_language("zh-TW")&.fetch(:code)p OpenEmail.language_by_code("DE")&.fetch(:native)p OpenEmail.rtl_language?("ar")Ces lignes affichent 200, le nombre de lignes livrées avec cette version, puis le nombre que l'API contient maintenant, puis "de", "zh-Hant", "Deutsch" et true. La table est embarquée, dans l'ordre du sélecteur, sous la forme OpenEmail::LANGUAGES, un Array gelé de Hashes avec code, label, native, flag et rtl, 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. OpenEmail.resolve_language accepte un code, un nom anglais, un endonyme ou un alias (zh-TW est l'alias d'un code qui n'est plus listé) et renvoie nil quand rien ne correspond, OpenEmail.language_by_code trouve un code exact sans tenir compte de la casse, et seize des lignes s'écrivent de droite à gauche. Cherchez dans native, label et code ensemble, 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 que l'API ne peut pas identifier donne une
validation_errorsurtranslate.to, avant tout envoi. translation_too_longau-delà de 30 000 caractères,translation_not_configuredquand l'installation n'a aucune IA configurée, un 429ai_quota_exceededquand l'espace de travail a épuisé les actions IA du jour (il se réinitialise à minuit UTC et n'est pas réessayé),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 qui est en file d'attente ou planifié garde la formulation approuvée.
emails.reschedulele déplace toujours, tandis queemails.updaterefuse une nouvelle formulation avec un 409translation_locked: changer ce qu'il dit implique donc de l'annuler et de l'envoyer à nouveau.
Pièces jointes
content est en base64 sur le réseau. Passez les octets et ils sont encodés pour vous : une String binaire comme celle que renvoie File.binread, un IO comme un File ouvert, ou un Pathname, qui est lu pour vous.
attachments = [ {filename: "invoice.pdf", content: File.binread("invoice.pdf"), contentType: "application/pdf"}, {filename: "report.pdf", content: Pathname("report.pdf")}, {fileId: "file_6bb640f5b99e47deb758f1f5"}] client.emails.send( from: "[email protected]", to: "[email protected]", subject: "Your documents", text: "Both are attached.", attachments:)Une String marquée comme texte, comme celle que renvoie File.read, est considérée comme déjà en base64, et une String qui n'est pas du base64 lève ArgumentError avant tout envoi. Lisez les fichiers avec File.binread, ou appelez .b sur des octets arrivés marqués comme texte.
OpenEmail.to_base64 est là si vous avez besoin du même encodage ailleurs. Il prend une String binaire, un IO ou un Pathname et renvoie du base64 strict, sans saut de ligne.