Een gids voor teams met e-mails in code: inventariseer ze met een coding agent, zet ze via MCP op één canvas in Brew, en migreer en verbeter ze daarna veilig.

E-mails in code zijn productmails die in je codebase zijn gedefinieerd en die je uitrolt zoals elke andere functie. Vraag in een team dat op Resend en React Email bouwt welke e-mails het product verstuurt, en het blijft even stil. De welkomstmail is een component in emails/. De wachtwoordreset staat in het dashboard van de auth-provider. De e-mail over het einde van de proefperiode is een string in een cronjob. Stripe verstuurt het betaalbewijs. Niemand weet meer wat de e-mail “we missen je” verstuurt.
Elke e-mail was makkelijk uit te rollen, dus niemand overziet het geheel. Wij zouden dat in deze volgorde aanpakken: eerst zichtbaarheid, dan migratie, dan verbetering. De coding agent die je al gebruikt, kan het meeste daarvan doen, gekoppeld aan Brew via MCP.
import_email. De import is deterministisch, gebruikt geen AI en is gratis.React Email en Resend hebben e-mail voor developers een stuk beter gemaakt: getypeerde componenten, beoordeeld in pull requests. De prijs betaal je later.
De e-mails staan verspreid. Componenten, inline HTML in een achtergrondjob, sjablonen in het dashboard van een derde partij, e-mails die een andere service voor je verstuurt. Geen enkel bestand zet ze allemaal op een rij.
Een preview is geen productie. De previewserver van React Email toont één sjabloon met de bijbehorende preview-props, niet wat een klant heeft ontvangen, en niets van de andere sjablonen.
Alleen engineers kunnen kijken. Product, design en support moeten een engineer vragen om het project te draaien, dus meestal doen ze dat niet.
Na de lancering kijkt niemand er meer naar. De e-mail van twee jaar oud heeft nog het oude logo en een pakket dat je niet meer verkoopt. Niemand merkt het, omdat niemand ze bij elkaar ziet.
Het doel is één gedeeld overzicht van elke e-mail. Niets hiervan raakt productie.

