Aller à la documentation
API

Envoyer aux audiences

Envoie un message à tous les membres d'une ou plusieurs audiences, sous forme de copie distincte pour chaque personne et personnalisée à partir de chaque contact. Chaque copie a exactement un destinataire et ni cc ni bcc, si bien que personne ne voit à qui d'autre elle est partie, et chaque copie est un e-mail ordinaire avec son propre identifiant `msg_`, ses événements, son suivi et ses webhooks. L'appel répond `202` tout de suite et l'envoi se poursuit en arrière-plan : suivez-le avec `GET /broadcasts/{id}`.

POSTapi.openemail.uk/broadcasts

Exécute le véritable appel sur votre espace de travail, avec votre propre clé.

POST /broadcasts

Envoie un message à tous les membres d'une ou plusieurs audiences, sous forme de copie distincte pour chaque personne et personnalisée à partir de chaque contact. Chaque copie a exactement un destinataire et ni cc ni bcc, si bien que personne ne voit à qui d'autre elle est partie, et chaque copie est un e-mail ordinaire avec son propre identifiant msg_, ses événements, son suivi et ses webhooks. L'appel répond 202 tout de suite et l'envoi se poursuit en arrière-plan : suivez-le avec GET /broadcasts/{id}.

Exemple

Exige emails:send et audiences:read. audienceIds contient de 1 à 10 identifiants. Le corps vient de html et/ou text, ou d'un template enregistré, jamais des deux, et subject est obligatoire sauf si le modèle le fournit.

curl
curl -X POST "$OE/broadcasts" -H "$AUTH" -H 'content-type: application/json' -d '{  "audienceIds": ["aud_4c1b8e2a7d9f05c36b4e8a71"],  "from": "Acme <[email protected]>",  "subject": "{{firstName|Hello}}, the September release is out",  "html": "<p>Hi {{firstName|there}},</p><p>Here is what changed this month.</p>",  "text": "Hi {{firstName|there}}, here is what changed this month. Unsubscribe: {{unsubscribeUrl}}",  "tags": { "campaign": "release-2026-09" },  "scheduledAt": "PT2H"}'
Réponse
{  "object": "broadcast",  "id": "brd_5a8c1e3f7b2d94a06c8e1f3b",  "status": "scheduled",  "mode": "live",  "source": "api",  "audienceIds": ["aud_4c1b8e2a7d9f05c36b4e8a71"],  "from": "Acme <[email protected]>",  "subject": "{{firstName|Hello}}, the September release is out",  "counts": {    "recipients": 412,    "created": 0,    "skipped": 0,    "failedToQueue": 0,    "queued": 0,    "sending": 0,    "sent": 0,    "failed": 0,    "cancelled": 0  },  "lastError": null,  "scheduledAt": "2026-09-23T14:00:00.000Z",  "startedAt": null,  "completedAt": null,  "cancelledAt": null,  "createdAt": "2026-09-23T12:00:00.000Z",  "updatedAt": "2026-09-23T12:00:00.000Z",  "replayed": false}

La réponse est queued, ou scheduled avec scheduledAt, qui accepte un instant ISO 8601 ou une durée comme PT2H, au plus 365 jours à l'avance. counts.recipients est l'estimation prise à cet instant, et les autres compteurs partent de 0. L'en-tête Location désigne la diffusion.

Réessayable sans risque avec un en-tête Idempotency-Key : la même clé répond 200 avec la diffusion créée par le premier appel et Idempotency-Replayed: true, et la même clé avec un autre corps donne un 422 idempotency_key_reuse. Sans clé, envoyer deux fois le même corps envoie la diffusion deux fois.

Les copies ne sont pas classées dans le dossier Envoyés, car la diffusion en fait foi. GET /emails?broadcastId=brd_5a8c1e3f7b2d94a06c8e1f3b les liste, une par personne.

Qui la reçoit

Chaque contact d'au moins une des audiences, compté une fois quel que soit le nombre d'audiences qui le contiennent. Deux sortes de contacts sont exclues : celui qui s'est désabonné de chacune des audiences choisies dont il fait partie, et celui dont l'adresse figure sur la liste de suppression après un rebond ou une plainte, ou parce que quelqu'un l'y a ajoutée. Un contact ajouté à l'une des audiences après l'appel, mais avant que l'envoi ne l'atteigne, est inclus.

