Sign inGet started

Event WhatsApp

Bird mencatat event untuk pesan WhatsApp inbound dan outbound. Linimasa outbound menunjukkan apa yang terjadi setelah pengiriman mengembalikan 202: penerimaan, penyerahan ke WhatsApp, pengiriman, pembacaan, atau kegagalan. Linimasa inbound mencatat kapan Bird menerima pesan tersebut.

Envelope event

Event pengiriman, pesan masuk, dan reaksi WhatsApp menggunakan envelope webhook standar: sebuah type, sebuah timestamp, dan objek data yang spesifik per tipe.
Contoh kode
{
  "data": {
    "direction": "outbound",
    "from": { "phone_number": "+13124495569" },
    "metadata": { "session_id": "sess_4821" },
    "tags": [{ "name": "flow", "value": "login-otp" }],
    "to": { "phone_number": "+14155550100" },
    "whatsapp_id": "wam_01ky7qbvswf3fvyaw3az90391c",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b"
  },
  "timestamp": "2026-07-23T14:51:39.913Z",
  "type": "whatsapp.delivered"
}
Setiap payload webhook WhatsApp publik untuk sebuah pesan membawa whatsapp_id, workspace_id, direction, from, to, tags, dan metadata. whatsapp.reacted adalah pengecualian, karena reaksi merupakan anotasi pada pesan, bukan pesan tersendiri; Reaksi di bawah menjelaskan bentuknya. Sebuah alamat dapat berisi phone_number E.164, Meta business-scoped user ID di bsuid, atau keduanya. Pesan yang diterima dari pengguna WhatsApp juga membawa profil yang mereka publikasikan, di username dan display_name. tags dan metadata bernilai null jika pengiriman tidak menyertakannya. Pesan yang dikirim sebagai balasan juga membawa in_reply_to_message_id pada setiap event outbound dalam linimasanya, dari whatsapp.accepted hingga whatsapp.read, whatsapp.failed, atau whatsapp.rejected, yang menyebutkan pesan yang dijawab.
API events mengembalikan catatan linimasa yang lebih ringkas dengan id, type, dan timestamp occurred_at. ID pesan sudah ada di URL permintaan.

Event siklus hidup

Event muncul dalam urutan kronologis. Pesan keluar dapat berhenti di whatsapp.failed atau whatsapp.rejected, dan event whatsapp.read hanya muncul jika penerima membuka pesan. Timeline pesan masuk dimulai dengan whatsapp.received dan dapat mencatat whatsapp.read setelah workspace Anda menandai pesan sebagai dibaca.
EventArti
whatsapp.acceptedBird menerima permintaan kirim. Ini yang dilaporkan oleh 202.
whatsapp.sentBird menyerahkan pesan ke jaringan WhatsApp.
whatsapp.deliveredWhatsApp mengonfirmasi pengiriman ke perangkat penerima.
whatsapp.readPenerima membuka pesan.
whatsapp.failedPesan tidak terkirim. error.code menjelaskan penyebabnya.
whatsapp.rejectedBird menolak pesan sebelum mengirimnya. Pesan tidak dikenakan biaya.
whatsapp.receivedBird menerima pesan inbound dari kontak.
Callback delivered atau read yang berlaku dapat memicu pengenaan bagian biaya milik Meta. Payload event WhatsApp tidak menyertakan informasi biaya. Baca kembali pesan dengan GET /v1/whatsapp/messages/{message_id} untuk melihat biayanya. Lihat Biaya dan penagihan.
Menandai pesan masuk sebagai dibaca mencatat whatsapp.read di timeline-nya, tetapi tidak memicu webhook konfirmasi baca. Pesan masuk tetap berstatus received dan mencatat read_at setelah WhatsApp menerima konfirmasi tersebut.
whatsapp.read tidak mengubah status pesan. Pesan yang sudah terkirim tetap delivered; pesan tersebut juga mencatat pembacaan di read_at.
whatsapp.delivered dapat dilewati sepenuhnya. Ketika penerima sudah membuka chat di perangkatnya, Meta melaporkan pembacaan tanpa pernah melaporkan pengiriman, sehingga timeline terbaca whatsapp.acceptedwhatsapp.sentwhatsapp.read tanpa whatsapp.delivered di antaranya. Perlakukan read sebagai bukti pengiriman: consumer yang menunggu delivered sebelum menganggap pesan sampai akan menggantung tepat pada penerima yang paling cepat membacanya, dan yang menghitung rasio pengiriman hanya dari delivered akan melaporkan angka terlalu rendah. status pesan tetap sent dalam kasus ini, karena hanya tanda terima pengiriman yang memajukannya.
Callback hanya-baca tetap dapat memicu biaya Meta yang berlaku. Bird menggunakan satu identitas biaya untuk jalur delivered dan read; event pengiriman yang tidak ada bukan berarti komponen Meta gratis. Lihat Cost and billing.
Daftar tipe event bersifat terbuka: tipe baru dapat ditambahkan sewaktu-waktu, jadi perlakukan nilai yang tidak dikenali sebagai event masa depan, bukan error.

