> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.goodflag.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.goodflag.com/_mcp/server.

# Templates email

> Cascade de réglages, variables, gabarits Handlebars

# Messages & notifications

La plateforme écrit à vos destinataires sans que votre application ait à s'en charger :
invitation à signer, relance, refus, parapheur terminé… Ces messages sont **les vôtres** aux
yeux du signataire, et ils constituent souvent le **premier contact** entre votre organisation
et lui. Ils se personnalisent entièrement — et, comme indiqué en *Positionnement & périmètre*,
c'est l'une des rares opérations qui ne se font **que par l'API**.

Cette fondation établit où vivent ces réglages, comment ils se résolvent d'un niveau à l'autre,
et ce que vous pouvez écrire dans un gabarit.

> **Qui envoie ces messages ?** La **plateforme**, avec son propre expéditeur et sa propre
> infrastructure d'envoi — pas la vôtre. Personnaliser un gabarit change donc le **contenu** du
> message, non son adresse d'expédition ni son acheminement. Deux conséquences : vos réglages de
> délivrabilité (SPF, DKIM, filtres antispam de votre domaine) ne s'appliquent **pas** à ces
> notifications, qui bénéficient de ceux de la plateforme ; et les e-mails que votre application
> envoie de son côté — création de compte, confirmations — restent entièrement à votre charge.
> Ce sont deux chaînes distinctes.

## Une cascade à trois niveaux

Les réglages ne sont pas un objet unique mais une **superposition**. Chaque niveau ne porte que
ce qu'il **surcharge** ; le résultat effectif se lit à part, sous le nom d'`appliedSettings`.

| Niveau          | Ce que vous lisez / écrivez                                                                       | Le résultat résolu                                                                                        |
| --------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **Plateforme**  | `GET /api/defaultSettings` — les réglages par défaut, communs à tous les tenants (lecture seule)  | —                                                                                                         |
| **Tenant**      | `GET` / `PUT /api/tenants/{tenantId}/settings` — vos surcharges                                   | `GET /api/tenants/{tenantId}/appliedSettings` = *les réglages par défaut surchargés par ceux du tenant*   |
| **Utilisateur** | `GET` / `PUT /api/users/{userId}/settings` — les surcharges propres à un utilisateur              | `GET /api/users/{userId}/appliedSettings` = *les réglages du tenant surchargés par ceux de l'utilisateur* |
| **Parapheur**   | `GET /api/workflows/{workflowId}/settings` — **lecture seule** : ce qui s'applique à ce parapheur | —                                                                                                         |

