Aller à la documentation
CLI

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 nomsAussiLes lectures demandentLes changements demandent
templatestemplatetemplates:readtemplates:write, et aussi emails:send pour send
rulesrulerules:read, test comprisrules:write
webhookswebhookwebhooks:readwebhooks: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.

Aide
openemail templates --helpopenemail rules create --helpopenemail webhooks replay-delivery --help --json

Modè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.

CommandeCe qu'il fait
openemail templates listLister 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-startersLister 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-fontsLister les polices web qu'un modèle peut charger
openemail templates renderRendre 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. publish le 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.
  • delete est refusé avec template_in_use tant 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_.

CommandeCe qu'il fait
openemail rules listLister 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-runsCe 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, hour et weekday.
  • Opérateurs : matches, contains, equals, starts_with, ends_with, gt et lt. gt et lt ne fonctionnent que sur les champs numériques, et has_attachment et spam n'acceptent que equals avec true ou false.
  • Types d'action : label, remove_label, archive, mark_read, star, spam, trash, forward, reply, block_sender et reject. label et remove_label prennent un identifiant de libellé comme USER_RECEIPTS, forward prend une adresse et reply un identifiant ou un slug de modèle.
  • from_domain correspond aussi aux sous-domaines, et hour et weekday sont lus en UTC, avec 0 pour dimanche.
  • Une règle avec une action reject doit aussi tester envelope_from, sinon elle est refusée avec reject_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_.

CommandeCe qu'il fait
openemail webhooks listLister 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-deliveriesLes 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-activityLe 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> --enabled le 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çoivent owner_only et le code de sortie 4.

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.

CI
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-shipped

Envoyer 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.

Terminal
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-run

Tester 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.

conditions.json
[  { "field": "from_domain", "op": "equals", "value": "stripe.com" },  { "field": "has_attachment", "op": "equals", "value": "true" }]
actions.json
[  { "type": "label", "value": "USER_RECEIPTS" },  { "type": "archive" }]
Terminal
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" --enabled

Lisez 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.

Terminal
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é.

Terminal
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.json

Mettez 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.

Terminal
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
  • --since et --until prennent un instant ISO 8601.
  • Une ligne en échec dont nextAttemptAt contient une heure a encore une nouvelle tentative automatique à venir.
  • replayRefusal vaut null quand un rejeu partirait, et nomme sinon la raison du refus, comme webhook_disabled tant 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 --json ou --no-input, en CI ou sans terminal, personne ne peut taper le code, donc la commande s'arrête avec le code de sortie 4 et ne change rien. Lancez d'abord openemail verify, et le profil n'a besoin d'aucun code pendant 60 minutes.
  • --yes confirme 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.

Terminal
$ 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-runs et chaque commande webhooks list… lisent une page à la fois, 25 lignes sauf si --limit en demande jusqu'à 100. Un terminal montre le --cursor à passer pour la page suivante.
  • --all lit chaque page, --max <n> s'arrête après autant de lignes, et --ndjson affiche un objet JSON par ligne. Avec --json, une liste affiche un seul document { items, hasMore, nextCursor }, --all compris.
  • 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 sortie 7.
  • templates list-sends pagine plutôt par numéro, avec --page et --page-size, indique total et n'a pas de --all. Les numéros de page se décalent pendant que du courrier part, restreignez donc la période avec --days ou --minutes plutôt que de paginer loin.
  • templates list-starters et templates list-fonts renvoient tout le catalogue d'un coup, et rules reorder renvoie 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 100 renvoie toujours chaque règle sur une seule page.

Des options qui méritent un second regard

  • --template-version est le champ version du corps, renommé parce que --version affiche la version de la CLI. L'argument <version> de get-version, restore-version et delete-version est un numéro de version, pas un identifiant tplv_.
  • --conditions, --actions, --document, --slots, --props et les autres options JSON prennent du JSON en ligne, depuis un fichier avec @path, ou depuis stdin avec -. --data prend tout le corps de la même façon, et toute option passée en plus remplace sa clé.
  • --html prend le balisage lui-même, pas un fichier, donc --html @page.html envoie le texte @page.html. Passez --html "$(cat page.html)", ou mettez html dans le fichier que vous donnez à --data.
  • rules update --conditions et --actions remplacent toute la liste, tout comme webhooks update --event-types, --address-allowlist et --domain-allowlist. Lisez la valeur actuelle, modifiez-la, et envoyez-la en entier.
  • Un --event-types vide 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-version sur templates update, replace-content et restore-version prend 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 sortie 6 et version_conflict, et n'écrit rien.
  • rules update <id> --no-enabled dé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.

Votre boîte de réception,
à vos conditions.

L’infrastructure e-mail pour les entreprises, l’IA, les agents et le courrier personnel. Conçue pour l’échelle, la confidentialité et le contrôle. Tout ce que l’e-mail aurait dû avoir dès le premier jour.

OpenEmail

L’infrastructure e-mail pour les entreprises, l’IA, les agents et le courrier personnel. Conçue pour l’échelle, la confidentialité et le contrôle. Tout ce que l’e-mail aurait dû avoir dès le premier jour.

© 2026 OpenEmail. Tous droits réservés.