# Koddaki e-postalar: görünür kılın, taşıyın, iyileştirin

Kanonik URL: https://brew.new/tr/blog/emails-in-code-visibility-migration-mcp
Yazar: Thomas Park
Yayımlandı: 2026-10-12
Güncellendi: 2026-10-15

Koddaki e-postalar, kod tabanınızda tanımlanan ve diğer tüm özellikler gibi yayına alınan ürün e-postalarıdır. Resend ve React Email üzerine kurulu bir ekibe ürünün hangi e-postaları gönderdiğini sorun; kısa bir sessizlik olur. Hoş geldin e-postası `emails/` klasöründe bir bileşen. Şifre sıfırlama e-postası kimlik doğrulama sağlayıcısının kontrol panelinde duruyor. Deneme süresinin bittiğini bildiren e-posta, bir cron işinin içinde bir metin dizesi. Makbuzu Stripe gönderiyor. “Sizi özledik” e-postasını neyin gönderdiğini ise kimse hatırlamıyor.

Her e-postayı yayına almak kolaydı; bu yüzden resmin tamamından kimse sorumlu değil. Biz bunu şu sırayla düzeltirdik: önce görünürlük, sonra geçiş, ardından iyileştirme. İşin çoğunu, hâlihazırda kullandığınız kodlama ajanı MCP üzerinden Brew ile bağlanarak yapabilir.

## Öne çıkan noktalar

- Yeni bir araçla değil, bir envanterle başlayın: her e-posta, tetikleyicisi, dosyası ve değişkenleri.
- Bir kodlama ajanı gönderim çağrılarını bulabilir, her şablonu HTML’e dönüştürebilir ve `import_email` MCP aracıyla içe aktarabilir. İçe aktarma deterministiktir, yapay zekâ kullanmaz ve ücretsizdir.
- İçe aktarılan e-postalar her grup için ayrı bir tuvale yerleşir; böylece ekip onları masaüstü ve mobil görünümde yan yana inceler.
- Geçişte Resend tarzı bir gönderim, tek bir REST çağrısıyla tetiklenen bir tetikleyiciye ve yayımlanmış bir otomasyona dönüşür. Üretim ortamındaki kod MCP’yi değil, REST’i kullanır.
- Her seferinde tek bir e-posta türünü bir özellik bayrağının (feature flag) arkasında taşıyın; işlemsel e-postaları en sona bırakın.

## Kodda tanımlanan e-postalar neden görünmez hâle gelir?

React Email ve Resend, e-postayı geliştiriciler için çok daha iyi hâle getirdi: tipli bileşenler ve pull request aşamasında gözden geçirilen kod. Bedeli ise daha sonra ortaya çıkıyor.

**E-postalar dağınık.** Bileşenler, bir arka plan işinin içindeki satır içi HTML, üçüncü taraf bir kontrol panelindeki şablonlar, başka bir hizmetin sizin adınıza gönderdiği e-postalar. Hepsini listeleyen bir dosya yok.

**Önizleme, üretim ortamı değildir.** React Email önizleme sunucusu bir şablonu, önizleme için tanımlanmış props değerleriyle gösterir; bir müşterinin gerçekte ne aldığını göstermez, diğer şablonlar içinse hiçbir şey göstermez.

**Yalnızca mühendisler bakabiliyor.** Ürün, tasarım ve destek ekiplerinin projeyi çalıştırması için bir mühendisten rica etmesi gerekir; bu yüzden çoğu zaman bunu yapmazlar.

**Yayından sonra hiçbir şey gözden geçirilmiyor.** İki yıllık e-postada hâlâ eski logo ve artık satmadığınız bir plan duruyor. Kimse fark etmiyor, çünkü kimse e-postaları bir arada görmüyor.

## Adım 1: Görünürlük

Amaç, tüm e-postaları ortak tek bir görünümde toplamak. Bu adımda üretim ortamına hiç dokunulmaz.

![E-postaları koddan taşımanın üç aşaması: import_email ve her grup için bir tuvalle görünürlük; alan adları, kişiler, tetikleyiciler ve otomasyonlarla geçiş; denetimler, görüntüleme testleri ve analizlerle optimizasyon.](/images/blog/emails-in-code-three-phases.png)

