Leitfaden für Teams mit E-Mails im Code: mit einem Coding-Agenten erfassen, per MCP auf ein Brew-Canvas holen, dann sicher migrieren und verbessern.

E-Mails im Code sind Produkt-E-Mails, die in deiner Codebasis definiert sind und wie jede andere Funktion ausgeliefert werden. Frag in einem Team, das auf Resend und React Email setzt, welche E-Mails das Produkt verschickt, und es wird kurz still. Die Willkommens-E-Mail ist eine Komponente in emails/. Der Passwort-Reset liegt im Dashboard des Auth-Anbieters. Die E-Mail zum Ende der Testphase ist ein String in einem Cronjob. Den Beleg verschickt Stripe. Und was die E-Mail „Wir vermissen dich“ verschickt, weiß niemand mehr.
Jede einzelne E-Mail war schnell ausgeliefert, deshalb ist niemand für das Gesamtbild zuständig. Wir würden das in dieser Reihenfolge angehen: erst Sichtbarkeit, dann Migration, dann Verbesserung. Das meiste davon kann der Coding-Agent übernehmen, den du schon nutzt, per MCP mit Brew verbunden.
import_email importieren. Der Import ist deterministisch, nutzt keine KI und ist kostenlos.React Email und Resend haben E-Mails für Entwickler deutlich besser gemacht: typisierte Komponenten, geprüft in Pull Requests. Der Preis dafür zeigt sich später.
Die E-Mails sind verstreut. Komponenten, Inline-HTML in einem Hintergrundjob, Vorlagen im Dashboard eines Drittanbieters, E-Mails, die ein anderer Dienst für dich verschickt. Keine Datei listet sie alle auf.
Vorschauen sind nicht die Produktion. Der Vorschauserver von React Email zeigt eine Vorlage mit ihren Vorschau-Props, nicht das, was ein Kunde bekommen hat, und für andere Vorlagen gar nichts.
Nur Entwickler können sie sehen. Produkt, Design und Support müssten jemanden aus der Entwicklung bitten, das Projekt zu starten, also lassen sie es meistens.
Nach dem Launch prüft niemand mehr etwas. Die zwei Jahre alte E-Mail zeigt noch das alte Logo und einen Plan, den du nicht mehr verkaufst. Niemand merkt es, weil niemand die E-Mails nebeneinander sieht.
Das Ziel ist eine gemeinsame Ansicht aller E-Mails. Nichts davon berührt die Produktion.

