Sign inGet Started

Mengirim email

POST /v1/email/messages mengirim satu email. Sediakan pengirim, penerima, dan konten dalam payload JSON. API mengembalikan 202 Accepted dengan message ID, lalu mengirim email secara asinkron. Lihat referensi API untuk skema lengkapnya.

Pengiriman minimal

Payload valid terkecil adalah from, minimal satu penerima to, sebuah subject, dan body (html, text, atau keduanya). Alamat from harus berada di domain yang sudah Anda verifikasi di workspace ini, atau di domain onboarding.

const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  subject: "Hello from Bird",
  html: "<p>My first Bird email.</p>",
});
console.log(msg.id, msg.status); // "em_…", "accepted"

Gunakan host regional Anda (https://us1.platform.bird.com atau https://eu1.platform.bird.com) dengan kunci bk_{region}_... yang sesuai.

Contoh pengiriman menggunakan delivered@messagebird.dev, sebuah alamat sandbox yang selalu menerima email. API menolak domain placeholder dengan 422: example.com, example.net, example.org, example.edu, test.com, dan apa pun di bawah TLD cadangan .test, .example, .invalid, atau .localhost. Pengiriman ke domain ini hanya akan bounce, yang merugikan reputasi pengirim Anda.

Mengirim sebelum Anda memverifikasi domain

Selama onboarding, Anda dapat mengirim dari domain onboarding bersama kami, onboarding@messagebird.dev. Pengiriman tersebut melewati pemeriksaan domain tetapi hanya menjangkau anggota terverifikasi di workspace Anda sendiri dan alamat sandbox, dengan batas penerima harian. Quickstart memuat aturan dan batas pastinya.

Menyusun payload

Penerima

to, cc, dan bcc masing-masing menerima hingga 50 alamat, dan to membutuhkan minimal satu. Setiap entri berupa string email biasa, string mailbox RFC 5322 (Jane <jane@acme.com>), atau objek dengan display name opsional.

Penerima yang ada di daftar supresi workspace tidak membuat permintaan gagal. Permintaan tetap mengembalikan 202, dan setiap penerima yang disupresi muncul di endpoint baca sebagai status: rejected dengan alasan recipient_suppressed, termasuk saat semua penerima pada pengiriman tersebut disupresi.

Konten

subject wajib untuk pengiriman inline, maksimal 998 karakter. Sediakan html, text, atau keduanya, masing-masing maksimal 524.288 karakter. Kirim keduanya jika memungkinkan: klien yang tidak dapat merender HTML akan menggunakan bagian teks.

Untuk mempersonalisasi konten inline, taruh token {{ variable }} di subject atau body dan kirim nilainya di parameters, maksimal 16 KB setelah diserialisasi. Satu set nilai berlaku untuk semua penerima pengiriman, dan token tanpa key yang cocok dirender kosong. Untuk konten yang Anda gunakan berulang, kirim template sebagai gantinya.

Sertakan parameters, bahkan sebagai objek kosong ({}), agar subject dan body diproses sebagai Liquid. Hilangkan untuk mengirim token seperti {{ animal }} apa adanya. Setiap nama parameter adalah satu kata, seperti first_name; nama bertitik dan nama cadangan bird ditolak. Sintaks Liquid yang tidak valid serta tag atau filter yang tidak didukung mengembalikan 422.

Nilai yang disisipkan ke HTML di-escape agar tidak mengubah markup di sekitarnya. Untuk URL link atau gambar yang lengkap, gunakan {{ link }} tanpa url_encode. Untuk nilai di dalam query URL, encode nilai tersebut secara eksplisit, misalnya https://example.com/search?q={{ query | url_encode }}.

Reply-to dan header kustom

reply_to menerima 1 hingga 25 alamat, dalam format yang sama seperti penerima. Setiap balasan penerima dikirim ke semua alamat tersebut, jadi biasanya satu atau dua saja.

headers adalah objek string-ke-string untuk header Anda sendiri, misalnya {"X-Campaign": "spring-2026"}, maksimal 25 header dengan nilai hingga 998 karakter. Dua jenis header dikembalikan sebagai 422:

  • Header alamat dan platform. Atur alamat pesan melalui field khusus (from, to, cc, bcc, reply_to, subject). Nama-nama tersebut, dan header yang kami buat untuk Anda (Content-Type, Content-Transfer-Encoding, DKIM-Signature, Received, Return-Path), tidak dapat diatur di sini.
  • Nilai apa pun yang mengandung carriage return atau line feed.

Pengiriman transaksional menerima List-Unsubscribe dan List-Unsubscribe-Post. Pengiriman marketing secara default menggunakan berhenti berlangganan yang dikelola Bird.

Pelacakan

track_opens dan track_clicks keduanya default ke true. Atur salah satu ke false untuk melewatkan penyisipan open-pixel atau penulisan ulang link pada pengiriman ini. Flag ini tidak mengubah piksel pelacakan dan link yang sudah ada di konten yang Anda kirim. Pelacakan dan metrik membahas perubahan yang dilakukan masing-masing pada pesan.

Kategori dan IP pool

category mengklasifikasikan konten dan mengatur kebijakan supresi: marketing memblokir pengiriman pada setiap alasan supresi dan opt-out apa pun, dan transactional tetap mengirim meskipun ada supresi keluhan atau opt-out khusus marketing (opt-out yang berlaku untuk semua pesan tetap memblokirnya). Nilai default-nya adalah kategori template pada pengiriman template dan marketing untuk yang lain, jadi atur transactional secara eksplisit untuk tanda terima, reset kata sandi, dan email operasional lainnya. Kategori membahas pilihannya. Email yang dikirim melalui SMTP mengambil kategorinya dari konfigurasi SMTP pada key tersebut.

ip_pool_id memilih pool pengiriman: pool ID (ipp_...), atau ipp_shared untuk merutekan melalui pool bersama secara eksplisit. Hilangkan untuk menggunakan pool default organisasi Anda. Pool yang tidak dikenal, atau pool tanpa IP khusus yang tersedia untuk mengirim, ditolak dengan 422.

Referensi field

FieldTipeWajibBatas dan catatan
fromaddressyaHarus di domain terverifikasi, atau domain onboarding
toaddress[]ya1 hingga 50
cc, bccaddress[]tidakMasing-masing hingga 50
subjectstringpengiriman inlineMaksimal 998 karakter; hilangkan pada pengiriman template
html, textstringminimal satuMasing-masing maksimal 524.288 karakter; hilangkan pada pengiriman template
reply_toaddress[]tidak1 hingga 25; balasan masuk ke setiap alamat yang tercantum
headersobject (string → string)tidakMaksimal 25; nama cadangan ditolak (lihat header kustom)
parametersobjecttidakNilai untuk {{ tokens }} di konten inline; maksimal 16 KB setelah diserialisasi; dibagikan ke semua penerima
tags{name, value}[]tidakMaksimal 20; nama ≤ 32 karakter, nilai ≤ 64 karakter; hanya [A-Za-z0-9_-]; nama unik per pengiriman
metadataobjecttidakJSON arbitrer, maksimal 2 KB setelah diserialisasi
referencestringtidak1–255 karakter; dicocokkan persis oleh ?reference= pada daftar pesan
track_opensbooleantidakDefault true
track_clicksbooleantidakDefault true
categorystringtidakmarketing atau transactional; default ke kategori template pada pengiriman template, selain itu marketing
ip_pool_idstringtidakipp_... atau ipp_shared; hilangkan untuk menggunakan pool default organisasi Anda
templateobjecttidakKirim template yang dipublikasikan berdasarkan id atau slug, dengan parameters untuk variabelnya dan language opsional
attachmentsobject[]tidakMaksimal 20; lihat lampiran
scheduled_atRFC 3339 timestamptidakJadwalkan konten inline atau template; lihat pengiriman terjadwal

Mengirim dengan template

Template dapat menggunakan nilai bird.contact.* dari kontak yang cocok dengan setiap alamat To. Penerima Cc dan Bcc menerima salinan email yang dipersonalisasi untuk setiap penerima To. Ini juga berlaku di API batch dan untuk pengiriman terjadwal; lihat personalisasi dengan variabel untuk contoh dengan beberapa penerima.

Alih-alih konten inline, kirim template yang sudah dipublikasikan: atur template ke objek yang menamainya berdasarkan id (emt_...) atau berdasarkan slug, tepat salah satu dari keduanya, dengan nilai variabelnya di template.parameters. Hilangkan subject, html, dan text, karena template sudah memilikinya.

const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  category: "transactional",
  template: {
    slug: "welcome-email",
    parameters: { first_name: "Jane" },
  },
});
console.log(msg.id, msg.status);

