Zamieniaj e-maile w przychody dzięki Brew.
Poradnik dla zespołów z e-mailami w kodzie: spisz je z agentem kodującym, zbierz przez MCP na jednej kanwie Brew, a potem bezpiecznie przenieś i ulepsz.

E-maile w kodzie to e-maile produktowe zdefiniowane w Twojej bazie kodu i wdrażane jak każda inna funkcja. Zapytaj zespół, który pracuje na Resend i React Email, jakie e-maile wysyła produkt, a zapadnie chwila ciszy. E-mail powitalny to komponent w emails/. Reset hasła siedzi w panelu dostawcy uwierzytelniania. E-mail o końcu okresu próbnego to ciąg znaków w zadaniu crona. Potwierdzenie płatności wysyła Stripe. A co wysyła e-mail „Tęsknimy za Tobą”, tego nikt już nie pamięta.
Każdy z tych e-maili łatwo było wdrożyć, więc nikt nie odpowiada za całość. My uporządkowalibyśmy to w takiej kolejności: najpierw widoczność, potem migracja, na końcu ulepszenia. Większość tej pracy wykona agent kodujący, którego już używasz, połączony z Brew przez MCP.
import_email. Import jest deterministyczny, nie korzysta z AI i jest bezpłatny.React Email i Resend bardzo ułatwiły programistom pracę z e-mailami: typowane komponenty, przeglądane w pull requestach. Koszt wychodzi na jaw później.
E-maile są rozproszone. Komponenty, HTML wpisany na sztywno w zadaniu działającym w tle, szablony w panelu zewnętrznej usługi, e-maile, które inna usługa wysyła za Ciebie. Żaden plik nie wymienia ich wszystkich.
Podgląd to nie produkcja. Serwer podglądu React Email pokazuje jeden szablon z jego propsami podglądu, a nie to, co faktycznie dostał klient – a dla pozostałych szablonów nie pokazuje niczego.
Zajrzeć mogą tylko programiści. Osoby z zespołów produktu, projektowania i wsparcia musiałyby prosić programistę o uruchomienie projektu, więc zwykle tego nie robią.
Po wdrożeniu nikt niczego nie przegląda. Dwuletni e-mail nadal ma stare logo i plan, którego już nie sprzedajesz. Nikt tego nie zauważa, bo nikt nie widzi tych e-maili razem.
Celem jest jeden wspólny widok wszystkich e-maili. Nic na tym etapie nie dotyka produkcji.