L'envoi parcourt les audiences 50 personnes à la fois et confie chaque copie au même circuit que POST /emails, si bien que chaque copie est réessayée, suivie et rapportée comme n'importe quel autre message. POST /broadcasts/preview renvoie le nombre dont partirait cet appel, sans rien envoyer.

L'envoi entier est confronté aux envois mensuels de la formule avant toute écriture. Une diffusion que le quota ne peut pas couvrir est refusée avec 429 send_quota_exceeded et ne laisse rien derrière elle. Chaque copie compte pour un envoi.

Champs de fusion

subject, html et text sont remplis pour chaque personne. Chaque champ accepte une valeur de repli après une barre, utilisée quand le contact n'a pas de valeur pour lui : {{firstName|there}} devient donc "there" pour un contact enregistré sans nom. Les valeurs sont échappées dans html, les espaces entre les accolades sont permis, et tout autre {{…}} est laissé exactement tel qu'il est écrit.

ChampRempli avec
`{{firstName}}`Le premier mot du nom du contact.
`{{lastName}}`Le reste du nom du contact après le premier mot.
`{{name}}`Le nom complet du contact.
`{{email}}`L'adresse à laquelle part la copie.
`{{unsubscribeUrl}}`Le lien qui désabonne cette personne de ces audiences.

Avec template à la place d'un corps, les cinq mêmes valeurs sont transmises comme props, mais seulement les props que le modèle déclare. Un modèle qui déclare firstName le reçoit, et un prop qu'il ne déclare pas n'est jamais envoyé, si bien que les copies n'échouent jamais sur un prop inconnu. Tout ce que vous mettez dans template.props va à l'identique dans chaque copie.

Désabonnement

Chaque copie porte List-Unsubscribe et List-Unsubscribe-Post: List-Unsubscribe=One-Click. C'est ce qui permet à un client de messagerie d'afficher son propre bouton de désabonnement, et ce que les grands fournisseurs de boîtes exigent du courrier en masse.

Un corps html ou text qui ne place pas {{unsubscribeUrl}} lui-même reçoit un pied de page d'une ligne : "You are receiving this because you are on this mailing list. Unsubscribe". Un modèle est envoyé exactement tel quel : mettez donc {{unsubscribeUrl}} dans le modèle.

Le lien ouvre une page avec un bouton Se désabonner, si bien qu'un analyseur de liens qui le récupère ne désabonne personne, tandis que la requête en un clic d'un client de messagerie désabonne immédiatement. Dans les deux cas, la personne est marquée désabonnée dans chaque audience à laquelle cette diffusion est partie, ce qui apparaît comme unsubscribedAt sur GET /audiences/{id}/contacts. Ses autres audiences, son contact et le courrier qui lui est envoyé un message à la fois ne sont pas touchés.

Refus

StatutCodeQuand
403from_address_forbiddenLa clé ne peut pas envoyer en tant que from.
404audience_not_foundUn identifiant de audienceIds ne désigne aucune audience de cet espace de travail.
409domain_not_sendableLe domaine de from ne peut pas encore signer de courrier, comme pour POST /emails.
422no_recipientsLes audiences sont vides, ou tous leurs membres se sont désabonnés ou sont supprimés.
422invalid_parameterPas de corps, html ou text à côté de template, pas de subject sans modèle, plus de 10 audiences ou de 8 étiquettes, ou un scheduledAt qui n'est pas dans le futur ou qui dépasse 365 jours.
422template_not_foundLe modèle ne se résout pas. Les autres refus liés au modèle désignent aussi template.*.
422capability_unsupportedLa clé est limitée à certaines adresses. Les audiences appartiennent à tout l'espace de travail.
429send_quota_exceededLa formule ne peut pas couvrir une copie pour tout le monde ce mois-ci.

Pas de pièces jointes, de cc, de bcc, de traduction ni de chiffrement. tags en accepte jusqu'à 8, et chaque copie porte aussi broadcast_id, ajouté par le serveur.