Event kegagalan

whatsapp.failed dan whatsapp.rejected bersifat terminal. Sebuah rejection berarti Bird menghentikan pesan sebelum mengirimnya ke WhatsApp, sehingga tidak dikenakan biaya. Penyebabnya antara lain penerima yang disuppresi atau opt-out, saldo wallet tidak mencukupi, atau tujuan tanpa harga yang dikonfigurasi. Sebuah failure berarti pesan tidak terkirim, dan error.code menyebutkan siapa yang memutuskannya. Sebagian besar kode membawa keputusan WhatsApp, yang dipetakan dari kode yang dilaporkannya. internal_error adalah pengecualian: ia mencatat kredensial pengirim yang tidak dapat dipakai atau percobaan pemrosesan yang habis. Upaya transport yang tidak pasti tidak membuktikan bahwa Meta tidak pernah menerima permintaan tersebut. meta_error_code berisi kode WhatsApp jika tersedia, dan kegagalan internal_error tidak memilikinya secara desain.
Kedua event berisi objek error dengan Bird code yang stabil, description yang dapat dibaca manusia, meta_error_code opsional, dan occurred_at. Objek ini muncul di catatan API dan payload webhook hanya untuk tipe event ini.

Event reaksi

Reaksi emoji menganotasi pesan yang sudah ada. Reaksi tidak membuat pesan whatsapp.received baru. Bird memicu whatsapp.reacted ketika kontak menambahkan, mengubah, atau menghapus reaksi, seperti dijelaskan di Reactions. Reaksi yang dikirim oleh nomor bisnis Anda tidak memicu webhook tersebut. Lihat Sending reactions untuk menambah, mengganti, atau menghapus reaksi Anda, dan Receiving reactions untuk contoh webhook dan REST API. Reaksi dari kontak tidak membuka jendela layanan pelanggan.
Log reaksi pesan yang direaksi mencatat perubahan oleh kontak maupun nomor bisnis Anda: penambahan, penggantian, dan penghapusan.
Satu kasus tidak tercatat di mana pun. Bird mencocokkan reaksi ke pesannya melalui provider ID yang disimpan selama 15 hari, sementara WhatsApp menerima reaksi pada pesan hingga 30 hari, sehingga reaksi yang ditempatkan pada pesan yang lebih tua dari itu tidak dapat dicocokkan dan tidak masuk ke log maupun reactions. Pesan tanpa entri bukan berarti tidak ada yang pernah memberikan reaksi.
Baca log tersebut dengan GET /v1/whatsapp/messages/{message_id}/reaction-events, terbaru lebih dulu. Sebuah entri menyebutkan emoji, siapa yang membuat perubahan, dan status berupa received, sent, failed, atau rejected; entri failed atau rejected membawa alasannya di error. Setiap entri memiliki reaction ID (war_…) dan timestamp occurred_at. Penghapusan memiliki emoji: null. Perubahan yang tertunda tidak memiliki entri sampai hasilnya diketahui. Reaksi tidak pernah dikenakan biaya, jadi tidak ada kegagalan reaksi yang merupakan kegagalan penagihan. Untuk melihat apa yang saat ini berlaku pada pesan alih-alih riwayat perubahan, baca reactions-nya dengan GET /v1/whatsapp/messages/{message_id}, yang meringkas log menjadi satu entri per pengirim.

