# E-mails dans le code : les voir, les migrer, les améliorer

URL canonique : https://brew.new/fr/blog/emails-in-code-visibility-migration-mcp
Rédigé par Thomas Park
Publication : 2026-10-12
Mise à jour : 2026-10-15

Les e-mails dans le code, ce sont les e-mails produit définis dans votre base de code et livrés comme n’importe quelle autre fonctionnalité. Dans une équipe qui s’appuie sur Resend et React Email, demandez quels e-mails le produit envoie : un silence s’installe. L’e-mail de bienvenue est un composant dans `emails/`. La réinitialisation du mot de passe se trouve dans le tableau de bord du fournisseur d’authentification. L’e-mail de fin d’essai est une chaîne de caractères dans un job cron. Stripe envoie le reçu. Et personne ne se souvient de ce qui envoie l’e-mail « Vous nous manquez ».

Chaque e-mail a été facile à livrer, si bien que personne n’a la vue d’ensemble. Nous procéderions dans l’ordre : la visibilité, puis la migration, puis l’amélioration. L’agent de code que vous utilisez déjà peut s’en charger en grande partie, une fois connecté à Brew via MCP.

## À retenir

- Commencez par un inventaire, pas par un nouvel outil : chaque e-mail, son déclencheur, son fichier et ses variables.
- Un agent de code peut repérer les appels d’envoi, faire le rendu de chaque modèle et l’importer avec l’outil MCP `import_email`. L’import est déterministe, n’utilise pas d’IA et est gratuit.
- Les e-mails importés arrivent sur un canevas par groupe : l’équipe les relit côte à côte, sur ordinateur et sur mobile.
- Pour migrer, un envoi à la Resend devient un déclencheur associé à une automatisation publiée, que l’on actionne d’un seul appel REST. Le code de production utilise REST, pas MCP.
- Migrez un type d’e-mail à la fois derrière un feature flag, et l’e-mail transactionnel en dernier.

## Pourquoi les e-mails définis dans le code deviennent invisibles

React Email et Resend ont nettement amélioré l’e-mail pour les développeurs : des composants typés, relus dans des pull requests. Le prix à payer apparaît plus tard.

**Les e-mails sont éparpillés.** Des composants, du HTML écrit en dur dans un job en arrière-plan, des modèles dans le tableau de bord d’un tiers, des e-mails qu’un autre service envoie pour vous. Aucun fichier ne les recense tous.

**Les aperçus ne reflètent pas la production.** Le serveur d’aperçu de React Email affiche un modèle avec ses props d’aperçu, pas ce qu’un client a reçu, et rien des autres modèles.

**Seuls les développeurs peuvent les voir.** Le produit, le design et le support devraient demander à un développeur de lancer le projet ; la plupart du temps, ils s’en abstiennent.

**Rien n’est relu après le lancement.** L’e-mail d’il y a deux ans affiche toujours l’ancien logo et une offre que vous ne vendez plus. Personne ne le remarque, car personne ne voit les e-mails tous ensemble.

## Étape 1 : visibilité

L’objectif : une vue partagée de tous les e-mails. À cette étape, rien ne touche à la production.

![Les trois phases pour sortir les e-mails du code : la visibilité, avec import_email et un canevas par groupe ; la migration, avec les domaines, les contacts, les déclencheurs et les automatisations ; et l’optimisation, avec les audits, les tests de rendu et les statistiques.](/images/blog/emails-in-code-three-phases.png)

*Chaque phase est utile en soi, et vous pouvez vous arrêter après n’importe laquelle.*

### Connectez votre agent de code à Brew

Le serveur MCP de Brew se trouve à l’adresse `https://brew.new/api/mcp` et utilise OAuth : vous vous connectez avec Brew et choisissez la marque dans laquelle l’agent peut travailler. Dans Claude Code :

```bash
claude mcp add --transport http brew "https://brew.new/api/mcp"
```

Cette commande n’enregistre le serveur que dans la configuration locale de ce projet. Pour l’utiliser dans tous vos projets, ajoutez `--scope user`. Avant d’utiliser ses outils, lancez `claude mcp login brew`, ou ouvrez `/mcp` et authentifiez la connexion Brew.