*Her aşama tek başına işe yarar; herhangi birinden sonra durabilirsiniz.*

### Kodlama ajanınızı Brew ile bağlayın

Brew MCP sunucusu `https://brew.new/api/mcp` adresindedir ve OAuth kullanır: Brew hesabınızla giriş yapar ve ajanın çalışabileceği markayı seçersiniz. Claude Code’da:

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

Bu komut, sunucuyu yalnızca bu projenin yerel yapılandırmasına kaydeder. Sunucuyu tüm projelerde kullanmak için `--scope user` ekleyin. Brew araçlarını kullanmadan önce `claude mcp login brew` komutunu çalıştırın ya da `/mcp` menüsünü açıp Brew için kimlik doğrulamasını tamamlayın.

[İstemci kurulum dokümantasyonunda](https://docs.brew.new/api-reference/mcp/connect-your-client) Cursor ve Codex için de talimatlar var. OAuth desteklemeyen istemciler markaya özel bir API anahtarı kullanabilir. Ardından ajana `get_brew_capabilities` aracını çağırtın; böylece tahmin yürütmek yerine canlı araç listesini okur.

### Envanteri çıkarın

Ajandan bir liste isteyin:

```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.
```

“Unknown” bölümünü silmeyin. Kimlik doğrulama onayları ve Stripe makbuzları kodunuzdaki gönderim çağrıları değildir, ama listede yer almaları gerekir. `EMAILS.md` dosyasını sürüm kontrolüne ekleyin. Burada dursanız bile, dün elinizde olmayan bir listeye sahip olursunuz.

### Her şablonu gerçekçi verilerle HTML’e dönüştürün

İçe aktarma için en iyi girdi, kodunuzun zaten ürettiği HTML’dir. React Email’in [render yardımcı fonksiyonu](https://react.email/docs/utilities/render) bu HTML’i bir metin dizesi olarak verir:

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

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

Eski projeler `render` fonksiyonunu `@react-email/render` paketinden içe aktarır. Bu e-postaları ekibiniz inceleyeceği için `"test"` yerine gerçekçi örnek veriler kullanın. Ajana, her şablonun HTML çıktısını bir dosyaya yazan bir betik hazırlatın.

Brew, bir React Email modülünü `format: "jsx"` ile doğrudan da içe aktarabilir; modülü çalıştırmadan, statik olarak okur. Props değerleri varsayılan değerlerine ya da `PreviewProps` değerlerine çözümlenir, diğer yerel dosyalardan içe aktarılan bileşenler atlanır ya da reddedilir, `await` ve benzeri yapılar ise kabul edilmez. Kendi içinde bağımsız tek bir dosyanın ötesine geçtiğinizde HTML çıktısını kullanın.

### Her şeyi tek bir yerde toplayın

Ardından her HTML çıktısını, ürün alanı başına bir `groupName` değeriyle `import_email` aracı üzerinden içe aktarın:

```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.
```

Geliştiriciler haklı olarak içe aktarmanın e-postalarını yeniden yazıp yazmadığını soruyor. Yazmıyor:

- Bir yapay zekâ modeli değil, deterministik bir derleyicidir ve kredi harcamaz.
- Düzenlenebilir bir Brew tasarımı üretir; gönderimde ise içe aktarılan satırlar için orijinal kaynak HTML korunur.
- Herkese açık görselleri yeniden barındırır; getiremediği görselleri `assetReport` listeler.
- Betikleri, iframe öğelerini, formları ve olay işleyicilerini kaldırır; mevcut açılma izleme piksellerini etkisiz hâle getirir.
- Her kaynak en fazla 5 MB olabilir. URL ile içe aktarma yoktur: Araç doğrudan HTML kodunu alır; `baseUrl` ise yalnızca göreli görsel yollarını çözümler.

### E-postaları ekip olarak inceleyin

Her grubun bir tuvali olur. `brew.new/emails/onboarding` adresini açtığınızda serinin tamamı yan yana durur; masaüstü ve mobil görünüm arasında geçiş yapabilir, her tasarıma yorum bırakabilirsiniz. Tuval bağlantıları, Brew çalışma alanınızda oturum açmış kişiler için çalışır. Herkese açık bir paylaşım bağlantısı olmadığından inceleyecek kişileri davet edin.

Sorunlar tam da burada ortaya çıkar: iki farklı logo, çalışmayan bir buton bağlantısı, üç ayrı alt bilgi. Herhangi bir şeyi düzeltmeden önce `audit_email` aracını çalıştırın. Bu araç bağlantıları, görselleri, yasal uyumluluğu, yükleme boyutunu, istemci desteğini, erişilebilirliği ve metni kontrol eder; tam bir denetim 5 kredi tutar. Kayıtlı bir `emailId` değerini ya da ham HTML’i kabul ettiği için içe aktarmadığınız e-postaları da denetleyebilirsiniz. [Gönderim öncesi kalite kontrol listemiz](/tr/blog/pre-send-email-qa-checklist) her kontrolü ve eşiği açıklıyor.

Artık elinizde bir envanter, tuvalde duran tüm e-postalar ve bir sorun listesi var; kodunuz ise her şeyi eskisi gibi göndermeye devam ediyor.

## Adım 2: Geçiş

Bu aşamada bazı e-postaları kodunuz yerine Brew gönderir. Her e-postanın taşınması gerekmez; belirli bir e-posta türünü ise her an yalnızca tek bir sistem gönderir.

### Kavramları eşleştirin

| Bugün kodunuzda | Brew içinde |
| --- | --- |
| `resend.emails.send({ react: <WelcomeEmail /> })` | Bir tetikleyici olayı ve tek bir “E-posta gönder” adımı içeren yayımlanmış bir otomasyon |
| Bileşene aktarılan props değerleri | `{{ firstName \| there }}` ya da `{{ trigger.planName }}` gibi birleştirme etiketleri olarak kullanılan tetikleyici payload alanları |
| `from` adresinin alan adı | Brew içinde doğrulanmış bir gönderim alan adı |
| Kullanıcılar tablonuz | REST API ya da bir entegrasyon üzerinden oluşturulan kişiler |
| Bir seriyi gönderen bir cron işi | Bekle, Filtre ve E-posta gönder adımlarından oluşan bir otomasyon |

### 1. Gönderim alan adını ayarlayın

Ajan, DNS kayıtları için `create_domain` aracını, kayıtlar yayıldıktan sonra da `verify_domain` aracını çağırır. MCP ve API üzerinden oluşturulan alan adları pazarlama alan adlarıdır. Şifre sıfırlama e-postaları ve makbuzlar için Brew uygulamasında gönderim amacı işlemsel olan bir alan adı kurun: Bu alan adı abonelikten çıkma alt bilgisini eklemez ve abonelikten çıkmış kişilere de teslim eder. Kayıtların ayrıntıları [Gönderim alan adınızı doğrulayın](https://docs.brew.new/get-started/verify-your-sending-domain) sayfasında.

### 2. Kişileri aktarın

MCP kişileri günceller ama oluşturmaz. Kişileri sunucunuzdan `POST /v1/contacts` ile oluşturun; bu uç nokta tek bir kişiyi ya da en fazla 1.000 kişilik bir partiyi ekler veya günceller (upsert) ve özel alanları otomatik olarak oluşturur. Dilerseniz uygulamaya bir CSV dosyası da yükleyebilirsiniz. Bir tetikleyiciyi tetiklemek de payload içindeki kişiyi oluşturur ya da günceller. Yalnızca sizden haber almayı kabul etmiş kişileri içe aktarın. Brew için nelerin gerektiğini [Kişi ekleme](https://docs.brew.new/audience/add-contacts) sayfası açıklıyor.

### 3. Önizleme değerlerini birleştirme etiketleriyle değiştirin

Gözden kaçması kolay bir ayrıntı: İçe aktarılan hoş geldin e-postasında kelimesi kelimesine “Hi Ana” yazıyor. Her örnek değeri `{{ firstName | there }}` gibi bir birleştirme etiketiyle değiştirin; dikey çizgiden sonraki metin varsayılan değerdir. Bunun için Brew editörünü kullanın ya da ajana yalnızca bu değişikliklerle sınırlı bir `edit_email` çağrısı yaptırın. Bu, kredi harcayan bir yapay zekâ düzenlemesidir; önizlemeyi kontrol edin. Söz dizimi [Birleştirme etiketleri](https://docs.brew.new/create-emails/merge-tags) sayfasında.

### 4. Tetikleyiciyi ve otomasyonu oluşturun

Ajan, `save_trigger` aracıyla bir tetikleyici oluşturur. Payload şeması alıcının `email` alanını içermelidir. Tetikleyiciye tipli bir sözleşme tanımlayın (`infer_payload_contract` örnek bir payload verisinden taslağını çıkarır, `set_trigger_contract` kaydeder), ardından sözleşmeyi `get_trigger_contract` ile `ts` ya da `zod` biçiminde alın; böylece sunucunuz ve Brew, payload yapısı konusunda aynı şeyi bilir.

Ardından `save_automation` aracı tetikleyiciyi ve e-postayı, alan adını ve konu satırını içeren bir “E-posta gönder” adımını kurar. Otomasyon, siz yayımlayana kadar taslak olarak kalır. `dryRun` grafı doğrular, `test_automation` adresinize gerçek bir kopya gönderir, `check_trigger` ise üretim ortamı herhangi bir şeyi tetiklemeden önce yayımlanmış bir otomasyonun tetikleyiciyi dinlediğini doğrular.

### 5. Çağrının yapıldığı yeri değiştirin

Uygulama kodunuzdaki tek değişiklik budur. Önce, Resend ile:

```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} />,
})
```

Sonra, Brew ile:

```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`, sunucu isteği reddettiğinde bile hata fırlatmaz; bu yüzden `res.ok` değerini kontrol edin ve yeniden denemeyi ya da uyarıyı iş çalıştırıcınıza bırakın. Uç nokta, olayı kabul ettiğinde `202`, tetikleyiciyi dinleyen yayımlanmış bir otomasyon yoksa `422` döndürür.

`Idempotency-Key` değerini kullanıcı ya da sipariş kimliği gibi sabit bir kimlikten oluşturun. Tekrarlanan bir anahtar, e-postayı ikinci kez göndermek yerine ilk çalıştırmayı döndürür; aynı anahtarı farklı bir istek gövdesiyle yeniden kullanırsanız `409 IDEMPOTENCY_CONFLICT` yanıtı alırsınız. Brew bir tetiklemenin anahtarını, tetikleyicinin alındı kaydı durduğu sürece saklar; bu süre, birçok API’nin kullandığı 24 saat değil, 90 gündür. Anahtarları kalıcı kabul edin ve her eylem için ayrı bir anahtar kullanın. Zaman damgasından üretilen bir anahtar her yeniden denemede değişir; haftalar sonra farklı bir eylem için yeniden kullanılan bir anahtar ise gönderim yerine önceki çalıştırmanın tekrarı olarak işlenebilir.

Üretim ortamındaki kod bu uç noktayı MCP ile değil, bir API anahtarıyla çağırır. OAuth üzerinden MCP canlı gönderimler, tetiklemeler ve yayımlama için bir kişinin onayını ister: Bu bir sohbet için doğru, bir istek işleyici (request handler) için yanlıştır. Bu iş bölümünü [MCP ve REST API rehberi](/tr/blog/mcp-vs-rest-api-for-email-marketing-which-workflow-should-you-use) anlatıyor.

### 6. Geçişi e-posta e-posta yapın

1. Yeni çağrıyı eskisinin yanına, bir özellik bayrağının (feature flag) arkasına koyun; böylece geri dönmek yalnızca bir yapılandırma değişikliği olur.
2. Bir yaşam döngüsü hatırlatması gibi riski düşük bir e-postayla başlayın ve birkaç gün boyunca teslimleri ve tıklamaları izleyin.
3. Ardından, tetikleyicileri doğru payload verileriyle tetiklendiğinde serileri taşıyın.
4. İşlemsel e-postaları en son taşıyın. Kullanıcıların ilk fark ettiği şey bozuk şifre sıfırlama e-postaları ve sihirli bağlantılardır.
5. Sorunsuz geçen bir haftadan sonra eski yolu silin ve `EMAILS.md` dosyasını güncelleyin.

[Loops’tan geçiş rehberi](https://docs.brew.new/migrations/loops) de aynı yöntemi izliyor.

### Kodda kalması gerekenler

Supabase’in onay ve sihirli bağlantı e-postaları Supabase’ten gelmeye devam eder. Brew içindeki Supabase entegrasyonu, Supabase Auth e-postalarının yerini almak yerine otomasyonları başlatmak için kullanıcı olaylarını dinler. Gönderim anında karmaşık verilerden oluşturulan kalem kalem bir fatura da kodda daha basit kalabilir; bu yüzden önce birleştirme etiketlerinizin neye ihtiyaç duyduğunu kontrol edin. Bu e-postalar da görünür kalsın diye içe aktarılmış kopyalarını tuvalde tutun.

## Adım 3: Optimizasyon

**Setin tamamını denetleyin.** Bir grubun tamamında `audit_email` aracını çalıştırın, sorunları düzeltin ve büyük gönderimlerden önce denetimi yeniden çalıştırın.

**Gerçek istemcileri kontrol edin.** `test_email_rendering`, belirli e-posta istemcilerinden ve cihazlardan karanlık mod dâhil ekran görüntüleri döndürür; her çalıştırma 10 kredi tutar. `create_inbox_placement_test` ise test gelen kutularına gerçek bir kopya gönderir ve e-postanın nereye düştüğünü raporlar; bu da 10 kredi tutar.

**Seti tutarlı hâle getirin.** Tutarsızlıkları editörde düzeltin ya da yeni bir alt bilgi gibi tek bir değişikliği ajana `edit_email` ile bir grubun tamamına uygulatın.

**Sonuçları okuyun.** `get_email_analytics` gönderimleri ve etkileşimi raporlar. `list_insights` ise Brew tarafından hesabınızda tespit edilen bulguları, her birinin dayandığı metriklerle birlikte döndürür.

**Versiyonları bilinçli olarak test edin.** Tuvalde versiyonlar oluşturun ve kişileri, örneğin tıklayıp tıklamadıkları gibi davranışlara göre bir Dallanma adımıyla yönlendirin. Brew, rastgele bölmede kazananı sizin yerinize seçmez; bu yüzden versiyonları kendiniz karşılaştırın.

**Yeni e-postaları görünür tutun.** `AGENTS.md` ya da benzeri bir dosyaya şu kuralı ekleyin: Yeni ürün e-postaları Brew içinde oluşturulur, bir tetikleyiciyle tetiklenir ve aynı pull request içinde `EMAILS.md` dosyasına eklenir.

## Sıkça sorulan sorular

### Brew, React Email şablonlarını içe aktarabilir mi?

Evet. `import_email` aracı HTML çıktısını, MJML’i, kaydedilmiş bir `.eml` mesajını ya da bir React Email modülünü kabul eder. En güvenilir seçenek HTML çıktısıdır, çünkü modül statik olarak okunur: Props değerleri varsayılan ya da önizleme değerlerine dönüşür, diğer yerel dosyalardan içe aktarılan bileşenler ise dışarıda kalır.

### Brew, Resend hesabıma bağlanıyor mu?

Hayır. Resend entegrasyonu, Resend’den içe aktarma ya da Resend’e dışa aktarma yoktur. Kodunuzun ürettiği HTML’i içe aktarırsınız; geçiş yaparsanız da kodunuz Resend’i çağırmak yerine bir Brew tetikleyicisini tetikler.

### Sunucumdaki Brew çağrıları MCP üzerinden mi yapılmalı?

Hayır. MCP’yi kurulum, içe aktarma, denetim ve test için bir kodlama ajanı üzerinden kullanın. Üretim ortamındaki kod REST API’yi bir API anahtarıyla çağırmalıdır; çünkü OAuth bağlantılarında MCP, canlı gönderimler ve tetiklemeler için bir kişinin onayını ister.

### Bir e-postayı içe aktarmak görünümünü değiştirir mi?

İçe aktarma, e-postayı düzenlenebilir bir Brew tasarımına dönüştürür ve gönderim için orijinal kaynak HTML’i korur. Betikleri, iframe öğelerini, formları ve olay işleyicilerini kaldırır; mevcut izleme piksellerini etkisiz hâle getirir ve herkese açık görselleri yeniden barındırır. Her birini Brew içinden göndermeden önce tuvalde, masaüstü ve mobil görünümde inceleyin.
