Panduan bagi tim yang emailnya ada di kode: inventarisasi dengan agen coding, satukan di kanvas Brew lewat MCP, lalu migrasikan dan tingkatkan dengan aman.

Email dalam kode adalah email produk yang didefinisikan di codebase Anda dan dirilis seperti fitur lainnya. Di tim yang dibangun di atas Resend dan React Email, coba tanyakan email apa saja yang dikirim produk, dan semua orang akan terdiam sejenak. Email sambutan adalah komponen di emails/. Reset kata sandi ada di dasbor penyedia autentikasi. Email berakhirnya masa uji coba berupa string di dalam cron job. Bukti pembayaran dikirim oleh Stripe. Tidak ada yang ingat apa yang mengirim email “kami merindukan Anda”.
Setiap email mudah dirilis, sehingga tidak ada yang memegang gambaran utuhnya. Kami akan membereskannya secara berurutan: visibilitas, lalu migrasi, lalu peningkatan. Agen coding yang sudah Anda pakai bisa mengerjakan sebagian besarnya setelah terhubung ke Brew lewat MCP.
import_email. Impor bersifat deterministik, tidak memakai AI, dan gratis.React Email dan Resend membuat urusan email jauh lebih baik bagi pengembang: komponen bertipe yang ditinjau lewat pull request. Harganya baru terasa belakangan.
Emailnya tersebar. Komponen, HTML inline di background job, templat di dasbor pihak ketiga, email yang dikirim layanan lain untuk Anda. Tidak ada satu file pun yang mencantumkan semuanya.
Pratinjau bukan produksi. Server pratinjau React Email menampilkan satu templat dengan props pratinjaunya, bukan apa yang diterima pelanggan, dan tidak menampilkan apa pun untuk templat lain.
Hanya engineer yang bisa melihatnya. Tim produk, desain, dan dukungan harus meminta engineer menjalankan proyeknya, jadi biasanya mereka tidak melakukannya.
Tidak ada yang ditinjau setelah peluncuran. Email yang berumur dua tahun masih memakai logo lama dan menyebut paket yang sudah tidak Anda jual. Tidak ada yang menyadarinya karena tidak ada yang melihat semua email itu bersamaan.
Tujuannya adalah satu tampilan bersama untuk setiap email. Tidak ada langkah di sini yang menyentuh produksi.