Ces réglages par défaut ne sont pas neutres : le Workflow Manager étant **multi-tenants**
(voir *Positionnement & périmètre*), ils sont **définis par Goodflag** pour l'ensemble des
tenants de la plateforme. Votre tenant vous est livré en mode SaaS avec ces messages-là — déjà
rédigés, déjà traduits, immédiatement fonctionnels. Vous ne partez donc jamais d'une page
blanche : vous surchargez un existant qui tient debout, et c'est aussi ce qui rend la branche
« sinon » d'un gabarit conditionnel sûre à utiliser (voir section [*Précautions avant d'écrire*](#precautions-avant-decrire)).

**Le piège à connaître.** `settings` et `appliedSettings` ne répondent pas à la même question.
Le premier dit *« qu'ai-je surchargé ? »* — il peut être quasi vide alors que tout fonctionne.
Le second dit *« que reçoit réellement mon destinataire ? »*. Pour diagnostiquer un message qui
ne ressemble pas à ce que vous attendiez, c'est **`appliedSettings`** qu'il faut lire, et au
niveau pertinent : un réglage d'utilisateur peut expliquer à lui seul un écart que vous
cherchiez au niveau du tenant.

> Le niveau **parapheur** est en lecture seule : on ne configure pas les messages parapheur par
> parapheur. Pour différencier les messages selon le type d'affaire, on s'appuie sur une
> **métadonnée** portée par le parapheur et sur un gabarit conditionnel — voir la fondation
> *Métadonnées* et les cas d'usage.

## Ce que contiennent les réglages

L'essentiel des réglages est constitué des **gabarits de message**. Chacun se décline **par
langue** (`fr`, `en`, et `de` pour certains), et porte deux champs : `subject` et `content`.

**Invitations et relances**

| Clé                        | Message                                                               |
| -------------------------- | --------------------------------------------------------------------- |
| `signatureInviteEmail`     | Invitation à signer.                                                  |
| `approbationInviteEmail`   | Invitation à valider.                                                 |
| `multiInviteEmail`         | Invitation groupée, lorsque plusieurs sollicitations sont regroupées. |
| `signatureUninviteEmail`   | La signature n'est plus attendue.                                     |
| `approbationUninviteEmail` | La validation n'est plus attendue.                                    |

**Issue d'une étape**

| Clé                        | Message                 |
| -------------------------- | ----------------------- |
| `signatureFinishedEmail`   | Signature enregistrée.  |
| `approbationFinishedEmail` | Validation enregistrée. |
| `signatureRefusedEmail`    | Signature refusée.      |
| `approbationRefusedEmail`  | Validation refusée.     |

**Cycle de vie du parapheur**

| Clé                                 | Message                                                |
| ----------------------------------- | ------------------------------------------------------ |
| `workflowStartedEmail`              | Parapheur démarré.                                     |
| `workflowFinishedEmail`             | Parapheur terminé.                                     |
| `workflowStoppedEmail`              | Parapheur interrompu.                                  |
| `workflowFinishedDownloadLinkEmail` | Terminé, avec le lien de téléchargement des documents. |

**Collaboration et divers**

| Clé                                                  | Message                                                                                                                                    |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `coManagerAddedEmail` / `coManagerRemovedEmail`      | Cogestionnaire ajouté ou retiré.                                                                                                           |
| `commentCreatedEmail` / `commentCreatedSecuredEmail` | Commentaire déposé (variante sécurisée).                                                                                                   |
| `anonymizedUsersInStepEmail`                         | Utilisateurs anonymisés dans une étape.                                                                                                    |
| `rolesTranslationInEmail`                            | **Exception** : pas de `subject`/`content`, mais les libellés des rôles cités dans les messages (`manager`, `comanager`, `watcher`, `cc`). |

À côté des gabarits, `preferredLocales` fixe la ou les langues retenues.

> Personnaliser l'invitation sans toucher aux autres messages produit une expérience
> incohérente si ces autres messages partent effectivement. Deux réponses possibles : habiller
> les gabarits d'une même famille, ou **réduire le nombre de messages émis** (voir les réglages
> de notification du parapheur, fondation [*Parapheurs*](/wm/guides/integration/creer-un-parapheur)).

## Écrire un gabarit

La référence ne nomme pas le moteur de gabarit. La syntaxe employée par les gabarits par
défaut — `{{variable}}`, `{{{variable}}}`, `{{#if}} … {{else}} … {{/if}}`, assistants à
paramètres nommés — est celle de la famille **Handlebars**, et c'est sur cette base que nous
raisonnons ici.

Le contexte offert au gabarit s'organise en **quatre objets**. Ce qui suit a été établi en
envoyant une invitation réelle dont le corps listait chaque chemin candidat :

| Objet              | Ce qu'il porte                                  | Chemins vérifiés                                              |
| ------------------ | ----------------------------------------------- | ------------------------------------------------------------- |
| **`workflow`**     | Le parapheur lui-même, **avec ses métadonnées** | `name`, `id`, `workflowStatus`, `created`, `data1` … `data16` |
| **`recipient`**    | Le destinataire du message                      | `email`, `firstName`, `lastName`, `organizationId`            |
| **`step`**         | L'étape en cours                                | `stepType`, `validityPeriod`, `invitePeriod`                  |
| **`notification`** | L'événement qui déclenche l'envoi               | `workflowName`, `creatorName`, `linkUrl`, `stepType`          |

