Modèles, règles et webhooks
Chaque commande `templates`, `rules` et `webhooks` : des corps enregistrés que vous envoyez par slug, des règles qui classent le courrier entrant, et des événements signés pour votre propre serveur.
Trois espaces de noms
Ces trois espaces de noms permettent à une boîte mail de fonctionner sans que personne ne la surveille. templates stocke des corps que vous envoyez souvent, rules classe le courrier à son arrivée, et webhooks indique à votre propre serveur ce qui s'est passé. Chaque commande est une méthode du SDK sous son nom en kebab-case, donc webhooks.rotateSecret devient openemail webhooks rotate-secret, et elle lit arguments et options comme toute autre commande de ressource.
| Espace de noms | Aussi | Les lectures demandent | Les changements demandent |
|---|---|---|---|
| templates | template | templates:read | templates:write, et aussi emails:send pour send |
| rules | rule | rules:read, test compris | rules:write |
| webhooks | webhook | webhooks:read | webhooks:write, test et replay-delivery compris |
Cette page liste chaque commande et ce qu'il vaut la peine de savoir avant de l'utiliser dans un script. Pour chaque argument et option, avec son type, les scopes nécessaires, son point de terminaison et ce qu'elle renvoie, lancez openemail <namespace> <verb> --help. Ajoutez --json pour obtenir la même page en JSON.
openemail templates --helpopenemail rules create --helpopenemail webhooks replay-delivery --help --jsonModèles
Des corps enregistrés une fois et envoyés souvent, avec des versions, des aperçus et des props typées. Chaque commande qui prend <id-or-slug> accepte l'identifiant tpl_ ou le slug. Le slug ne change jamais quand le modèle est renommé, fixez donc le slug dans vos scripts.
| Commande | Ce qu'il fait |
|---|---|
| openemail templates list | Lister les modèles, du plus récemment mis à jour au plus ancien. --status garde les brouillons, les actifs ou les archivés, --search cherche dans les noms, les slugs et les objets, et --sort choisit l'ordre |
| openemail templates get <id-or-slug> | Lire un modèle avec sa version de tête complète, corps compris |
| openemail templates create --name <value> | Créer un modèle et sa première version. Il reste un brouillon sauf si vous passez --publish, et --starter l'initialise à partir d'un design de départ |
| openemail templates update <id-or-slug> | Modifier le nom, le slug, la description ou le statut, ou le corps du brouillon. Les envois gardent la version publiée jusqu'à ce que vous publiiez |
| openemail templates duplicate <id-or-slug> | Copier la version de tête dans un nouveau modèle, qui démarre comme brouillon |
| openemail templates replace-content <id-or-slug> | Remplacer le corps par celui d'un design de départ (--starter) ou d'un autre modèle (--from-template-id). Demande confirmation |
| openemail templates delete <id-or-slug> | Supprimer un modèle et chacune de ses versions. Demande confirmation |
| openemail templates list-versions <id-or-slug> | Lister les versions, de la plus récente à la plus ancienne, sans leurs corps |
| openemail templates get-version <id-or-slug> <version> | Lire une version avec son corps, sans toucher au brouillon |
| openemail templates publish <id-or-slug> | Publier le brouillon pour que les envois l'utilisent. Publier une version de tête déjà en ligne ne change rien |
| openemail templates restore-version <id-or-slug> <version> | Ramener le corps d'une version plus ancienne comme brouillon. Demande confirmation |
| openemail templates delete-version <id-or-slug> <version> | Supprimer une version. La version en ligne, la version de tête et la seule version sont refusées. Demande confirmation |
| openemail templates list-starters | Lister les designs de départ intégrés |
| openemail templates get-starter <slug> | Lire un design de départ en entier, avec son arbre de blocs et un aperçu rendu |
| openemail templates list-fonts | Lister les polices web qu'un modèle peut charger |
| openemail templates render | Rendre un corps qui n'est enregistré nulle part, à partir de --html ou --document |
| openemail templates preview <id-or-slug> | Rendre un modèle enregistré avec --props et --slots, brouillons compris, sans l'envoyer |
| openemail templates get-analytics <id-or-slug> | Envois, ouvertures et clics sur une période, par jour, par source et par version |
| openemail templates list-sends <id-or-slug> | Les messages individuels envoyés par le modèle, du plus récent au plus ancien, page par page |
| openemail templates send <id-or-slug> --from <value> --to <a,b> | Envoyer un e-mail rendu à partir de la version publiée, ou de celle que fixe --template-version |
Un modèle a une version de tête, qui est un brouillon tant qu'elle contient des modifications non publiées, et une version publiée, celle qu'utilise un envoi sans --template-version. create sans --publish, une modification du corps avec update, replace-content et restore-version écrivent tous le brouillon, donc les destinataires ne voient rien de nouveau avant publish.
- Un modèle archivé refuse l'envoi avec
template_archived.publishle rend à nouveau actif. - Un espace de travail contient au plus 200 modèles, archivés compris, donc la suppression est le seul moyen de faire de la place.
deleteest refusé avectemplate_in_usetant qu'une diffusion programmée ou en file d'attente nomme encore le modèle.
Règles
Des conditions et des actions évaluées sur le courrier entrant, dans l'ordre qu'affiche rules list. Une règle n'agit que sur le courrier qui arrive pendant qu'elle est activée. Aucune commande n'applique une règle au courrier déjà présent dans la boîte mail, et rules test est le moyen de voir ce qu'elle attraperait. Les identifiants de règle commencent par rul_.
| Commande | Ce qu'il fait |
|---|---|
| openemail rules list | Lister les règles dans l'ordre où elles s'exécutent. --enabled ou --no-enabled garde un seul type |
| openemail rules get <id> | Lire une règle, avec matchCount et lastMatchedAt |
| openemail rules create --name <value> --conditions <json|@file|-> --actions <json|@file|-> | Créer une règle à la fin de l'ordre. Elle est activée sauf si vous passez --no-enabled |
| openemail rules update <id> | Modifier une règle. --conditions et --actions remplacent toute la liste, et --position ne déplace que cette règle |
| openemail rules delete <id> | Supprimer une règle. Ce qu'elle a déjà fait reste dans list-runs. Demande confirmation |
| openemail rules reorder <rule-ids...> | Fixer l'ordre de toutes les règles d'un coup, en nommant chaque règle exactement une fois |
| openemail rules test <id> | Simuler une règle sur le courrier déjà présent dans la boîte mail. Cela ne change rien, et fonctionne sur une règle désactivée |
| openemail rules list-runs | Ce que les règles ont réellement fait au courrier entrant, du plus récent au plus ancien. --rule-id et --thread-id filtrent la liste |
--conditions est une liste d'objets { field, op, value }, reliés par --match all ou --match any, où value est toujours une chaîne et negate: true inverse une condition. --actions est une liste d'objets { type, value }, appliqués dans l'ordre. Une règle prend de 1 à 20 conditions et de 1 à 10 actions, et une boîte mail contient au plus 100 règles.
- Champs de condition :
from,from_domain,envelope_from,to,cc,bcc,recipient,reply_to,delivered_to,subject,body,header,list_id,attachment_name,attachment_type,has_attachment,attachment_size,message_size,spam,houretweekday. - Opérateurs :
matches,contains,equals,starts_with,ends_with,gtetlt.gtetltne fonctionnent que sur les champs numériques, ethas_attachmentetspamn'acceptent queequalsavectrueoufalse. - Types d'action :
label,remove_label,archive,mark_read,star,spam,trash,forward,reply,block_senderetreject.labeletremove_labelprennent un identifiant de libellé commeUSER_RECEIPTS,forwardprend une adresse etreplyun identifiant ou un slug de modèle. from_domaincorrespond aussi aux sous-domaines, ethouretweekdaysont lus en UTC, avec0pour dimanche.- Une règle avec une action
rejectdoit aussi testerenvelope_from, sinon elle est refusée avecreject_needs_envelope.
Webhooks
Des points de terminaison sur votre propre serveur qui reçoivent des événements signés de la boîte mail, avec leurs secrets de signature, leur journal de livraison et un journal d'audit de chaque changement. Les identifiants de point de terminaison commencent par whe_ et ceux de livraison par whd_.
| Commande | Ce qu'il fait |
|---|---|
| openemail webhooks list | Lister les points de terminaison de l'espace de travail, du plus récent au plus ancien, avec leur état de santé |
| openemail webhooks get <id> | Lire un point de terminaison. Le secret de signature ne fait jamais partie d'une lecture |
| openemail webhooks create --url <value> | Enregistrer un point de terminaison HTTPS. Affiche le secret de signature, la seule fois où vous le voyez |
| openemail webhooks update <id> | Modifier l'URL, les événements, les listes d'autorisation ou l'activation. Chaque liste remplace celle qui est stockée |
| openemail webhooks delete <id> | Supprimer un point de terminaison et son journal de livraison. Demande confirmation |
| openemail webhooks rotate-secret <id> | Émettre un nouveau secret de signature. L'ancien cesse aussitôt de fonctionner. Demande confirmation |
| openemail webhooks test <id> | Envoyer un événement email.sent synthétique signé et rendre compte de la livraison |
| openemail webhooks list-deliveries <id> | Les tentatives de livraison d'un point de terminaison, de la plus récente à la plus ancienne. --status, --since et --until les filtrent |
| openemail webhooks get-delivery <id> <delivery-id> | Une tentative en entier : le corps envoyé, la réponse de votre serveur, chaque essai de l'événement, et si un rejeu serait accepté |
| openemail webhooks replay-delivery <id> <delivery-id> | Renvoyer maintenant un événement stocké au point de terminaison |
| openemail webhooks list-workspace-deliveries | Les tentatives de livraison sur tous les points de terminaison, ou ceux que nomme --endpoint-ids |
| openemail webhooks list-activity <id> | Le journal d'audit d'un point de terminaison : qui l'a créé, modifié, testé, rejoué ou retiré |
| openemail webhooks list-workspace-activity | Le journal d'audit de tous les points de terminaison, retirés compris |
Omettez --event-types et un point de terminaison reçoit l'ensemble par défaut, les événements email.* autres que email.replied. email.replied, les événements domain.* et les événements suppression.* ne l'atteignent que si vous les nommez. --address-allowlist et --domain-allowlist limitent un point de terminaison à certaines adresses ou certains domaines, comme elles limitent une clé API.
- Un espace de travail contient 10 points de terminaison, sauf si le support a relevé sa limite.
- Un point de terminaison qui échoue à 100 livraisons de suite est désactivé par le serveur, et
webhooks update <id> --enabledle rétablit. - Avec une connexion par navigateur, seul le propriétaire de l'espace de travail peut lire une livraison avec
get-delivery. Tous les autres reçoiventowner_onlyet le code de sortie4.
Vérifier un modèle, puis le publier
templates preview rend exactement ce que produirait un envoi avec les mêmes valeurs, brouillons compris, et ne demande que templates:read, donc même une clé en lecture seule peut la lancer. Elle signale une prop obligatoire manquante comme un avertissement là où send la refuserait, faites donc échouer le build au moindre avertissement. publish est sans risque à chaque déploiement, car publier une version de tête déjà en ligne ne change rien.
draft=$(openemail templates get order-shipped --json | jq .latestVersion)openemail templates preview order-shipped --template-version "$draft" \ --props '{"orderId":"AC-4192","customer":"Ada"}' --json | jq -e '.warnings == []'openemail templates publish order-shippedEnvoyer depuis un modèle
Fixez la version, pour qu'une réécriture publiée demain ne change pas ce qu'envoie ce code, et passez une clé d'idempotence tirée de ce qui a déclenché l'envoi, pour qu'une nouvelle tentative après une réponse perdue rejoue le premier message au lieu d'en envoyer un second. --dry-run affiche la méthode, l'URL, les en-têtes avec votre identifiant masqué et le corps, n'envoie rien et sort avec le code 0. Relancez sans --dry-run pour envoyer.
openemail templates send order-shipped \ --from 'Acme <[email protected]>' \ --to [email protected] \ --template-version 5 \ --props '{"orderId":"AC-4192","customer":"Ada"}' \ --idempotency-key order-shipped:AC-4192 \ --dry-runTester une règle avant qu'elle ne s'exécute
Créez la règle désactivée, simulez-la sur le courrier récent, et activez-la une fois qu'elle attrape ce que vous vouliez. Avec une connexion par navigateur, rules create et rules update demandent un code de vérification, qu'un script ne peut pas taper, lancez donc d'abord openemail verify. Pendant les 60 minutes suivantes, ce profil les exécute sans rien demander.
[ { "field": "from_domain", "op": "equals", "value": "stripe.com" }, { "field": "has_attachment", "op": "equals", "value": "true" }][ { "type": "label", "value": "USER_RECEIPTS" }, { "type": "archive" }]openemail verifyrule=$(openemail rules create --name 'Stripe receipts' \ --conditions @conditions.json --actions @actions.json --no-enabled --json | jq -r .id)openemail rules test "$rule" --days 30 --limit 100openemail rules update "$rule" --enabledLisez les avertissements de rules test avant ses correspondances. field_unevaluable signifie qu'une condition lit quelque chose que le courrier stocké ne contient plus, si bien que le test n'a pas pu en juger, et forward_unverified signifie qu'une cible de transfert n'est pas hébergée ici. wouldApply liste ce que la règle déclare : un transfert vers une adresse qui n'a pas confirmé échoue quand même quand du vrai courrier arrive.
Placer une règle en premier, et voir pourquoi un message a été déplacé
rules reorder prend chaque règle de la boîte mail exactement une fois. Une règle omise ou nommée deux fois est refusée et rien ne bouge. rules list renvoie les identifiants dans l'ordre où elles s'exécutent, placez donc celle que vous voulez en premier devant les autres.
first=rul_4f1c9a2b7d3e8f6a0b5c1d2eopenemail rules reorder "$first" $(openemail rules list --all --ndjson \ | jq -r --arg first "$first" 'select(.id != $first) | .id')openemail rules list-runs --thread-id CAHk7pQ2x9LmZ4 --json | jq '.items[] | {ruleName, actions, failures}'list-runs est le relevé de ce qui s'est réellement passé. Chaque ligne est une règle correspondant à un message, avec les actions qui ont pris effet et, dans failures, celles que la boîte mail a déclinées, comme une réponse à un expéditeur à qui l'on a déjà répondu ce jour-là. Chaque ligne garde le nom qu'avait la règle à ce moment-là, donc --rule-id fonctionne pour une règle supprimée depuis.
Enregistrer un webhook et prouver qu'il fonctionne
webhooks create montre le secret de signature une fois, et aucune commande ultérieure ne le remontre. Avec --json, il figure dans le JSON sur stdout, tandis que le rappel de l'enregistrer part sur stderr, donc la sortie reste analysable. webhooks test envoie un événement email.sent synthétique signé, quels que soient les abonnements du point de terminaison, et aucun courrier n'est envoyé.
openemail verifyopenemail webhooks create --url https://hooks.acme.com/openemail \ --event-types email.received,email.bounced,email.complained \ --description 'Support desk sync' --json > endpoint.jsonjq -r .secret endpoint.jsonopenemail webhooks test "$(jq -r .id endpoint.json)" --json | jq .deliveryrm endpoint.jsonMettez le secret dans votre coffre à secrets avant de supprimer le fichier. test sort avec le code 0 même quand votre serveur échoue, lisez donc delivery.status : delivered pour une réponse 2xx et failed pour tout le reste, redirection comprise, puisque les redirections ne sont jamais suivies. Un responseCode à null signifie qu'aucune réponse n'est arrivée.
Trouver les livraisons en échec et en renvoyer une
Après une panne de votre côté, listez ce qui a échoué sur tous les points de terminaison, vérifiez qu'un rejeu serait accepté, et renvoyez l'événement. Un rejeu porte le même identifiant d'événement, donc un récepteur qui écarte les identifiants déjà traités le considère comme l'événement qu'il connaît.
openemail webhooks list-workspace-deliveries --status failed --since 2026-09-26T00:00:00Z --all --ndjson \ | jq -r '[.endpointId, .id, .eventType, (.responseCode // "no answer")] | @tsv'openemail webhooks get-delivery whe_3f9c2a7b1e4d8f60a5c7b92d whd_8c1e4a7f2b9d3e6a0c5f1b28 --json | jq .replayRefusalopenemail webhooks replay-delivery whe_3f9c2a7b1e4d8f60a5c7b92d whd_8c1e4a7f2b9d3e6a0c5f1b28--sinceet--untilprennent un instant ISO 8601.- Une ligne en échec dont
nextAttemptAtcontient une heure a encore une nouvelle tentative automatique à venir. replayRefusalvautnullquand un rejeu partirait, et nomme sinon la raison du refus, commewebhook_disabledtant que le point de terminaison est désactivé.- Les rejeux se font un événement à la fois. Aucune commande ne renvoie toutes les livraisons en échec.
Codes de vérification
Avec une connexion par navigateur, quatre de ces commandes demandent un code de vérification avant de changer quoi que ce soit, comme le fait l'application web : rules create, rules update, webhooks create et webhooks update. On ne demande jamais de code à une clé API. Toutes les autres commandes de cette page s'exécutent sans code, suppressions et webhooks rotate-secret comprises.
- Dans un terminal, la CLI vous envoie par e-mail un code à six chiffres, ou en demande un de votre application d'authentification quand la connexion à deux facteurs est activée, puis exécute la commande une fois.
- Sans surveillance, avec
--jsonou--no-input, en CI ou sans terminal, personne ne peut taper le code, donc la commande s'arrête avec le code de sortie4et ne change rien. Lancez d'abordopenemail verify, et le profil n'a besoin d'aucun code pendant 60 minutes. --yesconfirme une suppression, mais ne saute jamais un code.
Confirmations et simulations
Sept commandes ici retirent ou écrasent quelque chose, elles demandent donc d'abord confirmation : templates delete, templates delete-version, templates replace-content, templates restore-version, rules delete, webhooks delete et webhooks rotate-secret. Sans surveillance, chacune s'arrête avec le code de sortie 2 sauf si vous passez --yes.
$ openemail webhooks delete whe_3f9c2a7b1e4d8f60a5c7b92d --no-input✗ Refusing to run unattended. Pass --yes to confirm.$ openemail webhooks delete whe_3f9c2a7b1e4d8f60a5c7b92d --yes--dry-run affiche la première requête qui changerait quelque chose et sort avec le code 0, sans l'envoyer ni vous demander de confirmer. Avec --json, il affiche un seul document { dryRun, request }. rules test, templates render et templates preview ne changent rien, mais ce sont des requêtes POST, donc une simulation les affiche au lieu de les exécuter.
Pagination
templates list,templates list-versions,rules list,rules list-runset chaque commandewebhooks list…lisent une page à la fois, 25 lignes sauf si--limiten demande jusqu'à 100. Un terminal montre le--cursorà passer pour la page suivante.--alllit chaque page,--max <n>s'arrête après autant de lignes, et--ndjsonaffiche un objet JSON par ligne. Avec--json, une liste affiche un seul document{ items, hasMore, nextCursor },--allcompris.- Renvoyez un curseur avec les mêmes filtres et le même tri que ceux avec lesquels il est venu. Tout le reste est refusé comme
invalid_cursor, avec le code de sortie7. templates list-sendspagine plutôt par numéro, avec--pageet--page-size, indiquetotalet n'a pas de--all. Les numéros de page se décalent pendant que du courrier part, restreignez donc la période avec--daysou--minutesplutôt que de paginer loin.templates list-startersettemplates list-fontsrenvoient tout le catalogue d'un coup, etrules reorderrenvoie chaque règle sous forme de simple liste dans son nouvel ordre.- Une boîte mail contient au plus 100 règles, donc
rules list --limit 100renvoie toujours chaque règle sur une seule page.
Des options qui méritent un second regard
--template-versionest le champversiondu corps, renommé parce que--versionaffiche la version de la CLI. L'argument<version>deget-version,restore-versionetdelete-versionest un numéro de version, pas un identifianttplv_.--conditions,--actions,--document,--slots,--propset les autres options JSON prennent du JSON en ligne, depuis un fichier avec@path, ou depuis stdin avec-.--dataprend tout le corps de la même façon, et toute option passée en plus remplace sa clé.--htmlprend le balisage lui-même, pas un fichier, donc--html @page.htmlenvoie le texte@page.html. Passez--html "$(cat page.html)", ou mettezhtmldans le fichier que vous donnez à--data.rules update --conditionset--actionsremplacent toute la liste, tout commewebhooks update --event-types,--address-allowlistet--domain-allowlist. Lisez la valeur actuelle, modifiez-la, et envoyez-la en entier.- Un
--event-typesvide est une erreur d'utilisation. Pour remettre un point de terminaison sur l'ensemble par défaut, envoyez--data '{"eventTypes":[]}', et pour arrêter ses livraisons, passez--no-enabled. --expected-versionsurtemplates update,replace-contentetrestore-versionprend la version de tête que vous avez lue. Quand quelqu'un d'autre a déplacé la tête entre-temps, la commande s'arrête avec le code de sortie6etversion_conflict, et n'écrit rien.rules update <id> --no-enableddésactive une règle et garde sa place dans l'ordre, c'est ainsi qu'on met une règle en pause sans la supprimer.