Menguji pengiriman email (sandbox email)
Sandbox email menguji handler webhook, logika supresi, dan hasil pengiriman tanpa mengirim ke kotak masuk sungguhan. Kirim melalui API seperti biasa ke alamat di messagebird.dev. Bagian lokal alamat menentukan hasilnya: bounce@messagebird.dev menghasilkan bounce dan delivered@messagebird.dev terkirim.
Pengiriman sandbox menggunakan jalur penerimaan, event, dan webhook yang biasa. Pengiriman ini mengembalikan respons 202 yang sama dan menghasilkan bentuk payload event per penerima yang sama seperti pengiriman produksi. Payload tidak memiliki penanda uji. Pesan tidak mencapai infrastruktur pengiriman eksternal atau kotak masuk sungguhan. Bounce dan keluhan simulasi tidak memengaruhi reputasi pengirim dan tidak ditulis ke daftar supresi, sehingga Anda dapat menggunakan ulang alamat tersebut.
Sandbox tidak memerlukan penyiapan: tanpa tombol, tanpa mode uji, tanpa kunci API khusus. Sandbox dipicu semata-mata oleh alamat penerima, pada endpoint pengiriman biasa (POST /v1/email/messages dan POST /v1/email/batches) dan pada broadcast. Kontak dalam audiens yang alamatnya merupakan alamat sandbox akan disimulasikan, bukan dikirimi pesan. Dengan cara ini Anda dapat melatih kampanye tanpa mengirim email ke siapa pun. Penerima simulasi tetap dihitung dalam jatah pengiriman Anda, sehingga latihan ini menguji kuota yang sama dengan pengiriman sungguhan.
Alamat ajaib
Semua alamat berada di @messagebird.dev. Bagian lokal alamat menentukan hasilnya:
| Alamat | Hasil simulasi | Urutan webhook | Catatan |
|---|---|---|---|
delivered@ | Server email penerima menerima pesan | email.accepted → email.processed → email.delivered | Jalur normal tanpa kendala |
bounce@ / hardbounce@ | Hard bounce: SMTP 550, 5.1.1 Unknown User, kelas bounce 10 | email.accepted → email.processed → email.bounced dengan bounce_type: "hard" | Tidak ada penulisan ke daftar supresi, sehingga alamat tetap dapat digunakan ulang |
softbounce@ | Soft bounce: SMTP 451, 4.3.0 Temporary failure, please retry, kelas 20 | email.accepted → email.processed → email.bounced dengan bounce_type: "soft" | Soft bounce tidak pernah memicu supresi, baik nyata maupun simulasi |
deferred@ / delay@ | Penundaan: SMTP 451, 4.2.1 Mailbox temporarily unavailable, will retry, kelas 21 | email.accepted → email.processed → email.deferred | Penundaan simulasi bersifat final: tidak ada percobaan ulang setelahnya, sehingga penerima tetap deferred |
complaint@ / spam@ | Penerima melaporkan pesan sebagai spam (feedback_type: "abuse") | email.accepted → email.processed → email.complained | Tidak ada penulisan supresi; alamat dapat digunakan ulang |
suppressed@ | Penerima diperlakukan seolah sudah ada di daftar supresi Anda | email.accepted → email.rejected dengan rejection_reason: "recipient_suppressed" | Pemrosesan dihentikan lebih awal, persis seperti penerima yang disupresi sungguhan: tidak ada email.processed, tidak ada event pengiriman |
reject@ | Pesan ditolak sebelum ada upaya pengiriman | 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 pengiriman tunggal. Pada broadcast, hilangkan email.accepted di awal: penerima broadcast diterima secara tersendiri, tetapi kami mencatat event tersebut tanpa mengirim webhook untuknya, 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 menjelaskan aturan ini secara lengkap.
Satu pengiriman dapat mencampur penerima sandbox dan penerima sungguhan. Setiap penerima memiliki siklus hidupnya sendiri: penerima sungguhan menerima pesan seperti biasa, sedangkan penerima sandbox disimulasikan.
Event yang sama juga muncul di linimasa pesan dalam log email dan di API event, sehingga Anda dapat menjalankan sandbox tanpa endpoint webhook dan membaca hasilnya kembali.
Aturan pengalamatan
- Deteksi hanya berdasarkan local-part, dan hanya pada domain
messagebird.dev.bounce@yourdomain.comadalah alamat biasa. - Hanya local part yang tercantum dalam tabel alamat khusus yang bersifat khusus. Alamat lain di
messagebird.devdiperlakukan sebagai penerima biasa. Jika Anda mengirim dari domain onboarding bersama, alamat tersebut harus milik anggota workspace yang terverifikasi. - Pencocokan tidak membedakan huruf besar dan kecil:
Bounce@messagebird.devdanbounce@messagebird.devberperilaku sama. - Subaddressing
+labeldihapus sebelum pencocokan:bounce+signup-flow@messagebird.devtetap mengalami bounce. Gunakan label untuk menghubungkan kasus uji; alamat lengkap beserta labelnya muncul di event dan webhook Anda, sehingga setiap pengujian dapat menandai penerimanya sendiri.
Panduan: menyimulasikan bounce dari awal hingga akhir
Anda tidak memerlukan domain pengirim yang terverifikasi. Kirim dari onboarding@messagebird.dev, seperti dijelaskan dalam Kirim email pertama Anda. Alamat sandbox yang dikenali dikecualikan dari batasan anggota terverifikasi pada domain onboarding, tetapi tetap dihitung dalam kuota harian domain tersebut.
Pastikan Anda memiliki endpoint webhook yang berlangganan event email (lihat Webhook), 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 diawali 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 kode Anda yang sebenarnya. Endpoint webhook Anda kemudian menerima email.accepted, email.processed, dan terakhir email.bounced. Setiap event menyertakan kembali tags dan metadata dari pengiriman tersebut (null jika pengiriman tidak memilikinya), dan payload email.bounced memuat klasifikasi bounce lengkap:
{
"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 dijelaskan dalam referensi event. Tidak ada penanda simulator di bagian mana pun dalam payload: jenis dan bentuk event sama persis dengan yang dihasilkan hard bounce sungguhan. Satu-satunya hal yang menandainya sebagai simulasi adalah alamat penerima itu sendiri. Jadi, jika handler Anda perlu memperlakukan trafik uji secara khusus, gunakan domain penerima messagebird.dev sebagai acuannya.
Untuk memverifikasi bahwa penerima yang sudah disupresi 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 maupun event pengiriman. Perilaku ini sama persis dengan penerima yang disupresi sungguhan: pesan diterima, lalu dihentikan saat pemrosesan sebelum dikirim.
Yang dilakukan dan tidak dilakukan sandbox
- Tidak ada penulisan ke daftar supresi. Hard bounce dan keluhan simulasi tidak menambahkan penerima ke daftar supresi Anda; karena itulah alamat tersebut dapat digunakan ulang. Webhook Anda tetap terpicu (
email.bounced,email.complained), sehingga logika supresi Anda sendiri tetap teruji sepenuhnya. Untuk menguji jalur penolakan penerima yang sudah disupresi, gunakan alamat khusussuppressed@. - Tidak pernah ada pengiriman sungguhan. 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 permintaan tetap berlaku. Pengiriman sandbox menggunakan endpoint biasa, sehingga pemeriksaan skema, batas ukuran, dan aturan header tetap menolak permintaan yang tidak valid seperti biasa. Yang dilewati sandbox adalah semua tahap setelah serah terima: perilaku rendering dan pengiriman setelah titik tersebut tidak diuji.
- Pembukaan dan klik tidak disimulasikan. Alamat khusus menyimulasikan hasil transmisi, dan tidak ada yang membuka pesan tersebut, sehingga
email.openeddanemail.clickedhanya berasal dari email sungguhan. - Statistik mencakup lalu lintas sandbox. Pengiriman sandbox dihitung dalam statistik agregat workspace Anda serta tingkat bounce dan keluhan. Banyaknya bounce di sandbox mendistorsi dasbor Anda, tetapi reputasi Anda tetap tidak berubah.
Langkah berikutnya
- 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
- Supresi: cara kerja daftar supresi yang sebenarnya
- Menguji email tanpa mengirim spam ke siapa pun: video yang menjelaskan alamat sandbox dan event yang dihasilkan masing-masing alamat
Sumber daya terkait
Lanjutkan dengan dokumentasi, panduan, dan contoh untuk topik ini.