S'y ajoute `log.reason`, le motif saisi lors d'un refus, et l'assistant
`{{durationToDays duration=… singular=… plural=… zero=…}}`.

**L'organisation : l'identifiant seulement.** `recipient.organizationId` rend bien l'identifiant
`org_…` de l'organisation au nom de laquelle le destinataire signe — à condition qu'elle ait été
portée **sur le destinataire** à la création du parapheur, l'héritage depuis la fiche ne la
transmettant pas (voir la fondation [*Provisionnement*](/wm/api-reference/concepts/provisionnement-des-utilisateurs)). En revanche le **nom** de l'organisation
et la **fonction** ne sont pas exposés : `recipient.organization`, `organizationName`,
`organizationTitle`, `title` et `organization.name` rendent tous une chaîne vide. Pour la
fonction, le constat va plus loin que le gabarit : envoyée sur le destinataire à la création, elle
**n'est pas restituée** lorsqu'on relit le parapheur (voir la fondation [*Provisionnement*](/wm/api-reference/concepts/provisionnement-des-utilisateurs)).

Conséquence pratique : on peut **aiguiller** selon l'organisation — `{{#eq recipient.organizationId
'org_…'}}` — mais pas **afficher** son nom ni la fonction du signataire. Le libellé lisible doit
être écrit dans la branche correspondante du gabarit. Pour faire figurer l'organisation et la
fonction sur le **document**, c'est le cartouche de signature qu'il faut employer
(`{{signerOrganization}}`, `{{signerOrganizationTitle}}` — fondation [*Profils de signature*](/wm/api-reference/concepts/profils-de-signature)).

**Chemins qui n'existent pas** — vérifié : `signer.*` et `user.*` ne rendent rien, alors que
`recipient.email` fonctionne ; `notification.recipientName` non plus. Pour désigner le
destinataire, c'est bien `recipient` qu'il faut employer. De même, le préfixe est **obligatoire** :
`{{data10}}` seul ne rend rien, il faut `{{workflow.data10}}`.

> **Deux formats à connaître avant d'insérer une valeur brute.** Les durées sont en
> **millisecondes** (`validityPeriod` rend `604800000` pour sept jours) — d'où l'assistant
> `durationToDays`, qui existe précisément pour cela. Et les dates sortent dans un format long à
> l'anglaise (`Sun Aug 02 14:38:32 CEST 2026`), qui n'est ni ISO ni localisé en français : à
> insérer tel quel, on obtient un message peu soigné.

### Insérer une variable, ou conditionner sur elle

Deux usages qu'il faut distinguer, car ils n'obéissent pas aux mêmes contraintes.

**Insérer** est sans difficulté : toute variable du contexte peut être écrite dans le sujet ou
dans le corps — le nom du parapheur, celui de son créateur, la durée de validité de l'étape.
C'est ce que font les gabarits par défaut, et c'est ce qui permet d'adresser un message
circonstancié.

**Conditionner** se fait de deux façons, qui répondent à deux questions différentes.

**Sur la présence** — `{{#if …}}` teste qu'une valeur est renseignée, sans regarder laquelle.
C'est la bonne réponse à la question *« ce parapheur est-il le mien ? »* : une variable toujours
présente, comme le nom du parapheur, serait vraie pour tout le monde et ne distinguerait rien. Une
**métadonnée** fait au contraire un discriminant parfait, parce que c'est la seule valeur dont
vous maîtrisez à la fois la présence *et* l'absence — renseignée sur vos parapheurs, vide sur ceux
des autres applications du tenant.

**Sur la valeur** — un assistant de comparaison est disponible, sous deux formes équivalentes :

```handlebars
{{#eq workflow.data10 'partenaire'}} … {{else}} … {{/eq}}
{{#if (eq workflow.data10 'partenaire')}} … {{else}} … {{/if}}
```

Cela répond à la question suivante : *« de quelle catégorie relève-t-il ? »*. Un seul emplacement
« type d'entité » suffit alors à s'adresser différemment à un client, un partenaire, un
fournisseur ou un auditeur — au lieu d'en consommer un par catégorie, ce qui compterait vite sur
seize. En revanche `{{#compare}}` n'existe pas : son emploi est refusé dès l'écriture.

En pratique, les deux se combinent : `{{#if …}}` sur la métadonnée pour n'affecter que vos
parapheurs, puis une comparaison à l'intérieur pour choisir le ton selon la catégorie.

> ⚠️ **Cet assistant n'est pas documenté par la référence** — qui ne nomme ni le moteur de
> gabarit, ni ses assistants. Son fonctionnement a été vérifié, mais rien ne garantit sa
> stabilité dans une version future. Écrivez donc la branche `{{else}}` de façon qu'elle tienne
> seule : si la comparaison venait à ne plus être reconnue, votre message resterait correct.

**Deux accolades ou trois ?** En **double** accolade, la valeur est **échappée pour le HTML** :
une apostrophe devient `&#x27;`, une esperluette `&amp;`. En **triple**, elle est insérée telle
quelle. Le bon choix dépend de **l'endroit où vous écrivez**.

**Dans le sujet** — qui est du texte brut, jamais interprété comme du HTML — l'échappement n'a
aucune vertu et se voit : un sujet contenant `{{notification.workflowName}}` en double accolade
affiche « Bulletin d'adhésion — … » chez le destinataire. Employez donc la **triple**
accolade dans les sujets ; le risque d'injection y est nul, faute de HTML à injecter.

**Dans le corps HTML** — l'échappement protège : une valeur venue d'un tiers (un nom, un titre de
parapheur, une métadonnée) pourrait sinon y insérer des balises. Restez en **double** accolade,
avec une exception : une **URL** placée dans un attribut `href` se met en **triple**, sinon les
`&` de sa chaîne de requête deviennent `&amp;` et le lien casse.

## Précautions avant d'écrire

**Les gabarits appartiennent au tenant, pas à votre application.** Si votre tenant sert
plusieurs usages, écraser un gabarit impose votre charte à tout le monde. Le moyen d'y échapper
est un gabarit **conditionnel** dont la condition porte sur une valeur présente sur vos seuls
parapheurs — en pratique une métadonnée, pour la raison exposée au section [*Écrire un gabarit*](#ecrire-un-gabarit) : la branche « sinon »
restitue alors aux autres applications le gabarit d'origine, celui fourni par Goodflag.

**Repartez toujours des réglages en vigueur.** Avant d'écrire, relisez `GET /api/defaultSettings`
pour la branche par défaut et les réglages actuels du tenant pour tout ce que vous ne modifiez
pas — puis renvoyez l'ensemble. Composer une nouvelle version à partir de votre propre version
précédente conduit, à la longue, à emboîter des conditions les unes dans les autres et à faire
de votre charte le défaut de tous.

**La plateforme valide vos gabarits à l'écriture.** Un contenu qu'elle ne sait pas analyser est
refusé par le `PUT` lui-même, avec un `400` et le code **`InvalidEmailTemplate`** (*An email
template could not be parsed*). C'est une garantie appréciable : **on ne peut pas mettre ses
notifications en panne** en publiant un gabarit mal formé — une expression inconnue, une balise
de bloc non refermée, un assistant qui n'existe pas sont écartés avant d'atteindre le moindre
destinataire. Corollaire pratique : pour savoir si une syntaxe est acceptée, il suffit de tenter
l'écriture, sans avoir à provoquer d'envoi.

**Vérifiez le résultat au bon endroit.** Après un `PUT`, relisez `appliedSettings` — et non vos
`settings` — pour constater ce que recevront vos destinataires.

\*\[Handlebars] : langage de gabarit utilisé par les messages — variables `{{…}}`, conditions `{{#if}}`
\*\[tenant] : espace client isolé dans la plateforme, avec ses réglages, ses groupes et ses pages de consentement
\*\[gabarit] : modèle de message, composé d'un sujet et d'un contenu, déclinés par langue