Konten template adalah Liquid, jadi selain substitusi {{ variable }} biasa, template dapat menggunakan filter, kondisional {% if %}, dan loop {% for %}. Personalisasi dengan variabel mencantumkan beberapa konstruksi yang ditolak saat publikasi. template.parameters adalah tempat Anda menaruh nilai untuk parameter template itu sendiri, dengan nama parameter sebagai key. Jika salah satunya tidak disertakan, pengiriman ditolak dengan 422 yang menyebutkan namanya. Pengiriman ini mendukung tags, metadata, pelacakan, dan lampiran yang sama seperti email inline. Yang khusus untuk pengiriman template:

  • Inline atau templated, tidak keduanya. Mengirim template bersamaan dengan subject, html, atau text ditolak dengan 422. API juga menolak nilai variabel di field parameters tingkat atas; pada pengiriman template, nilai tersebut ditempatkan di template.parameters.
  • bird adalah satu-satunya nama cadangan. Path placeholder yang dimulai dengan bird. merujuk data kami sendiri, seperti link berhenti berlangganan atau catatan kontak penerima, sehingga key template.parameters tidak boleh bernama bird. Semua key lain bebas Anda tentukan, dan masing-masing berupa satu kata: {"order_number": "A-1043"} mengisi {{ order_number }}.
  • Template dapat dikirim sekarang atau nanti. Tambahkan scheduled_at untuk menjadwalkan pengiriman. Kami menyimpan versi yang dipublikasikan, bahasa yang dipilih, parameter pemanggil, dan nilai kontak saat pengiriman diterima, sehingga perubahan kontak atau fallback setelahnya tidak mengubah pesan yang sudah diterima. Jika Anda menghapus template sebelum waktu pengiriman, pesan ditolak dengan generation_failure.
  • Personalisasi dengan atribut kontak. Lihat persyaratan kontak dan sapaan default.
  • Pencocokan kontak mengabaikan display name. Alamat email diubah ke huruf kecil, dengan titik dan tag plus tetap dipertahankan. Jika beberapa kontak memiliki alamat yang sama, pengiriman mengembalikan EmailTemplateContactAmbiguous; perbarui catatan duplikat sebelum mencoba lagi.
  • Pengiriman menggunakan izin email. Kunci dengan emails:write dapat mengirim template yang dipublikasikan tanpa izin penemuan kontak. emails:read memberikan akses ke konten nonsensitif dari pesan yang dihasilkan. Penemuan kontak dan properti tetap menggunakan izin yang sudah ada.
  • Pengiriman menggunakan versi template yang dipublikasikan. Draf tidak pernah dikirim. Template yang tidak dikenal ditolak dengan 404, dan template tanpa versi yang dipublikasikan ditolak dengan 422.
  • language memilih salah satu bahasa template. Hilangkan untuk mengirim bahasa default template. Minta bahasa yang tidak dimiliki template, dan pengaturan on_missing_language pada template itu sendiri yang menentukan apakah kecocokan terdekat yang dikirim atau pengiriman ditolak. Template yang mengatur language_source_required menolak pengiriman yang tidak menyebutkan bahasa sama sekali.
  • Kategori template adalah default, dan milik Anda menimpanya. Hilangkan category dan pengiriman mewarisi kategori template, sehingga template transaksional tidak perlu mengulangnya di setiap panggilan.