La [documentation de configuration des clients](https://docs.brew.new/api-reference/mcp/connect-your-client) couvre aussi Cursor et Codex. Les clients sans OAuth peuvent utiliser une clé API limitée à une marque. Demandez ensuite à l’agent d’appeler `get_brew_capabilities` pour qu’il lise la liste à jour des outils au lieu de la deviner.

### Dressez l’inventaire

Demandez une liste à l’agent :

```text
Find every email this codebase sends. Search for provider calls
(resend.emails.send, postmark, sendgrid, nodemailer, SES), React Email
components and render() calls, and any email templates referenced from
config or a third-party dashboard.

For each email, record: a name, what triggers it, the file and line that
sends it, the template file, the provider, and the variables it uses.
Write the result as a table in EMAILS.md. List anything you could not
resolve under "Unknown" instead of guessing.
```

Gardez la section « Unknown » (les cas non résolus). Les confirmations d’authentification et les reçus Stripe ne sont pas des appels d’envoi dans votre code, mais ils ont leur place dans la liste. Commitez `EMAILS.md`. Si vous vous arrêtez là, vous disposez déjà d’une liste que vous n’aviez pas la veille.

### Faites le rendu de chaque modèle avec des données réalistes

La meilleure source pour l’import, c’est le HTML que votre code produit déjà. L’[utilitaire render](https://react.email/docs/utilities/render) de React Email vous fournit cette chaîne :

```tsx
import { render } from 'react-email'
import WelcomeEmail from './emails/welcome'

const html = await render(
  <WelcomeEmail firstName="Ana" planName="Pro" trialDays={14} />
)
```

Les projets plus anciens importent `render` depuis `@react-email/render`. Utilisez des données de test réalistes, pas `"test"`, puisque votre équipe va relire ces e-mails. Demandez à l’agent un script qui fait le rendu de chaque modèle dans un fichier.

Brew importe aussi directement un module React Email avec `format: "jsx"`, en le lisant de façon statique, sans l’exécuter. Les props prennent leurs valeurs par défaut ou celles de `PreviewProps`, les composants importés depuis d’autres fichiers locaux sont ignorés ou rejetés, et `await` et les constructions similaires sont refusés. Au-delà d’un fichier unique et autonome, utilisez le HTML rendu.

### Importez tout au même endroit

Importez ensuite chaque rendu avec `import_email`, en indiquant un `groupName` par grande partie du produit :

```text
For each rendered email in ./email-renders, call import_email with
format "html", the title and subject line from EMAILS.md, and a
groupName by area: "Auth", "Onboarding", "Billing" or "Lifecycle".
Report the emailId for each one and any warnings in its assetReport.
```

Les développeurs se demandent, à juste titre, si l’import réécrit leurs e-mails. Ce n’est pas le cas :

- C’est un compilateur déterministe, pas un modèle d’IA, et il ne consomme aucun crédit.
- Il produit un design Brew modifiable, tandis que l’envoi conserve le HTML source d’origine pour les rangées importées.
- Il réhéberge les images publiques, et l’`assetReport` liste celles qu’il n’a pas pu récupérer.
- Il supprime les scripts, les iframes, les formulaires et les gestionnaires d’événements, et neutralise les pixels de suivi des ouvertures déjà présents.
- Chaque source peut peser jusqu’à 5 Mo. L’outil n’accepte pas d’URL : il prend le balisage lui-même, et `baseUrl` sert uniquement à résoudre les chemins d’images relatifs.

### Relisez les e-mails en équipe

Chaque groupe a son canevas. Ouvrez `brew.new/emails/onboarding` : toute la séquence s’affiche côte à côte, avec une bascule ordinateur/mobile et des commentaires sur chaque design. Les liens vers le canevas fonctionnent pour les personnes connectées à votre espace de travail Brew. Il n’existe pas de lien de partage public : invitez donc vos relecteurs.

C’est là que les problèmes apparaissent : deux logos, un bouton au lien mort, trois pieds de page. Lancez `audit_email` avant de corriger quoi que ce soit. Il contrôle les liens, les images, la conformité, le poids chargé, la compatibilité avec les clients de messagerie, l’accessibilité et les textes, coûte 5 crédits par audit complet, et accepte un `emailId` enregistré ou du HTML brut : vous pouvez donc auditer des e-mails que vous n’avez pas importés. Notre [checklist de contrôle qualité avant envoi](/fr/blog/pre-send-email-qa-checklist) détaille chaque contrôle et chaque seuil.

Vous disposez maintenant d’un inventaire, de tous vos e-mails sur un canevas et d’une liste de problèmes, et votre code envoie toujours tout comme avant.

## Étape 2 : migration

Désormais, Brew envoie certains e-mails à la place de votre code. Tous les e-mails n’ont pas besoin de migrer, et à tout moment, un seul système envoie un type d’e-mail donné.

### Faites correspondre les concepts

| Aujourd’hui dans votre code | Dans Brew |
| --- | --- |
| `resend.emails.send({ react: <WelcomeEmail /> })` | Un événement déclencheur associé à une automatisation publiée qui comporte une étape « Envoyer un e-mail » |
| Les props passées au composant | Les champs du payload du déclencheur, utilisés comme balises de fusion, par exemple `{{ firstName \| there }}` ou `{{ trigger.planName }}` |
| Le domaine du `from` | Un domaine d’envoi vérifié dans Brew |
| Votre table d’utilisateurs | Les contacts, créés via l’API REST ou une intégration |
| Un job cron qui envoie une séquence | Une automatisation avec des étapes « Attente », « Filtre » et « Envoyer un e-mail » |

### 1. Configurez le domaine d’envoi

L’agent appelle `create_domain` pour obtenir les enregistrements DNS, puis `verify_domain` une fois qu’ils sont en ligne. Les domaines créés via MCP ou l’API sont des domaines marketing. Pour les réinitialisations de mot de passe et les reçus, configurez dans l’application Brew un domaine à usage transactionnel : il omet le pied de page de désabonnement et envoie aussi aux contacts désabonnés. La documentation sur la [vérification de votre domaine d’envoi](https://docs.brew.new/get-started/verify-your-sending-domain) détaille les enregistrements.

### 2. Importez vos contacts

MCP met à jour les contacts mais ne les crée pas. Créez-les depuis votre backend avec `POST /v1/contacts`, qui crée ou met à jour un contact ou un lot de 1 000 contacts au maximum et crée automatiquement les champs personnalisés, ou importez un CSV dans l’application. Actionner un déclencheur crée ou met aussi à jour le contact présent dans son payload. N’importez que des personnes qui ont accepté d’avoir de vos nouvelles. La documentation sur l’[ajout de contacts](https://docs.brew.new/audience/add-contacts) explique ce que Brew attend.

### 3. Remplacez les valeurs d’aperçu par des balises de fusion

Un détail facile à rater : l’e-mail de bienvenue importé dit littéralement « Hi Ana ». Remplacez chaque valeur de test par une balise de fusion comme `{{ firstName | there }}`, où le texte après la barre verticale est la valeur par défaut. Utilisez l’éditeur de Brew, ou demandez à l’agent de lancer `edit_email` en se limitant à ces remplacements. C’est une modification par IA qui consomme des crédits : vérifiez donc l’aperçu. La page sur les [balises de fusion](https://docs.brew.new/create-emails/merge-tags) détaille la syntaxe.

### 4. Créez le déclencheur et l’automatisation

L’agent crée un déclencheur avec `save_trigger`. Le schéma du payload doit inclure l’`email` du destinataire. Donnez-lui un contrat typé (`infer_payload_contract` en rédige un à partir d’un exemple de payload, `set_trigger_contract` l’enregistre), puis récupérez-le avec `get_trigger_contract` au format `ts` ou `zod` pour que votre backend et Brew s’accordent sur la forme du payload.

`save_automation` construit ensuite le déclencheur et une étape « Envoyer un e-mail » avec l’e-mail, le domaine et l’objet. L’automatisation reste à l’état de brouillon jusqu’à ce que vous la publiiez. `dryRun` valide le graphe, `test_automation` envoie une copie réelle à votre adresse, et `check_trigger` confirme qu’une automatisation publiée est à l’écoute avant que la production n’actionne quoi que ce soit.

### 5. Remplacez l’appel dans votre code

C’est la seule modification de votre code applicatif. Avant, avec Resend :

```ts
await resend.emails.send({
  from: 'Acme <hello@acme.com>',
  to: user.email,
  subject: 'Welcome to Acme',
  react: <WelcomeEmail firstName={user.firstName} planName={plan.name} />,
})
```

Après, avec Brew :

```ts
const res = await fetch(
  `https://brew.new/api/v1/automations/triggers/${process.env.BREW_WELCOME_TRIGGER_ID}/fire`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.BREW_API_KEY}`,
      'Content-Type': 'application/json',
      'Idempotency-Key': `welcome-${user.id}`,
    },
    body: JSON.stringify({
      payload: { email: user.email, firstName: user.firstName, planName: plan.name },
    }),
  }
)

if (!res.ok) {
  // 422 means the automation is still a draft; 400 means the payload failed the schema.
  throw new Error(`Brew trigger failed: ${res.status} ${await res.text()}`)
}
```

`fetch` se résout même quand la requête est refusée : vérifiez donc `res.ok` et laissez votre gestionnaire de jobs réessayer ou déclencher une alerte. L’endpoint renvoie `202` quand il accepte l’événement et `422` si aucune automatisation publiée n’écoute le déclencheur.

Construisez l’`Idempotency-Key` à partir d’un identifiant stable, comme celui de l’utilisateur ou de la commande. Une clé répétée renvoie l’exécution d’origine au lieu d’envoyer deux fois, et réutiliser une clé avec un corps différent renvoie `409 IDEMPOTENCY_CONFLICT`. Brew conserve la clé d’un déclenchement aussi longtemps que son enregistrement de réception, soit 90 jours, et non les 24 heures de nombreuses API. Considérez les clés comme permanentes, une par action. Une clé fondée sur un horodatage change à chaque nouvelle tentative, et une clé réutilisée pour une autre action des semaines plus tard peut être rejouée au lieu d’être envoyée.

Le code de production appelle cet endpoint avec une clé API, pas via MCP. Sur une connexion OAuth, MCP demande à une personne de confirmer les envois réels, les déclenchements et les publications : parfait dans une conversation, inadapté à un gestionnaire de requêtes. Le [guide MCP ou REST](/fr/blog/mcp-vs-rest-api-for-email-marketing-which-workflow-should-you-use) détaille cette répartition.

### 6. Basculez un e-mail à la fois

1. Placez le nouvel appel derrière un feature flag, à côté de l’ancien, pour que revenir en arrière ne soit qu’un changement de configuration.
2. Commencez par un e-mail à faible risque, comme une relance de cycle de vie, et surveillez la délivrance et les clics pendant quelques jours.
3. Migrez ensuite les séquences, une fois que leurs déclencheurs sont actionnés avec les bons payloads.
4. Migrez l’e-mail transactionnel en dernier. Ce sont les réinitialisations de mot de passe et les liens magiques cassés que les utilisateurs remarquent en premier.
5. Supprimez l’ancien chemin après une semaine sans incident, et mettez à jour `EMAILS.md`.

Le [guide de migration depuis Loops](https://docs.brew.new/migrations/loops) suit la même logique.

### Ce qui doit rester dans le code

Les e-mails de confirmation et de lien magique de Supabase continuent de partir de Supabase. L’intégration Supabase de Brew écoute les événements utilisateur pour lancer des automatisations, sans remplacer les e-mails de Supabase Auth. Une facture détaillée, construite à partir de données complexes au moment de l’envoi, peut aussi rester plus simple dans le code : vérifiez d’abord ce dont vos balises de fusion auraient besoin. Gardez des copies importées sur le canevas pour que ces e-mails restent visibles.

## Étape 3 : optimisation

**Auditez l’ensemble.** Lancez `audit_email` sur tout un groupe, corrigez, puis relancez-le avant les gros envois.

**Vérifiez sur de vrais clients de messagerie.** `test_email_rendering` renvoie des captures d’écran issues de clients de messagerie et d’appareils précis, mode sombre compris, pour 10 crédits par exécution. `create_inbox_placement_test` envoie une copie réelle à des boîtes de réception témoins et indique où elle a atterri, pour 10 crédits également.

**Harmonisez l’ensemble.** Corrigez les incohérences dans l’éditeur, ou demandez à l’agent d’appliquer une modification, comme un nouveau pied de page, à tout un groupe avec `edit_email`.

**Lisez les résultats.** `get_email_analytics` rend compte des envois et de l’engagement. `list_insights` renvoie les constats que Brew a détectés dans votre compte, chacun avec les métriques sur lesquelles il repose.

**Testez des variantes de façon délibérée.** Créez des variantes sur le canevas et orientez les contacts avec une étape « Embranchement » fondée sur leur comportement, par exemple s’ils ont cliqué. Brew ne désigne pas pour vous le gagnant d’une répartition aléatoire : comparez donc les variantes vous-même.

**Gardez les nouveaux e-mails visibles.** Ajoutez une ligne à `AGENTS.md` ou à un fichier équivalent : les nouveaux e-mails produit sont créés dans Brew, envoyés par un déclencheur et ajoutés à `EMAILS.md` dans la même pull request.

## Questions fréquentes

### Brew peut-il importer des modèles React Email ?

Oui. `import_email` accepte du HTML rendu, du MJML, un message `.eml` enregistré ou un module React Email. Le HTML rendu est l’option la plus fiable, car un module est lu de façon statique : les props prennent leurs valeurs par défaut ou d’aperçu, et les composants importés depuis d’autres fichiers locaux sont laissés de côté.

### Brew se connecte-t-il à mon compte Resend ?

Non. Il n’existe ni intégration, ni import, ni export Resend. Vous importez le HTML que produit votre code et, si vous migrez, votre code actionne un déclencheur Brew au lieu d’appeler Resend.

### Mon backend doit-il appeler Brew via MCP ?

Non. Utilisez MCP depuis un agent de code pour la configuration, les imports, les audits et les tests. Le code de production doit appeler l’API REST avec une clé API, car sur les connexions OAuth, MCP demande à une personne de confirmer les envois réels et les déclenchements.

### L’import modifie-t-il l’apparence d’un e-mail ?

L’import convertit l’e-mail en design Brew modifiable et conserve le HTML source d’origine pour l’envoi. Il retire les scripts, les iframes, les formulaires et les gestionnaires d’événements, neutralise les pixels de suivi existants et réhéberge les images publiques. Relisez chaque e-mail sur le canevas, en vue ordinateur et en vue mobile, avant de l’envoyer depuis Brew.