Każdy etap jest przydatny sam w sobie i po każdym z nich możesz się zatrzymać.
Serwer MCP Brew działa pod adresem https://brew.new/api/mcp i korzysta z OAuth: logujesz się w Brew i wybierasz markę, w której agent może pracować. W Claude Code:
claude mcp add --transport http brew "https://brew.new/api/mcp"Dodanie serwera zapisuje go tylko w lokalnej konfiguracji tego projektu. Dodaj --scope user, aby był dostępny we wszystkich projektach. Zanim skorzystasz z jego narzędzi, uruchom claude mcp login brew albo otwórz /mcp i uwierzytelnij się w Brew.
Dokumentacja konfiguracji klientów zawiera też instrukcje dla Cursor i Codex. Aplikacje klienckie bez obsługi OAuth mogą korzystać z klucza API przypisanego do marki. Potem poproś agenta o wywołanie get_brew_capabilities, żeby odczytał aktualną listę narzędzi, zamiast zgadywać.
Poproś agenta o listę:
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.Zachowaj sekcję „Unknown”. Potwierdzenia wysyłane przez system uwierzytelniania i potwierdzenia płatności ze Stripe nie są wywołaniami wysyłki w Twoim kodzie, ale też należą do listy. Dodaj plik EMAILS.md do repozytorium. Nawet jeśli na tym poprzestaniesz, masz listę, której wczoraj nie było.
Najlepszym materiałem do importu jest HTML, który Twój kod już generuje. Dostaniesz go jako ciąg znaków z funkcji render w React Email:
import { render } from 'react-email'
import WelcomeEmail from './emails/welcome'
const html = await render(
<WelcomeEmail firstName="Ana" planName="Pro" trialDays={14} />
)Starsze projekty importują render z @react-email/render. Używaj realistycznych danych testowych, a nie "test", bo te e-maile będzie przeglądał Twój zespół. Poproś agenta o skrypt, który wyrenderuje każdy szablon do pliku.
Brew potrafi też zaimportować moduł React Email bezpośrednio, z format: "jsx": odczytuje go statycznie, bez uruchamiania. Propsy przyjmują wartości domyślne lub te z PreviewProps, komponenty importowane z innych lokalnych plików są pomijane lub odrzucane, a await i podobne konstrukcje nie są akceptowane. Jeśli szablon nie mieści się w jednym samodzielnym pliku, użyj wyrenderowanego HTML.
Potem zaimportuj każdy wyrenderowany plik za pomocą import_email, z jednym groupName na obszar produktu:
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.Programiści słusznie pytają, czy import przepisuje ich e-maile. Nie przepisuje:
assetReport wymienia te, których nie udało się pobrać.baseUrl służy tylko do rozwiązywania względnych ścieżek obrazów.Każda grupa dostaje własną kanwę. Otwórz brew.new/emails/onboarding, a zobaczysz obok siebie całą sekwencję, z przełącznikiem widoku na komputer i telefon oraz komentarzami przy każdym projekcie. Linki do kanwy działają dla osób zalogowanych do Twojego obszaru roboczego Brew. Nie ma publicznego linku do udostępniania, więc zaproś osoby, które mają przeglądać e-maile.
Tu wychodzą na jaw problemy: dwa logotypy, martwy link w przycisku, trzy różne stopki. Zanim cokolwiek poprawisz, uruchom audit_email. Narzędzie sprawdza linki, obrazy, zgodność z przepisami, łączny rozmiar do pobrania, obsługę w programach pocztowych, dostępność i treść. Pełny audyt kosztuje 5 kredytów, a narzędzie przyjmuje zapisany emailId albo surowy HTML, więc możesz audytować także e-maile, których nie zaimportowano. Nasza lista kontrolna przed wysyłką wyjaśnia każdy test i każdy próg.
Masz teraz inwentarz, wszystkie e-maile na kanwie i listę problemów, a Twój kod nadal wysyła wszystko tak jak wcześniej.
Teraz część e-maili zamiast Twojego kodu wysyła Brew. Nie każdy e-mail musi zostać przeniesiony, a dany typ e-maila w każdej chwili wysyła tylko jeden system.
| Dziś w Twoim kodzie | W Brew |
|---|---|
resend.emails.send({ react: <WelcomeEmail /> }) |
Zdarzenie wyzwalające i opublikowana automatyzacja z jednym krokiem „Wyślij e-mail” |
| Propsy przekazywane do komponentu | Pola danych zdarzenia wyzwalacza, używane jako tagi personalizacji, np. {{ firstName | there }} lub {{ trigger.planName }} |
Domena w polu from |
Zweryfikowana domena wysyłkowa w Brew |
| Twoja tabela użytkowników | Kontakty tworzone przez REST API lub integrację |
| Zadanie cron, które wysyła sekwencję | Automatyzacja z krokami „Czekaj”, „Filtr” i „Wyślij e-mail” |
Agent wywołuje create_domain, żeby uzyskać rekordy DNS, a gdy rekordy będą już aktywne – verify_domain. Domeny dodane przez MCP i API to domeny marketingowe. Do resetów haseł i potwierdzeń płatności skonfiguruj w aplikacji Brew domenę o przeznaczeniu transakcyjnym: pomija ona stopkę z linkiem do wypisania i dostarcza e-maile także do wypisanych kontaktów. Rekordy opisuje strona Weryfikacja domeny wysyłkowej.
MCP aktualizuje kontakty, ale ich nie tworzy. Twórz je z backendu przez POST /v1/contacts (endpoint tworzy lub aktualizuje jeden kontakt lub partię do 1000 kontaktów i automatycznie zakłada pola własne) albo prześlij plik CSV w aplikacji. Wywołanie wyzwalacza również tworzy lub aktualizuje kontakt na podstawie danych zdarzenia. Importuj tylko osoby, które zgodziły się otrzymywać od Ciebie wiadomości. Strona Dodawanie kontaktów wyjaśnia, czego oczekuje Brew.
Łatwo to przeoczyć: w zaimportowanym e-mailu powitalnym dosłownie widnieje „Hi Ana”. Zastąp każdą wartość testową tagiem personalizacji, np. {{ firstName | there }}, gdzie tekst po pionowej kresce to wartość zastępcza. Zrób to w edytorze Brew albo poproś agenta o uruchomienie edit_email ograniczonego do tych zamian. To edycja przez AI, która zużywa kredyty, więc sprawdź podgląd. Składnię opisuje strona Tagi personalizacji.
Agent tworzy wyzwalacz za pomocą save_trigger. Schemat danych zdarzenia musi zawierać pole email odbiorcy. Zdefiniuj typowany schemat (infer_payload_contract przygotowuje jego szkic na podstawie przykładowych danych zdarzenia, a set_trigger_contract go zapisuje), a potem pobierz go przez get_trigger_contract w formacie ts lub zod, żeby Twój backend i Brew zgadzały się co do kształtu danych zdarzenia.
Następnie save_automation buduje wyzwalacz i krok „Wyślij e-mail” z e-mailem, domeną i tematem. Automatyzacja pozostaje wersją roboczą, dopóki jej nie opublikujesz. dryRun sprawdza poprawność schematu automatyzacji, test_automation wysyła prawdziwą kopię na Twój adres, a check_trigger potwierdza, że opublikowana automatyzacja nasłuchuje, zanim produkcja cokolwiek wywoła.
To jedyna zmiana w kodzie Twojej aplikacji. Wcześniej, z 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} />,
})Potem, z 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 kończy się powodzeniem nawet wtedy, gdy żądanie zostanie odrzucone, więc sprawdzaj res.ok i pozwól, by system obsługi zadań ponowił próbę albo wysłał alert. Endpoint zwraca 202, gdy przyjmie zdarzenie, i 422, jeśli żadna opublikowana automatyzacja nie nasłuchuje tego wyzwalacza.
Twórz Idempotency-Key ze stałego identyfikatora, np. użytkownika lub zamówienia. Powtórzony klucz zwraca pierwotne uruchomienie zamiast drugiej wysyłki, a użycie tego samego klucza z inną treścią żądania kończy się błędem 409 IDEMPOTENCY_CONFLICT. Brew przechowuje klucz wywołania tak długo jak rekord odebrania wyzwalacza, czyli 90 dni, a nie 24 godziny, jak wiele innych API. Traktuj klucze jako trwałe – jeden na działanie. Klucz oparty na znaczniku czasu zmienia się przy każdym ponowieniu, a klucz użyty kilka tygodni później do innego działania może zwrócić wcześniejsze uruchomienie, zamiast wysłać e-mail.
Kod produkcyjny wywołuje ten endpoint z kluczem API, a nie przez MCP. W połączeniu OAuth MCP prosi człowieka o potwierdzenie rzeczywistych wysyłek, wywołań wyzwalaczy i publikacji: to dobre rozwiązanie na czacie, ale złe w kodzie obsługującym żądania. Podział pracy opisuje poradnik MCP kontra REST.
EMAILS.md.Przewodnik po migracji z Loops stosuje ten sam schemat.
E-maile potwierdzające i e-maile z magicznym linkiem nadal wysyła Supabase. Integracja Brew z Supabase nasłuchuje zdarzeń użytkowników, żeby uruchamiać automatyzacje, a nie zastępuje e-maili Supabase Auth. Szczegółowa faktura budowana w chwili wysyłki ze złożonych danych też może być prostsza w kodzie, więc najpierw sprawdź, czego potrzebują Twoje tagi personalizacji. Zostaw zaimportowane kopie na kanwie, żeby te e-maile pozostały widoczne.
Audytuj cały zestaw. Uruchom audit_email dla całej grupy, popraw błędy i powtórz audyt przed dużymi wysyłkami.
Sprawdź w prawdziwych programach pocztowych. test_email_rendering zwraca zrzuty ekranu z konkretnych programów pocztowych i urządzeń, także w trybie ciemnym, za 10 kredytów za uruchomienie. create_inbox_placement_test wysyła prawdziwą kopię do skrzynek testowych i raportuje, gdzie trafiła – również za 10 kredytów.
Zadbaj o spójność zestawu. Popraw niespójności w edytorze albo poproś agenta, żeby za pomocą edit_email wprowadził jedną zmianę, np. nową stopkę, w całej grupie.
Analizuj wyniki. get_email_analytics raportuje wysyłki i zaangażowanie. list_insights zwraca spostrzeżenia, które Brew wykrył na Twoim koncie, każde z danymi, na których się opiera.
Testuj warianty świadomie. Utwórz warianty na kanwie i kieruj kontakty krokiem „Podział” na podstawie zachowania, np. tego, czy ktoś kliknął. Brew nie wybiera za Ciebie zwycięzcy losowego podziału, więc porównaj warianty samodzielnie.
Dbaj o widoczność nowych e-maili. Dodaj do AGENTS.md lub podobnego pliku regułę: nowe e-maile produktowe powstają w Brew, są uruchamiane wyzwalaczem i trafiają do EMAILS.md w tym samym pull requeście.
Tak. import_email przyjmuje wyrenderowany HTML, MJML, zapisaną wiadomość .eml albo moduł React Email. Najbardziej niezawodny jest wyrenderowany HTML, bo moduł jest odczytywany statycznie: propsy przyjmują wartości domyślne lub wartości podglądu, a komponenty importowane z innych lokalnych plików są pomijane.
Nie. Brew nie ma integracji z Resend ani funkcji importu czy eksportu dla Resend. Importujesz HTML, który renderuje Twój kod, a jeśli przeprowadzisz migrację, Twój kod wywołuje wyzwalacz Brew zamiast Resend.
Nie. Używaj MCP z poziomu agenta kodującego do konfiguracji, importów, audytów i testów. Kod produkcyjny powinien wywoływać REST API z kluczem API, bo w połączeniach OAuth MCP prosi człowieka o potwierdzenie rzeczywistych wysyłek i wywołań wyzwalaczy.
Import zamienia e-mail w edytowalny projekt Brew i zachowuje oryginalny źródłowy HTML do wysyłki. Usuwa skrypty, elementy iframe, formularze i obsługę zdarzeń, neutralizuje istniejące piksele śledzące i przenosi publiczne obrazy na hosting Brew. Zanim wyślesz e-mail z Brew, przejrzyj każdy z nich na kanwie, w widoku na komputer i na telefon.