Event supresi

Di luar siklus hidup per pesan, satu event melaporkan perubahan pada daftar supresi workspace: whatsapp_suppression.created dipicu saat supresi dibuka. Payload membawa suppression_id, address yang disuppresi dalam format E.164, waba yang membatasi blokir tersebut (null jika mencakup seluruh workspace, akun mana pun yang mengirim), reason, dan workspace_id, sehingga sistem Anda dapat melihat blokir baru tanpa polling. Hanya pembukaan yang memicu event: pengakhiran supresi belum memicunya, jadi baca ulang daftar sebelum menganggap blokir yang dicerminkan masih berlaku:
Contoh kode
{
  "type": "whatsapp_suppression.created",
  "timestamp": "2026-08-25T14:52:03.192524705Z",
  "data": {
    "suppression_id": "was_01krdgeqcxet5s7t44vh8rt9mg",
    "address": "+14155550100",
    "waba": null,
    "reason": "manual",
    "workspace_id": "ws_01ky7m21hjffh9s8kq76gyb1xf"
  }
}
Opt-out yang dinyatakan sendiri oleh penerima adalah preferensi, bukan supresi, dan memicu preference.revoked sebagai gantinya.

Membaca event dari API

GET /v1/whatsapp/messages/{message_id}/events mengembalikan timeline dalam urutan kronologis. Daftar terbatas ini tidak dipaginasi. Membaca event memerlukan kunci API dengan whatsapp:read:
const { data } = await bird.whatsapp.listEvents("wa_abc123");
for (const event of data) console.log(event.type, event.occurred_at);
Pesan yang diterima, dikirim, terkirim, dan dibaca mengembalikan empat event:
Contoh kode
{
  "data": [
    {
      "id": "ev_01ky7q6a1fejfbvs0myn41hj41",
      "occurred_at": "2026-07-23T14:48:34.71Z",
      "type": "whatsapp.accepted"
    },
    {
      "id": "ev_01ky7q6a2denvtd6jg1vqwmg13",
      "occurred_at": "2026-07-23T14:48:35.671Z",
      "type": "whatsapp.sent"
    },
    {
      "id": "ev_01ky7q6a2zff9r2qm74mmg1g6z",
      "occurred_at": "2026-07-23T14:48:36.642Z",
      "type": "whatsapp.delivered"
    },
    {
      "id": "ev_01ky7q6c21frssf0vj8h50qysw",
      "occurred_at": "2026-07-23T14:48:38.65Z",
      "type": "whatsapp.read"
    }
  ]
}
Gunakan type untuk mengembalikan satu tipe event publik tertentu, seperti ?type=whatsapp.failed atau ?type=whatsapp.read. Hilangkan parameter ini untuk timeline lengkap.
Timeline yang sama adalah yang dirender oleh halaman WhatsApp log saat Anda membuka sebuah pesan.
Lembar detail pesan WhatsApp di dashboard Bird, dibuka untuk pesan bird_order_confirmation yang sudah terkirim: tab Events menampilkan timeline siklus hidup per pesan berupa Accepted, Sent, Delivered, dan Read, masing-masing dengan timestamp-nya, di atas daftar pesan yang diredupkan

Webhook