Template email membahas pembuatan, publikasi, dan konstruksi yang dapat dimuat template.

Membaca konten untuk penerima

Riwayat pesan template menggunakan nilai kontak yang disimpan saat pengiriman diterima, meskipun Anda kemudian mengubah atau menghapus kontak tersebut. Setiap pengiriman salinan memiliki ID penerimanya sendiri.

Tampilkan daftar penerima pesan untuk mendapatkan ID mereka, lalu teruskan recipient_id ke Get stored message content. Pesan template baru dengan beberapa snapshot penerima mengembalikan 422 tanpa selector tersebut. Penerima tunggal dipilih secara otomatis; penerima milik pesan lain mengembalikan 404.

Respons GET dan daftar pesan menggunakan subject yang diterima untuk penerima To pertama, yang diidentifikasi oleh subject_recipient_id. Daftar penerima menyediakan subject masing-masing penerima. Subject ini tetap tersedia saat penyimpanan body dinonaktifkan. Pembacaan konten tetap mengikuti pengaturan penyimpanan dan retensi pesan, termasuk 404 jika tidak disimpan, 425 selama masih tertunda, dan 410 setelah kedaluwarsa.

Jika snapshot penerima yang disimpan menjadi tidak tersedia, status pesan dan subject representatif tetap dapat dibaca, tetapi subject penerima dihilangkan dan konten tidak tersedia. Pembacaan tidak mencari nilai kontak saat ini untuk merekonstruksi pengiriman sebelumnya.