Setiap fase sudah bermanfaat dengan sendirinya, dan Anda bisa berhenti setelah fase mana pun.
Server MCP Brew berada di https://brew.new/api/mcp dan memakai OAuth: Anda masuk dengan akun Brew dan memilih merek tempat agen boleh bekerja. Di Claude Code:
claude mcp add --transport http brew "https://brew.new/api/mcp"Menambahkan server hanya menyimpannya di konfigurasi lokal proyek ini. Tambahkan --scope user agar server berlaku untuk semua proyek. Sebelum memakai alat Brew, jalankan claude mcp login brew, atau buka /mcp lalu lakukan autentikasi ke Brew.
Dokumentasi penyiapan klien juga memuat petunjuk untuk Cursor dan Codex. Klien tanpa OAuth bisa memakai kunci API dengan cakupan merek. Lalu minta agen memanggil get_brew_capabilities agar agen membaca daftar alat yang berlaku saat ini, bukan menebak-nebak.
Minta agen membuat daftar:
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.Pertahankan bagian “Unknown”. Email konfirmasi autentikasi dan bukti pembayaran Stripe memang bukan panggilan pengiriman di kode Anda, tetapi keduanya tetap harus masuk daftar. Commit file EMAILS.md. Jika Anda berhenti di sini pun, Anda sudah memiliki daftar yang kemarin belum ada.
Input impor terbaik adalah HTML yang sudah dihasilkan kode Anda. Utilitas render dari React Email memberikan string tersebut:
import { render } from 'react-email'
import WelcomeEmail from './emails/welcome'
const html = await render(
<WelcomeEmail firstName="Ana" planName="Pro" trialDays={14} />
)Proyek yang lebih lama mengimpor render dari @react-email/render. Gunakan data fixture yang realistis, bukan "test", karena tim Anda akan meninjau hasilnya. Minta agen membuat skrip yang merender setiap templat ke sebuah file.
Brew juga bisa mengimpor modul React Email secara langsung dengan format: "jsx", dengan membacanya secara statis tanpa mengeksekusinya. Props diisi dengan nilai default atau PreviewProps, komponen yang diimpor dari file lokal lain akan dibuang atau ditolak, dan await serta sejenisnya tidak diterima. Untuk apa pun di luar satu file yang berdiri sendiri, gunakan HTML hasil render.
Lalu impor setiap hasil render dengan import_email, satu groupName untuk setiap area produk:
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.Wajar jika pengembang bertanya apakah impor akan menulis ulang email mereka. Jawabannya tidak:
assetReport mencantumkan gambar yang tidak bisa diambil.baseUrl hanya dipakai untuk me-resolve path gambar relatif.Setiap grup mendapat satu kanvas. Buka brew.new/emails/onboarding, dan seluruh rangkaian tampil berdampingan, lengkap dengan sakelar tampilan desktop dan seluler serta komentar di setiap desain. Tautan kanvas bisa dibuka oleh orang yang sudah masuk ke ruang kerja Brew Anda. Tidak ada tautan berbagi publik, jadi undang para peninjau Anda.
Di sinilah masalah mulai terlihat: dua logo, tautan tombol yang mati, tiga footer. Jalankan audit_email sebelum memperbaiki apa pun. Alat ini memeriksa tautan, gambar, kepatuhan, ukuran saat dimuat, dukungan klien email, aksesibilitas, dan teks, memakan 5 kredit per audit lengkap, dan menerima emailId yang tersimpan atau HTML mentah, sehingga Anda bisa mengaudit email yang belum diimpor. Daftar periksa QA sebelum mengirim kami menjelaskan setiap pemeriksaan dan ambang batasnya.
Kini Anda memiliki inventaris, setiap email di kanvas, dan daftar masalah, sementara kode Anda tetap mengirim semuanya seperti sebelumnya.
Sekarang Brew mengirim sebagian email menggantikan kode Anda. Tidak semua email harus dipindahkan, dan pada satu waktu hanya satu sistem yang mengirim jenis email tertentu.
| Di kode Anda saat ini | Di Brew |
|---|---|
resend.emails.send({ react: <WelcomeEmail /> }) |
Peristiwa pemicu ditambah otomatisasi yang sudah dipublikasikan dengan satu langkah Kirim email |
| Props yang diteruskan ke komponen | Field payload pemicu, yang dipakai sebagai merge tag seperti {{ firstName | there }} atau {{ trigger.planName }} |
Domain from |
Domain pengirim terverifikasi di Brew |
| Tabel pengguna Anda | Kontak, yang dibuat lewat REST API atau integrasi |
| Cron job yang mengirim rangkaian | Otomatisasi dengan langkah Tunggu, Filter, dan Kirim email |
Agen memanggil create_domain untuk mendapatkan record DNS, lalu verify_domain setelah record-record itu aktif. Domain yang dibuat lewat MCP dan API adalah domain pemasaran. Untuk reset kata sandi dan bukti pembayaran, siapkan domain khusus transaksional di aplikasi Brew: domain ini tidak menambahkan footer berhenti berlangganan dan tetap mengirim ke kontak yang sudah berhenti berlangganan. Panduan memverifikasi domain pengirim membahas record-record tersebut.
MCP bisa memperbarui kontak, tetapi tidak bisa membuatnya. Buat kontak dari backend Anda dengan POST /v1/contacts, yang membuat atau memperbarui satu kontak atau satu batch berisi hingga 1.000 kontak dan otomatis membuat field kustom, atau unggah file CSV di aplikasi. Menjalankan pemicu juga membuat atau memperbarui kontak yang ada di payload-nya. Impor hanya orang-orang yang sudah setuju menerima email dari Anda. Panduan menambahkan kontak menjelaskan apa yang dibutuhkan Brew.
Hal ini mudah terlewat: email sambutan yang diimpor benar-benar bertuliskan “Hi Ana”. Ganti setiap nilai fixture dengan merge tag seperti {{ firstName | there }}, dengan teks setelah garis tegak sebagai nilai cadangan. Gunakan editor Brew, atau minta agen menjalankan edit_email yang dibatasi pada penggantian tersebut. Ini adalah pengeditan dengan AI yang memakai kredit, jadi periksa pratinjaunya. Dokumentasi merge tag mencantumkan sintaksnya.
Agen membuat pemicu dengan save_trigger. Skema payload-nya wajib menyertakan email penerima. Berikan kontrak bertipe untuk pemicu itu (infer_payload_contract menyusun draf kontrak dari contoh payload, set_trigger_contract menyimpannya), lalu ambil kontraknya dengan get_trigger_contract dalam format ts atau zod agar backend Anda dan Brew sepakat tentang bentuk payload.
Setelah itu, save_automation membangun pemicu beserta langkah Kirim email dengan email, domain, dan subjeknya. Otomatisasi ini tetap berupa draf sampai Anda memublikasikannya. dryRun memvalidasi diagramnya, test_automation mengirim salinan sungguhan ke alamat Anda, dan check_trigger memastikan sudah ada otomatisasi terpublikasi yang siap menerima pemicu sebelum kode produksi menjalankan apa pun.
Inilah satu-satunya perubahan pada kode aplikasi Anda. Sebelumnya, dengan 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} />,
})Sesudahnya, dengan 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 tetap resolve meskipun permintaannya ditolak, jadi periksa res.ok dan biarkan job runner Anda mencoba ulang atau mengirim peringatan. Endpoint ini mengembalikan 202 saat menerima peristiwa dan 422 jika tidak ada otomatisasi terpublikasi yang mendengarkan pemicu tersebut.
Susun Idempotency-Key dari ID yang stabil, seperti ID pengguna atau pesanan. Kunci yang berulang akan mengembalikan eksekusi aslinya alih-alih mengirim dua kali, dan memakai ulang kunci dengan isi permintaan yang berbeda akan menghasilkan 409 IDEMPOTENCY_CONFLICT. Brew menyimpan kunci setiap pemicuan selama tanda terima pemicunya disimpan, yaitu 90 hari, bukan 24 jam seperti yang dipakai banyak API. Anggap kunci bersifat permanen, satu kunci untuk satu tindakan. Kunci berbasis stempel waktu berubah pada setiap percobaan ulang, dan kunci yang dipakai ulang untuk tindakan lain beberapa minggu kemudian bisa diperlakukan sebagai pengulangan alih-alih dikirim.
Kode produksi memanggil endpoint ini dengan kunci API, bukan lewat MCP. Lewat OAuth, MCP meminta seseorang mengonfirmasi pengiriman nyata, pemicuan, dan publikasi: tepat untuk chat, keliru untuk request handler. Panduan MCP atau REST membahas pembagian ini.
EMAILS.md.Panduan migrasi dari Loops memakai pola yang sama.
Email konfirmasi dan magic link Supabase tetap dikirim dari Supabase. Integrasi Supabase di Brew mendengarkan peristiwa pengguna untuk memulai otomatisasi, bukan menggantikan email dari Supabase Auth. Invoice terperinci yang disusun dari data kompleks saat pengiriman mungkin juga lebih sederhana jika tetap di kode, jadi periksa dulu apa yang dibutuhkan merge tag Anda. Simpan salinan hasil impornya di kanvas agar email-email ini tetap terlihat.
Audit seluruh email. Jalankan audit_email untuk satu grup, perbaiki, lalu jalankan ulang sebelum pengiriman besar.
Periksa di klien email sungguhan. test_email_rendering mengembalikan tangkapan layar dari klien email dan perangkat tertentu, termasuk mode gelap, dengan biaya 10 kredit per eksekusi. create_inbox_placement_test mengirim salinan sungguhan ke kotak masuk pengujian dan melaporkan ke mana email itu masuk, juga dengan biaya 10 kredit.
Buat semua email konsisten. Perbaiki ketidakkonsistenan di editor, atau minta agen menerapkan satu perubahan, seperti footer baru, ke seluruh grup dengan edit_email.
Baca hasilnya. get_email_analytics melaporkan pengiriman dan keterlibatan. list_insights mengembalikan temuan yang dideteksi Brew di akun Anda, masing-masing dengan metrik yang mendasarinya.
Uji varian secara terencana. Buat varian di kanvas dan arahkan kontak dengan langkah Percabangan berdasarkan perilaku, misalnya apakah mereka mengklik. Brew tidak memilihkan pemenang dari pembagian acak untuk Anda, jadi bandingkan variannya sendiri.
Pastikan email baru tetap terlihat. Tambahkan satu baris ke AGENTS.md atau file serupa: email produk baru dibuat di Brew, dikirim lewat pemicu, dan ditambahkan ke EMAILS.md dalam pull request yang sama.
Bisa. import_email menerima HTML hasil render, MJML, pesan .eml yang tersimpan, atau modul React Email. HTML hasil render adalah yang paling andal, karena modul dibaca secara statis: props diisi dengan nilai default atau nilai pratinjaunya, dan komponen yang diimpor dari file lokal lain tidak disertakan.
Tidak. Tidak ada integrasi, impor, atau ekspor untuk Resend. Anda mengimpor HTML yang dirender kode Anda, dan jika Anda bermigrasi, kode Anda menjalankan pemicu Brew alih-alih memanggil Resend.
Tidak. Gunakan MCP dari agen coding untuk penyiapan, impor, audit, dan pengujian. Kode produksi sebaiknya memanggil REST API dengan kunci API, karena pada koneksi OAuth, MCP meminta seseorang mengonfirmasi pengiriman nyata dan pemicuan.
Impor mengubah email menjadi desain Brew yang bisa diedit dan tetap menyimpan HTML sumber aslinya untuk pengiriman. Impor menghapus skrip, iframe, formulir, dan event handler, menetralkan piksel pelacakan yang sudah ada, dan meng-hosting ulang gambar publik. Tinjau setiap email di kanvas, dalam tampilan desktop dan seluler, sebelum mengirimnya dari Brew.