Jede Phase ist für sich nützlich, und du kannst nach jeder einzelnen aufhören.
Der MCP-Server von Brew liegt unter https://brew.new/api/mcp und nutzt OAuth: Du meldest dich bei Brew an und wählst die Marke, in der der Agent arbeiten darf. In Claude Code:
claude mcp add --transport http brew "https://brew.new/api/mcp"Der Befehl speichert den Server nur in der lokalen Konfiguration dieses Projekts. Ergänze --scope user, damit er in jedem Projekt verfügbar ist. Bevor du die Tools nutzt, führe claude mcp login brew aus oder öffne /mcp und authentifiziere die Verbindung zu Brew.
Die Dokumentation zur Einrichtung der Clients enthält Einträge für Cursor und Codex. Clients ohne OAuth können einen markenbezogenen API-Key nutzen. Lass den Agenten danach get_brew_capabilities aufrufen, damit er die aktuelle Liste der Tools liest, statt zu raten.
Bitte den Agenten um eine Liste:
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.Behalte den Abschnitt „Unknown“. Bestätigungs-E-Mails deines Auth-Anbieters und Belege von Stripe sind keine Versandaufrufe in deinem Code, gehören aber auf die Liste. Committe EMAILS.md. Selbst wenn du hier aufhörst, hast du eine Liste, die du gestern noch nicht hattest.
Das beste Ausgangsmaterial für den Import ist das HTML, das dein Code ohnehin erzeugt. Die Hilfsfunktion zum Rendern von React Email liefert dir diesen String:
import { render } from 'react-email'
import WelcomeEmail from './emails/welcome'
const html = await render(
<WelcomeEmail firstName="Ana" planName="Pro" trialDays={14} />
)Ältere Projekte importieren render aus @react-email/render. Nimm realistische Testdaten statt "test", denn dein Team wird diese E-Mails prüfen. Lass den Agenten ein Skript schreiben, das jede Vorlage in eine Datei rendert.
Brew importiert ein React-Email-Modul auch direkt mit format: "jsx" und liest es dabei statisch, ohne es auszuführen. Props werden zu ihren Standardwerten oder zu PreviewProps aufgelöst, Komponenten, die aus anderen lokalen Dateien importiert werden, fallen weg oder werden abgelehnt, und await und Ähnliches wird zurückgewiesen. Sobald es um mehr als eine in sich geschlossene Datei geht, nimm gerendertes HTML.
Importiere dann jede gerenderte Datei mit import_email, mit einem groupName pro Produktbereich:
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.Entwickler fragen zu Recht, ob der Import ihre E-Mails umschreibt. Das tut er nicht:
assetReport listet alle auf, die er nicht abrufen konnte.baseUrl löst nur relative Bildpfade auf.Jede Gruppe bekommt ein Canvas. Öffne brew.new/emails/onboarding, und die ganze Sequenz liegt nebeneinander, mit einem Umschalter zwischen Desktop und Mobil und Kommentaren zu jedem Design. Canvas-Links funktionieren für alle, die in deinem Brew-Workspace angemeldet sind. Einen öffentlichen Link zum Teilen gibt es nicht, lade also die Leute ein, die prüfen sollen.
Hier zeigen sich die Probleme: zwei Logos, ein toter Link hinter einem Button, drei Footer. Führ audit_email aus, bevor du irgendetwas korrigierst. Das Tool prüft Links, Bilder, Compliance, Ladegröße, Client-Kompatibilität, Barrierefreiheit und Texte, kostet 5 Credits pro vollständiger Prüfung und akzeptiert eine gespeicherte emailId oder rohes HTML. So kannst du auch E-Mails prüfen, die du nicht importiert hast. Unsere QA-Checkliste vor dem E-Mail-Versand erklärt jede Prüfung und jeden Schwellenwert.
Jetzt hast du eine Bestandsaufnahme, jede E-Mail auf einem Canvas und eine Liste mit Problemen, und dein Code versendet weiterhin alles wie bisher.
Jetzt versendet Brew einige E-Mails anstelle deines Codes. Nicht jede E-Mail muss umziehen, und einen bestimmten E-Mail-Typ versendet zu jedem Zeitpunkt nur ein System.
| Heute in deinem Code | In Brew |
|---|---|
resend.emails.send({ react: <WelcomeEmail /> }) |
Ein Trigger-Event plus eine veröffentlichte Automatisierung mit einem Schritt „E-Mail senden“ |
| Props, die an die Komponente übergeben werden | Felder der Trigger-Payload, genutzt als Merge-Tags wie {{ firstName | there }} oder {{ trigger.planName }} |
Die Domain in from |
Eine verifizierte Versanddomain in Brew |
| Deine Nutzertabelle | Kontakte, angelegt über die REST-API oder eine Integration |
| Ein Cronjob, der eine Sequenz versendet | Eine Automatisierung mit den Schritten „Warten“, „Filter“ und „E-Mail senden“ |
Der Agent ruft create_domain für die DNS-Einträge auf und verify_domain, sobald sie aktiv sind. Domains, die per MCP oder API angelegt werden, sind Marketing-Domains. Für Passwort-Resets und Belege richtest du in der Brew-App eine Domain mit dem Versandzweck „Transaktional“ ein: Sie lässt den Abmelde-Footer weg und stellt auch an abgemeldete Kontakte zu. Die Einträge erklärt die Seite Verifying your sending domain in der Dokumentation.
Per MCP kannst du Kontakte aktualisieren, aber nicht anlegen. Leg sie aus deinem Backend mit POST /v1/contacts an, das einen Kontakt oder einen Batch von bis zu 1.000 Kontakten per Upsert anlegt oder aktualisiert und benutzerdefinierte Felder automatisch erstellt, oder lade in der App eine CSV-Datei hoch. Auch das Auslösen eines Triggers legt den Kontakt aus der Payload an oder aktualisiert ihn. Importiere nur Personen, die zugestimmt haben, von dir zu hören. Was Brew dabei erwartet, erklärt die Seite Adding contacts in der Dokumentation.
Leicht zu übersehen: In der importierten Willkommens-E-Mail steht wörtlich „Hi Ana“. Ersetze jeden Testwert durch ein Merge-Tag wie {{ firstName | there }}; der Text nach dem senkrechten Strich ist der Fallback. Nutze dafür den Editor von Brew, oder lass den Agenten edit_email ausführen, beschränkt auf genau diese Ersetzungen. Das ist eine KI-Änderung, die Credits kostet, also prüf die Vorschau. Die Syntax steht auf der Seite Merge tags in der Dokumentation.
Der Agent legt mit save_trigger einen Trigger an. Das Payload-Schema muss die email des Empfängers enthalten. Gib dem Trigger einen typisierten Payload-Vertrag (infer_payload_contract entwirft einen aus einer Beispiel-Payload, set_trigger_contract speichert ihn), und hol ihn dann mit get_trigger_contract im Format ts oder zod ab, damit sich dein Backend und Brew über die Form der Payload einig sind.
Danach baut save_automation den Trigger plus einen Schritt „E-Mail senden“ mit E-Mail, Domain und Betreff. Die Automatisierung bleibt ein Entwurf, bis du sie veröffentlichst. dryRun validiert den Ablauf, test_automation schickt eine echte Kopie an deine Adresse, und check_trigger bestätigt, dass eine veröffentlichte Automatisierung auf den Trigger hört, bevor die Produktion irgendetwas auslöst.
Das ist die einzige Änderung an deinem Anwendungscode. Vorher, mit Resend:
await resend.emails.send({
from: 'Acme <hello@acme.com>',
to: user.email,
subject: 'Welcome to Acme',
react: <WelcomeEmail firstName={user.firstName} planName={plan.name} />,
})Nachher, mit Brew:
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 wird auch dann aufgelöst, wenn Brew das Event ablehnt. Prüf also res.ok und lass deinen Job-Runner einen neuen Versuch starten oder Alarm schlagen. Der Endpunkt antwortet mit 202, wenn er das Event annimmt, und mit 422, wenn keine veröffentlichte Automatisierung auf den Trigger hört.
Bilde den Idempotency-Key aus einer stabilen ID, etwa der des Nutzers oder der Bestellung. Ein wiederholter Key liefert die ursprüngliche Ausführung zurück, statt doppelt zu versenden, und wer einen Key mit einem anderen Body wiederverwendet, bekommt 409 IDEMPOTENCY_CONFLICT. Brew bewahrt den Key einer Auslösung so lange auf wie den Eingangsdatensatz des Triggers, nämlich 90 Tage, nicht 24 Stunden wie viele APIs. Behandle Keys als dauerhaft, einen pro Aktion. Ein Key aus einem Zeitstempel ändert sich bei jedem neuen Versuch, und ein Key, der Wochen später für eine andere Aktion wiederverwendet wird, liefert womöglich die alte Ausführung zurück, statt neu zu versenden.
Produktivcode ruft diesen Endpunkt mit einem API-Key auf, nicht über MCP. Bei OAuth bittet MCP einen Menschen, Live-Versände, Trigger-Auslösungen und Veröffentlichungen zu bestätigen: richtig für einen Chat, falsch für einen Request-Handler. Der Leitfaden zu MCP und REST erklärt die Aufteilung.
EMAILS.md.Der Leitfaden für den Wechsel von Loops folgt demselben Muster.
Bestätigungs-E-Mails und Magic Links von Supabase kommen weiterhin von Supabase. Die Supabase-Integration von Brew hört auf Nutzer-Events, um Automatisierungen zu starten, und ersetzt nicht die E-Mails von Supabase Auth. Eine Rechnung mit Einzelposten, die beim Versand aus komplexen Daten zusammengesetzt wird, ist im Code womöglich ebenfalls einfacher. Prüf also zuerst, was deine Merge-Tags brauchen. Behalte importierte Kopien auf dem Canvas, damit auch diese E-Mails sichtbar bleiben.
Prüf den ganzen Bestand. Führ audit_email für eine ganze Gruppe aus, korrigiere die Befunde und prüf vor großen Versänden erneut.
Prüf echte Clients. test_email_rendering liefert Screenshots aus bestimmten E-Mail-Clients und Geräten, auch im Dunkelmodus, für 10 Credits pro Durchlauf. create_inbox_placement_test schickt eine echte Kopie an Seed-Postfächer und meldet, wo sie gelandet ist, ebenfalls für 10 Credits.
Sorg für Einheitlichkeit. Behebe Unstimmigkeiten im Editor, oder lass den Agenten mit edit_email eine Änderung, etwa einen neuen Footer, auf eine ganze Gruppe anwenden.
Lies die Ergebnisse. get_email_analytics meldet Versände und Engagement. list_insights liefert Insights, die Brew in deinem Konto erkannt hat, jeweils mit den Kennzahlen dahinter.
Teste Varianten bewusst. Erstelle Varianten auf dem Canvas und leite Kontakte mit einem Split-Schritt nach ihrem Verhalten weiter, etwa danach, ob sie geklickt haben. Brew kürt bei einem Zufallssplit keinen Gewinner für dich, also vergleiche die Varianten selbst.
Halte neue E-Mails sichtbar. Ergänze eine Zeile in AGENTS.md oder einer ähnlichen Datei: Neue Produkt-E-Mails werden in Brew erstellt, per Trigger ausgelöst und im selben Pull Request in EMAILS.md eingetragen.
Ja. import_email nimmt gerendertes HTML, MJML, eine gespeicherte .eml-Nachricht oder ein React-Email-Modul an. Am zuverlässigsten ist gerendertes HTML, denn ein Modul wird statisch gelesen: Props bekommen ihre Standard- oder Vorschauwerte, und Komponenten, die aus anderen lokalen Dateien importiert werden, fallen weg.
Nein. Es gibt keine Integration mit Resend, weder für den Import noch für den Export. Du importierst das HTML, das dein Code rendert, und wenn du migrierst, löst dein Code einen Trigger in Brew aus, statt Resend aufzurufen.
Nein. Nutze MCP aus einem Coding-Agenten heraus für Einrichtung, Importe, Prüfungen und Tests. Produktivcode sollte die REST-API mit einem API-Key aufrufen, denn bei OAuth-Verbindungen bittet MCP einen Menschen, Live-Versände und Trigger-Auslösungen zu bestätigen.
Der Import wandelt die E-Mail in ein bearbeitbares Design in Brew um und behält für den Versand das ursprüngliche Quell-HTML. Er entfernt Skripte, iframes, Formulare und Event-Handler, neutralisiert vorhandene Tracking-Pixel und hostet öffentliche Bilder neu. Prüf jede E-Mail auf dem Canvas in der Desktop- und der Mobilansicht, bevor du sie aus Brew versendest.