Tag vs metadata

Field ini melampirkan data Anda sendiri ke pengiriman, dan perbedaannya terletak pada cara Anda mengkuerinya nanti:

  • tags adalah pasangan {name, value} terstruktur: maksimal 20 per pengiriman, nama maksimal 32 karakter, nilai maksimal 64, hanya huruf ASCII, angka, underscore, dan tanda hubung, dan nama unik dalam satu pengiriman. Tag adalah dimensi filter, sehingga Anda dapat memfilter daftar pesan berdasarkan tag dan memecah analitik serta rollup dasbor berdasarkan tag. Gunakan untuk label berkardinalitas rendah seperti campaign, experiment_variant, atau source.
  • metadata adalah objek JSON arbitrer, maksimal 2 KB setelah diserialisasi. Kami menyimpannya, mengembalikannya pada pembacaan API, dan mengirimkannya di setiap event webhook, sehingga cocok untuk konteks yang ingin Anda terima kembali: ID internal, foreign key, payload terstruktur.
  • reference adalah satu string 1 hingga 255 karakter yang Anda gunakan untuk mencari pesan, seperti nomor pesanan atau tiket. GET /v1/email/messages?reference= mencocokkannya secara persis dan peka huruf besar-kecil, dan beberapa pesan dapat memakai nilai yang sama. Nilai ini dikembalikan pada pembacaan dan dikirimkan di event webhook pesan. Atur hanya jika Anda perlu mencarinya, dan taruh data lain di metadata.

Setiap event webhook menyertakan tag dan metadata bersama ID korelasi (email_id, recipient_id), sehingga Anda dapat mencocokkannya dengan catatan Anda sendiri tanpa pencarian tambahan. Nama tag dan key metadata tingkat atas yang dimulai dengan __bird ditolak. Anda tidak perlu menyandikan perangkat, geografi, penyedia mailbox, tipe bounce, atau domain penerima ke salah satu field, karena kami sudah menangkap masing-masing sebagai dimensi analitik.