Berlangganan whatsapp.accepted, whatsapp.sent, whatsapp.delivered, whatsapp.read, whatsapp.failed, whatsapp.rejected, whatsapp.received, dan whatsapp.reacted dari halaman Webhooks atau API webhooks. Webhooks guide membahas endpoint, tanda tangan, dan percobaan ulang.
whatsapp.received membawa konten pesan di atas envelope di atas, sehingga endpoint dapat bertindak atas pesan masuk tanpa membacanya kembali. Ketukan pada pesan interaktif tiba sebagai interactive_reply, dan in_reply_to_message_id menyebutkan pesan yang dijawabnya:
Contoh kode
{
  "data": {
    "direction": "inbound",
    "from": {
      "display_name": "Alex Rivera",
      "phone_number": "+14155550100",
      "username": "alexr"
    },
    "in_reply_to_message_id": "wam_01ky7qbvswf3fvyaw3az90391c",
    "interactive_reply": {
      "list": {
        "description": "Next day to 2 days",
        "slug": "priority_express",
        "text": "Priority Mail Express"
      },
      "type": "list"
    },
    "metadata": null,
    "tags": null,
    "to": { "phone_number": "+13124495569" },
    "whatsapp_id": "wam_01ky8b3xq4gd7pmzn2ka51f7te",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b"
  },
  "timestamp": "2026-07-23T14:52:04.118Z",
  "type": "whatsapp.received"
}
Cabang konten lainnya mengikuti bentuk salah-satu-dari-ini yang sama: text, image, video, audio, sticker, document, location, contact_cards, dan unsupported untuk jenis yang tidak dimodelkan oleh API. GET /v1/whatsapp/messages/{message_id} mendokumentasikan masing-masing.

Reaksi

whatsapp.reacted dipicu saat pengguna WhatsApp memberikan reaksi pada salah satu pesan Anda. Ini adalah satu-satunya event WhatsApp yang bukan bagian dari timeline pengiriman pesan: ia tidak muncul di GET /v1/whatsapp/messages/{message_id}/events, dan tidak ada filter untuk itu di sana.
whatsapp_id menyebutkan pesan yang direaksi, bukan reaksinya, dan emoji adalah perubahan yang dilakukan pengguna. Pengguna yang memberi reaksi, mengubah emoji, lalu mencabut reaksinya menghasilkan tiga event pada satu pesan itu. WhatsApp tidak mengirim penghapusan di antara dua event pertama, sehingga perubahan tiba sebagai satu event yang membawa emoji baru.
Contoh kode
{
  "data": {
    "emoji": "👍",
    "from": { "phone_number": "+14155550100" },
    "to": { "phone_number": "+13124495569" },
    "whatsapp_id": "wam_01ky8b3xq4gd7pmzn2ka51f7te",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b"
  },
  "timestamp": "2026-07-23T14:52:11.000Z",
  "type": "whatsapp.reacted"
}
WhatsApp melaporkan waktu reaksi hingga detik, sehingga dua dari tiga event tersebut dapat memiliki timestamp yang sama. Mengurutkannya berdasarkan nilai itu tidak akan menghasilkan urutan yang benar, begitu juga urutan pengiriman, yang tidak dapat diandalkan karena percobaan ulang. Tindakan berdasarkan reaksi yang dibawa setiap event, sebagai perubahan yang dideskripsikannya. Jangan merekonstruksi urutan dari event atau memperlakukan event terakhir yang tiba sebagai reaksi aktif pada pesan, karena baik timestamp maupun urutan kedatangan tidak mendukungnya. Baca kembali pesan untuk reaksi yang berlaku: GET /v1/whatsapp/messages/{message_id} mengembalikan satu entri per pengirim di reactions, dan log reaksi pesan tersebut memiliki setiap perubahan.
emoji ada dan bernilai null ketika pengguna mencabut reaksinya, jadi null adalah penghapusan itu sendiri, bukan nilai yang hilang. Emoji dikirimkan persis seperti yang dikirim WhatsApp dan tidak dinormalisasi, sehingga dan ❤️ sampai ke Anda sebagai string yang berbeda.

Langkah selanjutnya