Menguji pengiriman email (mail sandbox)
Mail sandbox menguji webhook handler, logika supresi, dan hasil pengiriman tanpa mengirim ke kotak masuk nyata. Kirim melalui API biasa ke alamat di messagebird.dev. Local part menentukan hasilnya: bounce@messagebird.dev menghasilkan bounce dan delivered@messagebird.dev berhasil terkirim.
Pengiriman sandbox menggunakan jalur penerimaan, event, dan webhook yang biasa. Sandbox mengembalikan respons 202 yang sama dan menghasilkan bentuk payload event per penerima yang sama seperti pengiriman produksi. Payload tidak memiliki flag pengujian. Pesan tidak mencapai infrastruktur pengiriman eksternal atau kotak masuk nyata. Bounce dan komplain simulasi tidak memengaruhi reputasi pengiriman atau menulis ke daftar supresi, sehingga Anda dapat menggunakan kembali alamat-alamat tersebut.
Sandbox tidak memerlukan pengaturan: tanpa toggle, tanpa mode pengujian, tanpa kunci API khusus. Sandbox aktif murni berdasarkan alamat penerima, pada endpoint pengiriman biasa (POST /v1/email/messages dan POST /v1/email/batches) dan pada broadcast: kontak di audience yang alamatnya adalah alamat sandbox disimulasikan alih-alih dikirim, dan inilah cara Anda melatih kampanye tanpa mengirim email ke siapa pun. Penerima simulasi tetap dihitung terhadap kuota pengiriman Anda, sehingga latihan ini menggunakan kuota yang sama dengan pengiriman sesungguhnya.
Alamat magic
Semua alamat berada di @messagebird.dev. Local-part menentukan hasilnya:
| Alamat | Hasil simulasi | Urutan webhook | Catatan |
|---|---|---|---|
| delivered@ | Server email penerima menerima pesan | email.accepted → email.processed → email.delivered | Jalur sukses |
| bounce@ / hardbounce@ | Hard bounce: SMTP 550, 5.1.1 Unknown User, bounce class 10 | email.accepted → email.processed → email.bounced dengan bounce_type: "hard" | Tidak menulis ke daftar supresi, sehingga alamat tetap dapat digunakan kembali |
| softbounce@ | Soft bounce: SMTP 451, 4.3.0 Temporary failure, please retry, class 20 | email.accepted → email.processed → email.bounced dengan bounce_type: "soft" | Soft bounce tidak pernah menyupresi, baik nyata maupun simulasi |
| deferred@ / delay@ | Deferral: SMTP 451, 4.2.1 Mailbox temporarily unavailable, will retry, class 21 | email.accepted → email.processed → email.deferred | Deferral simulasi bersifat terminal: tidak ada percobaan ulang, sehingga penerima tetap deferred |
| complaint@ / spam@ | Penerima melaporkan pesan sebagai spam (feedback_type: "abuse") | email.accepted → email.processed → email.complained | Tidak menulis ke daftar supresi; dapat digunakan kembali |
| suppressed@ | Penerima diperlakukan seolah sudah ada di daftar supresi Anda | email.accepted → email.rejected dengan rejection_reason: "recipient_suppressed" | Diputus saat pemrosesan persis seperti penerima tersupresikan yang sesungguhnya: tidak ada email.processed, tidak ada event pengiriman |
| reject@ | Pesan ditolak sebelum upaya pengiriman apa pun | email.accepted → email.rejected dengan rejection_reason: "transmission_failed" | Tidak ada email.processed atau event pengiriman setelahnya |
Urutan di atas adalah webhook yang dihasilkan oleh satu kali pengiriman. Pada broadcast, hilangkan email.accepted di awal: penerima broadcast diterima secara tersendiri, tetapi event tersebut dicatat tanpa mengirim webhook, sehingga setiap urutan dimulai dari event berikutnya, yaitu email.processed pada jalur pengiriman dan email.rejected untuk suppressed@ dan reject@. Semua event setelahnya identik, dan referensi event membahas aturan ini secara lengkap.
Satu pengiriman dapat mencampur penerima sandbox dan nyata. Setiap penerima memiliki siklus hidupnya sendiri: penerima nyata dikirim seperti biasa, penerima sandbox disimulasikan.
Event yang sama juga muncul di timeline pesan dalam log email dan di event API, sehingga Anda dapat menggunakan sandbox tanpa endpoint webhook dan membaca hasilnya langsung.
Aturan pengalamatan
- Deteksi berdasarkan local-part saja, dan hanya pada domain messagebird.dev. bounce@yourdomain.com adalah alamat biasa.
- Hanya local part dalam tabel alamat magic yang bersifat magic. Alamat lain di messagebird.dev adalah penerima biasa. Saat Anda mengirim dari domain onboarding bersama, alamat tersebut harus milik anggota workspace yang terverifikasi.
- Pencocokan tidak peka huruf besar/kecil: Bounce@messagebird.dev dan bounce@messagebird.dev berperilaku identik.
- Subaddressing +label dihapus sebelum pencocokan: bounce+signup-flow@messagebird.dev tetap menghasilkan bounce. Gunakan label untuk mengorelasikan kasus uji; alamat lengkap, termasuk label, muncul di event dan webhook Anda, sehingga setiap sesi pengujian dapat menandai penerimanya sendiri.
Panduan langkah demi langkah: simulasi bounce dari awal hingga akhir
Anda tidak memerlukan domain pengirim terverifikasi. Kirim dari onboarding@messagebird.dev, seperti dijelaskan di Kirim email pertama Anda. Alamat sandbox yang dikenali dikecualikan dari pembatasan anggota terverifikasi pada domain onboarding, tetapi tetap dihitung terhadap kuota harian-nya.
Pastikan Anda memiliki endpoint webhook yang berlangganan event email (lihat Webhooks), lalu kirim:
const msg = await bird.email.send({
from: { email: "onboarding@messagebird.dev", name: "Bird" },
to: ["bounce+signup-flow@messagebird.dev"],
subject: "Sandbox bounce test",
html: "<p>This message will hard-bounce.</p>",
tags: [{ name: "flow", value: "signup" }],
metadata: { test_run: "docs-capture-1" },
});
console.log(msg.id, msg.status); // "em_…", "accepted"msg = client.email.send(
from_={"email": "onboarding@messagebird.dev", "name": "Bird"},
to=["bounce+signup-flow@messagebird.dev"],
subject="Sandbox bounce test",
html="<p>This message will hard-bounce.</p>",
tags=[{"name": "flow", "value": "signup"}],
metadata={"test_run": "docs-capture-1"},
)
print(msg.id, msg.status)package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
msg, err := client.Email.Send(context.Background(), bird.EmailSendParams{
From: "onboarding@messagebird.dev",
To: []string{"bounce+signup-flow@messagebird.dev"},
Subject: "Sandbox bounce test",
HTML: "<p>This message will hard-bounce.</p>",
Tags: []bird.Tag{{Name: "flow", Value: "signup"}},
Metadata: map[string]any{"test_run": "docs-capture-1"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$message = $bird->email->send(
from: 'Bird <onboarding@messagebird.dev>',
to: ['bounce+signup-flow@messagebird.dev'],
subject: 'Sandbox bounce test',
html: '<p>This message will hard-bounce.</p>',
);
echo $message->getId(), ' ', $message->getStatus();bird email send \
--from onboarding@messagebird.dev \
--html '<p>This message will hard-bounce.</p>' \
--metadata '{"test_run":"docs-capture-1"}' \
--subject 'Sandbox bounce test' \
--tag flow=signup \
--to bounce+signup-flow@messagebird.devcurl -X POST "https://us1.platform.bird.com/v1/email/messages" \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "onboarding@messagebird.dev",
"to": ["bounce+signup-flow@messagebird.dev"],
"subject": "Sandbox bounce test",
"html": "<p>This message will hard-bounce.</p>",
"tags": [{ "name": "flow", "value": "signup" }],
"metadata": { "test_run": "docs-capture-1" }
}'Jika kunci Anda dimulai dengan bk_eu1_, panggil https://eu1.platform.bird.com sebagai gantinya.
API merespons 202 Accepted dengan ID pesan em_*, tidak dapat dibedakan dari pengiriman produksi. Itulah tujuannya: jalur kode yang Anda uji adalah jalur nyata Anda. Endpoint webhook Anda kemudian menerima email.accepted, email.processed, dan terakhir email.bounced. Setiap event menyertakan tags dan metadata dari pengiriman (null jika pengiriman tidak memilikinya), dan payload email.bounced memiliki klasifikasi bounce lengkap:
Contoh kode
{
"type": "email.bounced",
"timestamp": "2026-07-23T14:51:00.362Z",
"data": {
"email_id": "em_01ky7qanhrejer0bn34v38hrxh",
"recipient_id": "er_01ky7qanhrejds4decpk83q5qq",
"workspace_id": "ws_01ky7m21hjffh9s8kq76gyb1xf",
"recipient": "bounce+signup-flow@messagebird.dev",
"recipient_role": "to",
"bounce_type": "hard",
"bounce_class": 10,
"bounce_code": "550",
"bounce_description": "5.1.1 Unknown User",
"sending_ip": null,
"tags": [{ "name": "flow", "value": "signup" }],
"metadata": { "test_run": "docs-capture-1" },
"broadcast_id": null
}
}Arti setiap field ada di referensi event. Tidak ada flag simulator di mana pun dalam payload: tipe dan bentuk event persis sama dengan yang dihasilkan hard bounce nyata. Satu-satunya yang mengidentifikasi bahwa ini adalah simulasi adalah alamat penerima itu sendiri, jadi jika handler Anda perlu membedakan trafik pengujian, gunakan domain penerima messagebird.dev sebagai kunci.
Untuk memverifikasi bahwa penerima yang sudah tersupresikan tidak pernah dikirimi pesan, ulangi pengiriman dengan suppressed@messagebird.dev. Pastikan Anda menerima email.accepted, lalu email.rejected dengan rejection_reason: "recipient_suppressed". Anda seharusnya tidak menerima email.processed atau event pengiriman. Ini mencerminkan penerima tersupresikan yang sesungguhnya: pesan diterima, lalu diputus saat pemrosesan sebelum pengiriman apa pun.
Apa yang dilakukan dan tidak dilakukan sandbox
- Tidak menulis ke daftar supresi. Hard bounce dan komplain simulasi tidak menambahkan penerima ke daftar supresi Anda; inilah yang membuat alamat tetap dapat digunakan kembali. Webhook Anda tetap aktif (email.bounced, email.complained), sehingga logika supresi Anda sendiri tetap teruji sepenuhnya. Untuk menguji jalur penolakan penerima yang sudah tersupresikan, gunakan alamat khusus suppressed@.
- Tidak ada pengiriman nyata, selamanya. Penerima sandbox dicegat sebelum pesan mencapai infrastruktur pengiriman. Tidak ada yang ditransmisikan, tidak ada kotak masuk yang terlibat, dan reputasi pengiriman Anda tidak terpengaruh.
- Validasi request tetap berlaku. Pengiriman sandbox menggunakan endpoint biasa, sehingga pemeriksaan skema, batas ukuran, dan aturan header menolak request yang tidak valid seperti biasa. Yang dilewati sandbox adalah semua proses setelah serah terima: rendering dan perilaku pengiriman setelah titik itu tidak dijalankan.
- Open dan klik tidak disimulasikan. Alamat magic mensimulasikan hasil transmisi, dan tidak ada yang membuka pesan, sehingga email.opened dan email.clicked hanya berasal dari email nyata.
- Statistik mencakup trafik sandbox. Pengiriman sandbox dihitung terhadap statistik agregat workspace Anda serta rasio bounce dan komplain. Bounce sandbox yang banyak dapat menggeser dasbor Anda, tetapi reputasi Anda tetap tidak terpengaruh.
Langkah selanjutnya
- Events: kosakata event lengkap dan siklus hidup per penerima
- Webhooks: berlangganan, verifikasi tanda tangan, dan percobaan ulang
- Kirim email pertama Anda: panduan cepat domain onboarding yang menjadi dasar panduan ini
- Suppressions: cara kerja daftar supresi yang sesungguhnya
- Menguji email tanpa mengirim spam ke siapa pun: video yang membahas alamat sandbox dan event yang dihasilkan masing-masing
Sumber daya terkait
Lanjutkan dengan dokumentasi, panduan, dan contoh untuk topik ini. Sumber daya tersedia dalam bahasa Inggris.