Contoh kode
{
  "tags": [{ "name": "campaign", "value": "onboarding" }],
  "metadata": { "user_id": "usr_12345", "order_id": "ord_98765" },
  "reference": "ord_98765"
}

Lampiran

attachments menerima hingga 20 file per pesan, sebagai byte yang di-encode base64 secara inline. Kami menolak pengiriman yang estimasi ukuran pesan yang dihasilkan melampaui 20 MB, diukur setelah encoding base64, jadi jaga konten lampiran mentah pada atau di bawah 15 MB untuk ruang cadangan. Lampiran memuat kontrak field, gambar inline, tipe file yang diblokir, dan cara mengunduh kembali lampiran.

Arti respons 202

Pengiriman yang berhasil mengembalikan 202 Accepted dengan message ID berawalan em_ dan status: accepted:

Contoh kode
{
  "id": "em_01ky7ma8y2es1s2akzk53tmjn0",
  "status": "accepted",
  "category": "marketing",
  "from": { "email": "hello@yourdomain.com" },
  "to": [{ "email": "delivered@messagebird.dev" }],
  "subject": "Hello from Bird",
  "accepted_count": 1,
  "processed_count": 0,
  "delivered_count": 0,
  "deferred_count": 0,
  "bounced_count": 0,
  "complained_count": 0,
  "rejected_count": 0,
  "open_count": 0,
  "click_count": 0,
  "track_opens": true,
  "track_clicks": true,
  "created_at": "2026-07-23T13:58:20.866Z"
}

202 berarti kami telah menerima pengiriman secara permanen. Kegagalan yang dapat Anda perbaiki dikembalikan langsung pada request sebagai 422: domain pengirim yang belum diverifikasi, atau field yang tidak valid. Hasil per penerima (delivered, bounced, deferred, complained) tiba setelahnya melalui webhook dan endpoint pembacaan pesan.

Dua hal yang mengikuti dari itu:

  • Pembacaan mengembalikan status tanpa body. GET /v1/email/messages/{message_id} mengembalikan status pesan dan penerima, tidak pernah body html atau text. Jika penyimpanan konten diaktifkan untuk workspace, body yang disimpan tetap tersedia hingga 30 hari dari GET /v1/email/messages/{message_id}/content.
  • Pembacaan bisa sedikit tertinggal dari pengiriman. 404 pada endpoint pembacaan tepat setelah 202 berarti pesan belum terlihat, jadi coba lagi sesaat kemudian.

Mencoba ulang dengan aman

Kirim header Idempotency-Key dengan nilai unik per pengiriman logis. Jika request berhasil tetapi Anda tidak menerima responsnya, ulangi dengan kunci yang sama. API mengembalikan hasil asli alih-alih mengirim email kedua dan menyertakan header Idempotency-Replay. Idempotensi memuat format kunci dan masa retensinya.

Pengiriman batch

Untuk mengurangi request API, POST /v1/email/batches menerima hingga 100 pesan independen dan memvalidasinya sebagai satu unit. Memanggil endpoint pengiriman tunggal dalam loop juga didukung. Setiap item batch menggunakan payload di halaman ini, termasuk scheduled_at, sehingga satu batch dapat mencampur pesan langsung dan terjadwal.

Biaya dan penagihan

Pengiriman email dihitung per penerima, jadi satu pesan ke tiga penerima dihitung sebagai tiga pengiriman. Pada paket Email Free, pengiriman dihitung terhadap kuota bulanan dan hariannya. Pada paket Email berbayar, setiap pengiriman ditagih dari wallet Anda sesuai tarif paket per 1.000 email. Penagihan dan penggunaan membahas model pengukuran dan pembacaan penggunaan secara langsung, dan halaman harga email mencantumkan tarif setiap paket.

Langkah selanjutnya