Elke fase is op zichzelf nuttig, en je kunt na elke fase stoppen.
De MCP-server van Brew staat op https://brew.new/api/mcp en gebruikt OAuth: je logt in bij Brew en kiest het merk waarin de agent mag werken. In Claude Code:
claude mcp add --transport http brew "https://brew.new/api/mcp"Daarmee sla je de server alleen op in de lokale configuratie van dit project. Voeg --scope user toe om hem in elk project te gebruiken. Voer claude mcp login brew uit, of open /mcp en authenticeer je bij Brew, voordat je de tools van Brew gebruikt.
De documentatie over het instellen van clients behandelt ook Cursor en Codex. Clients zonder OAuth kunnen een API-sleutel voor één merk gebruiken. Laat de agent daarna get_brew_capabilities aanroepen, zodat hij de actuele lijst met tools leest in plaats van te gokken.
Vraag de agent om een lijst:
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.Laat de sectie “Unknown” staan. Auth-bevestigingen en betaalbewijzen van Stripe zijn geen verzendaanroepen in je code, maar ze horen wel op de lijst. Commit EMAILS.md. Stop je hier, dan heb je in elk geval een lijst die je gisteren nog niet had.
De beste input voor de import is de HTML die je code al produceert. De render-utility van React Email geeft je die string:
import { render } from 'react-email'
import WelcomeEmail from './emails/welcome'
const html = await render(
<WelcomeEmail firstName="Ana" planName="Pro" trialDays={14} />
)Oudere projecten importeren render uit @react-email/render. Gebruik realistische fixtures, niet "test", want je team gaat deze e-mails beoordelen. Laat de agent een script schrijven dat elk sjabloon naar een bestand rendert.
Brew importeert een React Email-module ook direct met format: "jsx", en leest die dan statisch, zonder hem uit te voeren. Props krijgen hun standaardwaarde of de waarde uit PreviewProps, componenten die uit andere lokale bestanden worden geïmporteerd, worden weggelaten of geweigerd, en await en vergelijkbare constructies worden geweigerd. Gaat het om meer dan één zelfstandig bestand, gebruik dan gerenderde HTML.
Importeer daarna elke render met import_email, met één groupName per productonderdeel:
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.Developers vragen terecht of de import hun e-mails herschrijft. Dat doet hij niet:
assetReport vermeldt welke hij niet kon ophalen.baseUrl zet alleen relatieve paden naar afbeeldingen om.Elke groep krijgt een canvas. Open brew.new/emails/onboarding en de hele reeks staat naast elkaar, met een schakelaar voor desktop en mobiel en opmerkingen bij elk ontwerp. Links naar een canvas werken voor mensen die zijn ingelogd in je werkruimte in Brew. Er is geen openbare deellink, dus nodig je reviewers uit.
Hier komen de problemen boven: twee logo’s, een knop met een dode link, drie footers. Voer audit_email uit voordat je iets repareert. De tool controleert links, afbeeldingen, compliance, de geladen grootte, ondersteuning in e-mailclients, toegankelijkheid en tekst, kost 5 credits per volledige controle en accepteert een opgeslagen emailId of ruwe HTML, dus je kunt ook e-mails controleren die je niet hebt geïmporteerd. Onze QA-checklist voor het versturen legt elke controle en elke drempel uit.
Je hebt nu een inventaris, elke e-mail op een canvas en een lijst met problemen, en je code verstuurt nog alles zoals voorheen.
Nu verstuurt Brew een deel van de e-mails in plaats van je code. Niet elke e-mail hoeft mee te verhuizen, en op elk moment verstuurt maar één systeem een bepaald type e-mail.
| Nu in je code | In Brew |
|---|---|
resend.emails.send({ react: <WelcomeEmail /> }) |
Een triggerevent plus een gepubliceerde automatisering met één stap E-mail versturen |
| Props die aan de component worden doorgegeven | Velden in de triggerpayload, gebruikt als personalisatietags zoals {{ firstName | there }} of {{ trigger.planName }} |
Het domein in from |
Een geverifieerd verzenddomein in Brew |
| Je tabel met gebruikers | Contacten, aangemaakt via de REST-API of een integratie |
| Een cronjob die een reeks verstuurt | Een automatisering met de stappen Wachten, Filter en E-mail versturen |
De agent roept create_domain aan voor de DNS-records, en daarna verify_domain zodra die live staan. Domeinen die je via MCP en de API aanmaakt, zijn marketingdomeinen. Stel voor wachtwoordresets en betaalbewijzen in de app van Brew een domein in met het doel Transactioneel: dat laat de afmeldfooter weg en bezorgt ook bij afgemelde contacten. Je verzenddomein verifiëren behandelt de records.
Via MCP kun je contacten bijwerken, maar niet aanmaken. Maak ze aan vanuit je backend met POST /v1/contacts, dat één contact of een batch van maximaal 1.000 contacten toevoegt of bijwerkt en aangepaste velden automatisch aanmaakt, of upload een CSV in de app. Een trigger activeren maakt ook het contact uit de payload aan of werkt het bij. Importeer alleen mensen die ermee hebben ingestemd om van je te horen. Contacten toevoegen legt uit wat Brew verwacht.
Makkelijk over het hoofd te zien: in de geïmporteerde welkomstmail staat letterlijk “Hi Ana”. Vervang elke fixturewaarde door een personalisatietag zoals {{ firstName | there }}, waarbij de tekst na de verticale streep de standaardwaarde is. Gebruik de editor van Brew, of laat de agent edit_email uitvoeren, beperkt tot die vervangingen. Dat is een AI-bewerking die credits kost, dus controleer het voorbeeld. Personalisatietags beschrijft de syntaxis.
De agent maakt een trigger aan met save_trigger. Het payloadschema moet het veld email van de ontvanger bevatten. Geef de trigger een getypeerd contract (infer_payload_contract stelt er een op aan de hand van een voorbeeldpayload, set_trigger_contract slaat het op), en haal het daarna op met get_trigger_contract in het formaat ts of zod, zodat je backend en Brew het eens zijn over de vorm van de payload.
Daarna bouwt save_automation de trigger plus een stap E-mail versturen, met de e-mail, het domein en de onderwerpregel. De automatisering blijft een concept tot je publiceert. dryRun valideert de flow, test_automation stuurt een echt exemplaar naar je eigen adres, en check_trigger bevestigt dat een gepubliceerde automatisering luistert voordat productie iets activeert.
Dit is de enige wijziging in je applicatiecode. Voorheen, met 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} />,
})Nu, met 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 gooit ook bij een geweigerd verzoek geen fout, dus controleer res.ok en laat je jobrunner het opnieuw proberen of alarm slaan. Het endpoint geeft 202 terug als het het event accepteert, en 422 als er geen gepubliceerde automatisering naar de trigger luistert.
Bouw de Idempotency-Key op uit een stabiele ID, zoals die van de gebruiker of de bestelling. Een herhaalde sleutel geeft de oorspronkelijke run terug in plaats van twee keer te versturen, en wie een sleutel hergebruikt met een andere body, krijgt 409 IDEMPOTENCY_CONFLICT. Brew bewaart de sleutel van een event even lang als de ontvangstregistratie van de trigger, en dat is 90 dagen, niet de 24 uur die veel API’s aanhouden. Behandel sleutels als permanent: één per actie. Een sleutel op basis van een tijdstempel verandert bij elke nieuwe poging, en een sleutel die je weken later voor een andere actie hergebruikt, levert mogelijk de oude run opnieuw op in plaats van een nieuwe verzending.
Productiecode roept dit endpoint aan met een API-sleutel, niet via MCP. Bij OAuth vraagt MCP een persoon om live verzendingen, het activeren van triggers en het publiceren te bevestigen: goed voor een chat, verkeerd voor een request handler. De gids over MCP of REST behandelt die verdeling.
EMAILS.md bij.De migratiegids voor Loops volgt hetzelfde patroon.
Bevestigingsmails en magic links van Supabase blijven uit Supabase komen. De Supabase-integratie van Brew luistert naar gebruikersevents om automatiseringen te starten, en vervangt de e-mails van Supabase Auth niet. Een gespecificeerde factuur die bij het versturen uit complexe data wordt opgebouwd, is in code misschien ook eenvoudiger, dus controleer eerst wat je personalisatietags nodig hebben. Houd geïmporteerde kopieën op het canvas, zodat ook deze e-mails zichtbaar blijven.
Controleer de hele set. Voer audit_email uit voor een hele groep, los de problemen op en voer de controle opnieuw uit vóór grote verzendingen.
Controleer echte e-mailclients. test_email_rendering levert screenshots uit specifieke e-mailclients en apparaten, inclusief de donkere modus, voor 10 credits per run. create_inbox_placement_test stuurt een echt exemplaar naar seedinboxen en meldt waar het terechtkwam, ook voor 10 credits.
Maak de set consistent. Herstel inconsistenties in de editor, of laat de agent met edit_email één wijziging, zoals een nieuwe footer, op een hele groep toepassen.
Lees de resultaten. get_email_analytics rapporteert verzendingen en betrokkenheid. list_insights geeft bevindingen terug die Brew in je account heeft gevonden, elk met de metrics die eraan ten grondslag liggen.
Test varianten bewust. Maak varianten op het canvas en verdeel contacten met een stap Splitsen op basis van gedrag, bijvoorbeeld of ze hebben geklikt. Brew kiest bij een willekeurige splitsing niet zelf een winnaar, dus vergelijk de varianten zelf.
Houd nieuwe e-mails zichtbaar. Voeg een regel toe aan AGENTS.md of een vergelijkbaar bestand: nieuwe productmails worden in Brew gemaakt, via een trigger geactiveerd en in dezelfde pull request aan EMAILS.md toegevoegd.
Ja. import_email accepteert gerenderde HTML, MJML, een opgeslagen .eml-bericht of een React Email-module. Gerenderde HTML is het betrouwbaarst, omdat een module statisch wordt gelezen: props krijgen hun standaard- of previewwaarde, en componenten die uit andere lokale bestanden worden geïmporteerd, worden weggelaten.
Nee. Er is geen integratie, import of export voor Resend. Je importeert de HTML die je code rendert, en als je migreert, activeert je code een trigger in Brew in plaats van Resend aan te roepen.
Nee. Gebruik MCP vanuit een coding agent voor de inrichting, imports, controles en tests. Productiecode hoort de REST-API aan te roepen met een API-sleutel, omdat MCP bij OAuth-koppelingen een persoon vraagt om live verzendingen en het activeren van triggers te bevestigen.
De import zet de e-mail om in een bewerkbaar ontwerp in Brew en bewaart de oorspronkelijke bron-HTML voor de bezorging. Hij verwijdert scripts, iframes, formulieren en event handlers, schakelt bestaande trackingpixels uit en host openbare afbeeldingen opnieuw. Bekijk elke e-mail op het canvas, in de desktop- en de mobiele weergave, voordat je hem vanuit Brew verstuurt.
