Menghubungkan dengan Meta Graph API
Ada dua cara mengirim pesan WhatsApp lewat WizMessage. API WizMessage adalah pilihan yang tepat jika Anda memulai dari nol — API ini memberi Anda variabel template bernama, antarmuka pemetaan, dan kolom reference yang bisa Anda pakai sebagai kunci untuk catatan Anda sendiri.
Halaman ini mendokumentasikan cara yang lain. API ini meniru WhatsApp Cloud API milik Meta dengan cukup rapat sehingga kode yang sudah ditulis untuk Meta berfungsi di sini setelah mengubah dua hal: base URL dan token. Path yang sama, isi permintaan yang sama, isi respons yang sama, format error yang sama. Jika Anda sudah punya integrasi, library, atau developer yang sudah mengenal Cloud API, ini jalan yang lebih singkat.
Ini bukan hanya permukaan untuk mengirim. Di samping pengiriman pesan, API ini membawa endpoint manajemen akun — membuat dan menghapus template, membaca dan menulis ulang profil bisnis, kode QR, pengaturan commerce, daftar blokir, pengaturan nomor telepon, dan flow — semuanya di path milik Meta sendiri. Mengelola akun adalah katalognya, dan Apa yang tidak tersedia di sini adalah separuh lainnya yang sama pentingnya.
Kedua permukaan memakai API key yang sama, izin yang sama, dan batas laju yang sama. Tidak ada yang usang — pilih per integrasi, dan Anda bisa memakai keduanya sekaligus.
Mana yang Anda butuhkan
Dasar-dasarnya
Base URL, dan versi di dalam path
https://wizmessage.com/v26.0/{phone-number-id}/messages
Segmen versi itu nyata dan wajib, tetapi nilainya tidak mengubah apa pun. Kami mencocokkannya sebagai v{major}.{minor} lalu mengabaikannya — platform yang menentukan versi Graph mana yang dipakai ke hulu.
Dua konsekuensi yang perlu diketahui:
- Memakai versi lama tetap berfungsi. Saat Meta menghentikan versi yang tertanam di kode Anda, panggilan di sini terus berjalan. Tidak ada yang perlu dijadwalkan.
v26saja ditolak. Angka minor tidak opsional.v26.0boleh,v26adalah 404.
{phone-number-id} harus berupa nomor WhatsApp di balik API key yang Anda pakai. Selain itu ditolak — lihat Ketika sebuah id bukan milik Anda.
Autentikasi
Kirim API key Anda sebagai bearer token, persis seperti Anda mengirim access token ke Meta.
| Header | Nilai |
|---|---|
Authorization | Bearer pk_live_… — API key lengkap Anda, bukan prefiks yang ditampilkan di dasbor. |
X-API-Key juga diterima di sini, jadi key yang sudah berfungsi pada API WizMessage tidak memerlukan perubahan header. Bearer adalah bentuk yang lebih baik dipakai: itulah yang dikirim SDK Meta yang tidak dimodifikasi, dan itulah tujuan permukaan ini.
Catatan: "Require signature" harus mati pada key. Ini bukan preferensi di sini. Key yang mengaktifkannya tidak bisa menyimpan webhook Meta Graph — dasbor menolak dengan "Signed-request enforcement is not compatible with the Graph format — disable it on this key first" — dan setiap panggilan tanpa tanda tangan dari key itu pada permukaan ini ditolak sebagai
code 190dengan401. Klien Cloud API siap pakai tidak bisa menghasilkan tanda tangan permintaan kami, jadi keduanya saling eksklusif secara desain.
Langganan Anda
Modul API memerlukan langganan Wiz Bot atau Wiz Pro yang aktif. Ini berlaku untuk kedua permukaan — API WizMessage dan yang ini — dan diperiksa pada setiap panggilan, bukan hanya saat key dibuat.
Jika langganan berakhir, API tidak berhenti seketika. Panggilan pertama setelah berakhir memulai jendela tujuh hari dan diizinkan lewat, dan setiap respons selama jendela itu membawa header:
| Header | Nilai |
|---|---|
X-Subscription-Warning | Subscription inactive. API access ends in 5 day(s). |
Setelah jendela berakhir, panggilan ditolak dengan Graph code 10 pada 403:
{
"error": {
"message": "(#10) Access denied. The API module requires an active Wiz Bot or Wiz Pro subscription. Renew at /subscriptions to restore access.",
"type": "OAuthException",
"code": 10,
"fbtrace_id": "AKnBaXsYl7FlmMVw3ql5Ywo"
}
}
Tiga detail yang menentukan apakah Anda menyadarinya tepat waktu:
- Jam dimulai pada panggilan pertama Anda, bukan pada tanggal penagihan. Key yang menganggur selama dua minggu setelah berakhir tetap mendapat tujuh hari penuh, dimulai kapan pun ia memanggil berikutnya.
- Jendela itu per API key, bukan per akun. Setiap key menjalankan jamnya sendiri dari panggilan pertamanya sendiri, jadi key yang Anda pakai untuk uji coba dan key yang menjalankan produksi akan berakhir pada hari yang berbeda. Periksa header pada key yang penting.
- Penolakan datang tanpa peringatan pada permintaan yang memicunya.
X-Subscription-Warningadalah satu-satunya pemberitahuan di muka — catat, atau hal pertama yang Anda ketahui adalah403.
Catatan: Perpanjangan memerlukan beberapa menit untuk berlaku, bukan detik. Jawaban hak akses di-cache selama lima menit dan status langganan di baliknya lima menit lagi, jadi perpanjangan yang dilakukan saat integrasi sedang gagal tidak akan langsung beres pada percobaan ulang berikutnya. Tunggu beberapa menit sebelum menyimpulkan bahwa itu tidak berhasil.
Mengirim pesan
POST /{version}/{phone-number-id}/messages, dengan isi milik Meta. Kami tidak memvalidasinya selain memeriksa bahwa itu pesan WhatsApp — Meta adalah otoritas atas pesan yang valid, dan error-nya lebih baik daripada yang bisa kami buat.
POST https://wizmessage.com/v26.0/106540352242922/messages
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "919876543210",
"type": "text",
"text": { "preview_url": false, "body": "Your order has shipped." }
}
Catatan:
tomenerima angka saja di sini —919876543210, tanpa+. Ini format Meta, dan ini hal yang paling sering salah saat berpindah antar dua permukaan: API WizMessage mewajibkan+E.164dan menolak yang lain, sedangkan yang ini meneruskan nilai Anda langsung ke Meta. Satu-satunya pengecualian adalahPOST /messages/contacts, yang menerima kedua bentuk justru agar isi Graph bisa dipindahkan tanpa diubah.
Pengiriman template juga berbentuk milik Meta — komponen posisional, bukan variabel bernama. Tidak ada lapisan pemetaan di permukaan ini, itulah pertukaran yang Anda ambil demi kompatibilitas.
{
"messaging_product": "whatsapp",
"to": "919876543210",
"type": "template",
"template": {
"name": "payment_failed_alert",
"language": { "code": "en" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Priya Sharma" },
{ "type": "text", "text": "12 Aug 2026" },
{ "type": "text", "text": "Wiz Pro" }
]
}
]
}
}
Mengirim kartu kontak
type: "contacts" adalah vCard — sebuah nama, dan apa pun yang Anda lampirkan. Ini selalu berfungsi di permukaan ini dan masih berfungsi, dengan isi Meta yang tidak diubah.
POST https://wizmessage.com/v26.0/106540352242922/messages
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "919876543210",
"type": "contacts",
"contacts": [
{
"name": {
"formatted_name": "Priya Sharma",
"first_name": "Priya",
"last_name": "Sharma"
},
"phones": [
{ "phone": "+911122334455", "type": "WORK", "wa_id": "911122334455" }
],
"emails": [
{ "email": "[email protected]", "type": "WORK" }
],
"org": { "company": "Acme Retail", "department": "Support", "title": "Account Manager" }
}
]
}
name.formatted_name adalah satu-satunya kolom yang diwajibkan WhatsApp. phones[].wa_id adalah yang layak ditambahkan: dengannya kartu membawa tombol Message yang membuka obrolan dengan orang itu, tanpanya kartu hanya bisa disimpan.
Isi yang persis sama juga bisa dikirim ke POST /messages/contacts pada API WizMessage — kunci amplop, to angka saja, dan semuanya. Itu satu-satunya endpoint di permukaan itu yang menerima bentuk ini, dan ia ada karena dua hal yang tidak bisa diberikan path ini layak dimiliki: pengiriman di sini tidak dicatat di bawah message_id, jadi tidak ada status yang bisa dicek dan tidak ada webhook message.sent untuk dicocokkan, dan isinya hampir tidak diperiksa sebelum sampai ke Meta. Mengubah URL adalah seluruh migrasinya.
Catatan: Mengirim beberapa kartu dalam satu pesan adalah array
contactsmilik Meta, dan Meta menerima hingga 257. Endpoint WizMessage membatasinya sepuluh — jadi pengiriman kartu massal yang berfungsi di sini adalah salah satu dari sedikit isi yang tidak bisa dipindahkan tanpa diubah.
Menandai pesan sebagai sudah dibaca
Tanda terima baca dan indikator mengetik dikirim ke endpoint yang sama, dengan isi yang tidak memuat to maupun type. markAsRead() pada setiap SDK Cloud API bergantung pada hal ini berfungsi, jadi ia didukung persis seperti yang ditetapkan Meta.
POST https://wizmessage.com/v26.0/106540352242922/messages
{
"messaging_product": "whatsapp",
"status": "read",
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgUQ0Uz…"
}
Balasannya adalah {"success": true} — bukan amplop pengiriman di bawah. Bercabanglah berdasarkan panggilan yang Anda buat, jangan mencoba membaca messages[0].id dari setiap respons.
Apa yang dikembalikan
Respons Meta, tanpa diubah:
{
"messaging_product": "whatsapp",
"contacts": [{ "input": "919876543210", "wa_id": "919876543210" }],
"messages": [{ "id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgUQ0Uz…" }]
}
wamid itu adalah satu-satunya pegangan Anda atas pesan. Permukaan ini sengaja tidak punya kolom reference dan tidak punya endpoint pengecekan status — keduanya tambahan WizMessage yang tidak ada di API Meta, jadi menyertakannya akan merusak kompatibilitas yang menjadi alasan permukaan ini ada.
Untuk mengikuti nasib sebuah pesan, cocokkan wamid dengan status pengiriman yang tiba di webhook Anda. Jika Anda lebih suka punya kolom sendiri sebagai kunci, itulah gunanya reference pada API WizMessage.
Error
Error datang dalam amplop Meta, tidak pernah amplop kami. Asal error menentukan seberapa banyak darinya yang berasal dari kami.
Error yang kami munculkan
code | HTTP | Kapan |
|---|---|---|
190 | 401 | Key hilang, tidak dikenal, dicabut, ditangguhkan, atau kedaluwarsa — atau panggilan tanpa tanda tangan ke key yang mewajibkan tanda tangan. Juga muncul saat akun WhatsApp itu sendiri kehilangan koneksinya ke Meta; lihat catatan di bawah. |
10 | 403 | Key tidak memiliki izin yang diperlukan panggilan ini (lihat Izin akses), atau langganan akun berakhir melewati masa tenggangnya. |
4 | 429 | Jatah per menit atau harian key sudah habis. |
100 | 400 | Permintaan cacat, id di path yang bukan milik Anda, atau endpoint yang tidak dilayani permukaan ini. |
2 | 500 | Sesuatu gagal di pihak kami. Coba lagi. |
Catatan: Dua hal berbeda mengembalikan
190, dan keduanya perlu perbaikan yang berlawanan. Jika pesannya menyebut key, putar atau masukkan ulang key. Jika bunyinya "the connection to this WhatsApp Business Account needs to be renewed", key-nya baik-baik saja dan akun kehilangan tautannya ke Meta — hubungkan kembali dari dasbor. Memutar key yang berfungsi sebagai respons terhadap yang kedua menyia-nyiakan satu sore dan tidak mengubah apa pun.
Error yang dimunculkan Meta
Diteruskan persis seperti yang diterima, dengan status HTTP Meta. Semua yang dikirim Meta bertahan — error_subcode, error_data.details, dan fbtrace_id milik Meta sendiri:
{
"error": {
"message": "Unsupported post request. Object with ID '106540352242922' does not exist, cannot be loaded due to missing permissions, or does not support this operation.",
"code": 100,
"type": "GraphMethodException",
"error_subcode": 33,
"fbtrace_id": "AKnBaXsYl7FlmMVw3ql5Ywo"
}
}
Catatan: Perhatikan
typepada contoh itu —GraphMethodException, bukanOAuthException. Meta memakai beberapa jenis exception dan kami tidak menormalkannya. Bercabanglah padaerror.code, yang berupa angka dan stabil; perlakukantypesebagai label untuk manusia.
Ketika sebuah id bukan milik Anda
Setiap id di path — id nomor telepon, id WABA, id media — diperiksa terhadap akun di balik key Anda sebelum hal lain terjadi. Ketidakcocokan mengembalikan code 100 dengan kata-kata yang dipakai Meta untuk objek yang tidak bisa Anda lihat:
Unsupported post request. Object with ID '…' does not exist, cannot be loaded due to missing permissions, or does not support this operation.
Kata-kata itu disengaja di pihak Meta dan disalin dengan sengaja di sini: id yang ada tetapi milik orang lain dan id yang sama sekali tidak ada harus tidak bisa dibedakan, atau error itu menjadi cara menemukan nomor telepon orang lain.
Media
Empat endpoint, sesuai dengan milik Meta.
| Method | Path | Fungsinya |
|---|---|---|
POST | /{version}/{phone-number-id}/media | Mengunggah file. Multipart, mengembalikan {"id": "…"}. |
GET | /{version}/{media-id} | Objek metadata milik Meta untuk file itu. |
GET | /{version}/{media-id}/download | Byte-nya. |
DELETE | /{version}/{media-id} | Menghapusnya, mengembalikan {"success": true}. |
Satu perbedaan yang disengaja
GET /{version}/{media-id} mengembalikan objek metadata milik Meta — messaging_product, mime_type, sha256, file_size, id — tetapi url menunjuk ke kami, bukan ke host lookaside milik Meta.
Itu harus begitu. URL media Meta hanya bisa diambil dengan token bisnis akun WhatsApp, yang ada di pihak kami dan tidak pernah keluar. Jadi kami memberi Anda URL yang bisa Anda ambil: endpoint unduhan di atas, pada host dan segmen versi yang sama dengan yang Anda panggil, diautentikasi dengan bearer token yang sama dengan yang sudah Anda kirim.
Klien memperlakukan URL itu sebagai buram dan cukup melakukan GET dengan token mereka, itulah sebabnya SDK yang tidak dimodifikasi tidak menyadarinya. Jika kode Anda mengkhususkan nama host, itulah satu-satunya tempat yang perlu diubah.
Izin akses
Chip izin yang sama seperti API WizMessage, pada key yang sama. Dari mana izin itu dibaca bergantung pada panggilannya: endpoint pengiriman menurunkannya dari isi permintaan, karena Meta menaruh setiap jenis pesan pada satu path; di tempat lain izin berasal dari path dan method.
Pengiriman dan media:
| Panggilan | Izin |
|---|---|
POST /{v}/{pnid}/messages dengan "type": "template" | send_template |
sama, type berupa image, video, audio, document, atau sticker | send_media |
| sama, selain itu — text, interactive, reaction, location, contacts, dan mark-as-read | send_text |
POST /{v}/{pnid}/media — unggah | send_media |
DELETE /{v}/{media-id} | send_media |
GET /{v}/{media-id} dan /download | view_messages |
Manajemen akun — lihat Mengelola akun:
| Panggilan | Izin |
|---|---|
GET /{v}/{waba-id}/message_templates | view_templates |
POST dan DELETE /{v}/{waba-id}/message_templates | manage_templates |
GET /{v}/{waba-id}/template_analytics | view_analytics |
GET pada profil bisnis, kode QR, pengaturan commerce, daftar blokir, pengaturan telepon, dan flow | view_templates |
POST pada keenam hal yang sama | manage_profile |
DELETE /{v}/{pnid}/message_qrdls/{code} dan DELETE /{v}/{pnid}/block_users | manage_profile |
Itu hanya dua DELETE pada sisi manajemen. Tidak ada delete pada profil bisnis, pengaturan commerce, pengaturan telepon, atau flow — DELETE ke salah satunya ditolak sebagai code 100, sama seperti endpoint yang tidak ada.
Catatan: Unggah media memerlukan
send_mediadi sini. Pada API WizMessage, unggahan tidak memerlukan izin sama sekali, dengan alasan bahwasend_mediamengatur pengiriman file ke pelanggan, bukan menaruh file di server Meta. Permukaan ini mengambil garis yang lebih ketat. Jika Anda memindahkan key dan unggahan mulai mengembalikan403, inilah alasannya.
Catatan:
view_templatesmengatur jauh lebih banyak daripada template. Membaca profil bisnis, kode QR, pengaturan commerce, daftar blokir, pengaturan telepon, dan flow semuanya ada di balik izin ini. Namanya bersifat historis — ia lebih dulu ada daripada semua yang ada di tabel kedua. Hapus centangnya saat mempersempit key dan enam bacaan yang tidak berkaitan mulai mengembalikan403dengan pesan tentang template, yang bukan petunjuk yang membantu.
Catatan: Tiga chip hanya untuk Meta Graph.
manage_templates,manage_profile, danview_analyticstidak mengatur apa pun pada API WizMessage — mereka ada untuk endpoint di halaman ini. Setiap key yang sudah ada diberi ketiganya, jadi tidak ada yang tadinya berfungsi berhenti; mempersempit adalah penghapusan centang yang disengaja pada formulir edit key.
Penolakan adalah Graph code 10 pada 403 — tidak pernah PERMISSION_DENIED milik kami. Satu handler error mencakup seluruh permukaan ini.
Kepemilikan diperiksa sebelum izin. Jika id di path bukan yang ada di balik key Anda, jawabannya code 100 dan tidak pernah mengungkapkan izin mana yang Anda kekurangan — jika tidak, error itu sendiri akan menjadi cara menyelidiki akun orang lain.
Mengelola akun
Selain mengirim, permukaan ini membawa endpoint manajemen akun milik Meta pada path milik Meta sendiri. Delapan sumber daya, dan izin yang Anda perlukan bergantung pada apakah Anda membaca atau menulis.
Tiga hal berlaku untuk setiap endpoint di bagian ini, dan itulah seluruh alasan bagian ini bisa ada:
- Isi permintaan adalah milik Meta sendiri, diteruskan tanpa diubah dan tanpa divalidasi. Kami tidak mendokumentasikan ulang isi milik Meta di sini dan tidak memeriksanya. Referensi Meta adalah spesifikasinya, dan error Meta-lah yang kembali jika Anda salah — yang merupakan error yang lebih baik daripada yang bisa kami buat.
- Isi hanya dikirim pada
POST. Isi yang dilampirkan padaDELETEdibuang sebelum permintaan meninggalkan kami. Delete template milik Meta memakai?name=atau?hsm_id=sebagai parameter query, bukan isi, sehingga berfungsi seperti didokumentasikan; hal lain yang mengharapkan isi delete tidak akan berfungsi. - Query string Anda diteruskan, kecuali
access_tokendanappsecret_proof. Keduanya dibuang, karena Meta mengutamakan token di query string daripada headerAuthorizationdan membiarkannya lewat akan membuat bearer Anda opsional. Klien yang mengautentikasi lewat query string akan mendapati token itu diabaikan begitu saja — pakai header.
Catatan: Id WABA dan id nomor telepon pada path ini harus yang ada di balik key Anda. Itu diperiksa sebelum apa pun meninggalkan jaringan kami, dan ketidakcocokan adalah
code 100yang sama seperti dijelaskan di Ketika sebuah id bukan milik Anda.
Catatan:
paging.nextmenunjuk ke Meta, bukan ke kami. Respons dikembalikan persis seperti yang dikirim Meta, jadi tautannextdalam respons berhalaman adalah URLgraph.facebook.com— dan API key Anda tidak mengautentikasi di sana. Halaman dengan mengambilpaging.cursors.afterdan mengirim ulang permintaan ke host ini dengan?after=.
Template
GET https://wizmessage.com/v26.0/{waba-id}/message_templates?limit=25
POST https://wizmessage.com/v26.0/{waba-id}/message_templates
DELETE https://wizmessage.com/v26.0/{waba-id}/message_templates?name=order_shipped
| Method | Izin | Mengembalikan |
|---|---|---|
GET | view_templates | { data, paging } milik Meta |
POST | manage_templates | Respons pembuatan milik Meta |
DELETE | manage_templates | Respons penghapusan milik Meta |
GET menerima limit (1–100, bawaan 25) dan after untuk paginasi kursor.
Catatan:
GEThanya menerimalimitdanafter. Filter Meta lainnya pada edge ini —name,status,fields,language,category,name_or_content— dibuang tanpa pemberitahuan, bukan ditolak. Permintaan?status=APPROVEDmengembalikan200yang memuat setiap template, disetujui atau tidak. Saring di sisi Anda, atau pakaiafteruntuk menelusuri seluruh daftar. Ini satu-satunya tempat di halaman ini di mana klien yang dipindahkan mendapat jawaban yang salah, bukan error, jadi layak memeriksa kode Anda untuk hal ini.
Analytics template
GET https://wizmessage.com/v26.0/{waba-id}/template_analytics
Memerlukan view_analytics. Meta mewajibkan start, dan menjawab code 100 — "(#100) The parameter start is required" — tanpanya. granularity dan parameter lainnya milik Meta; teruskan dan semuanya tiba tanpa diubah.
Profil bisnis
GET https://wizmessage.com/v26.0/{phone-number-id}/whatsapp_business_profile?fields=about,address,description,email,profile_picture_url,websites,vertical
POST https://wizmessage.com/v26.0/{phone-number-id}/whatsapp_business_profile
GET memerlukan view_templates dan mengembalikan { data } milik Meta. POST memerlukan manage_profile.
Kode QR
GET https://wizmessage.com/v26.0/{phone-number-id}/message_qrdls
POST https://wizmessage.com/v26.0/{phone-number-id}/message_qrdls
DELETE https://wizmessage.com/v26.0/{phone-number-id}/message_qrdls/{code}
GET memerlukan view_templates dan mengembalikan { data } milik Meta. POST dan DELETE memerlukan manage_profile.
Path /{code} adalah hanya DELETE. Tidak ada GET atau POST pada satu kode — baca koleksinya dan pilih dari sana.
Pengaturan commerce
GET https://wizmessage.com/v26.0/{phone-number-id}/whatsapp_commerce_settings
POST https://wizmessage.com/v26.0/{phone-number-id}/whatsapp_commerce_settings
GET memerlukan view_templates dan mengembalikan { data } milik Meta. POST memerlukan manage_profile.
Pelanggan yang diblokir
GET https://wizmessage.com/v26.0/{phone-number-id}/block_users
POST https://wizmessage.com/v26.0/{phone-number-id}/block_users
DELETE https://wizmessage.com/v26.0/{phone-number-id}/block_users
GET memerlukan view_templates dan mengembalikan { data } milik Meta. POST dan DELETE memerlukan manage_profile. Perhatikan bahwa membuka blokir adalah DELETE ke koleksinya sendiri, bukan ke path per pelanggan.
Pengaturan nomor telepon
GET https://wizmessage.com/v26.0/{phone-number-id}/settings
POST https://wizmessage.com/v26.0/{phone-number-id}/settings
GET memerlukan view_templates dan mengembalikan { calling, storage_configuration }. POST memerlukan manage_profile.
Catatan: Path-nya adalah
/settings. Beberapa referensi API pihak ketiga menyebutnyawhatsapp_calling_settings— path itu tidak ada di Meta dan mengembalikancode 2500di sini, karena kami meneruskannya dan Meta menolaknya. Lihat Apa yang tidak tersedia di sini.
Flow
GET https://wizmessage.com/v26.0/{waba-id}/flows
POST https://wizmessage.com/v26.0/{waba-id}/flows
GET memerlukan view_templates dan mengembalikan { data, paging } milik Meta. POST memerlukan manage_profile.
Apa yang tidak tersedia di sini
Semua yang di atas telah dikonfirmasi pada akun WhatsApp yang aktif. Begitu pula semua yang di bawah — itulah sebabnya bagian ini bisa memberi tahu Anda mengapa sebuah endpoint tidak ada, dan itu biasanya yang menentukan apa yang Anda lakukan selanjutnya.
Apa yang dikembalikan endpoint yang tidak tersedia. Apa pun yang tidak ada di daftar di atas dijawab dengan kata-kata Meta sendiri untuk path yang tidak akan dilayaninya — code 100 pada 400:
Unsupported get request. Object with ID '…' does not exist, cannot be loaded due to missing permissions, or does not support this operation.
Path yang didukung tetapi dipanggil dengan method yang tidak ditawarkannya mengembalikan code yang sama dengan pesan yang lebih pendek: "Unsupported delete request." Keduanya sengaja tidak bisa dibedakan dari objek yang memang tidak bisa Anda lihat — error yang mengonfirmasi endpoint mana yang ada akan menjadi cara memetakan permukaan ini.
Ditolak dengan sengaja
Ini diblokir pada setiap method, sebelum apa pun diteruskan:
| Path | Alasan |
|---|---|
register, deregister, request_code, verify_code | Nomor coexistence tidak boleh pernah didaftarkan pada Cloud API. Melakukannya persis adalah cara akun berakhir gagal pada setiap pengiriman dengan error 133010, dan itu tidak bisa dibatalkan dari pihak Anda. |
subscribed_apps | Semua method, termasuk GET. DELETE di sini akan membatalkan langganan webhook kami dari akun WhatsApp Anda sendiri dan diam-diam merusak integrasi Anda sendiri; kami juga tidak menawarkan bacaannya, agar tidak ada path tempat satu kekeliruan menyebabkan hal itu. |
marketing_messages | Pengiriman pemasaran melewati alat kampanye di dasbor, yang menegakkan laju kirim per akun. |
/{template-id}/unpause | Meta mengenali template lewat id polos tanpa akun di path, jadi kami tidak bisa membuktikan template itu milik Anda sebelum bertindak atasnya. Lanjutkan dari dasbor. |
messages pada method apa pun selain POST | Path pengiriman membawa pembatas laju dan penanganan penangguhan akun. Hanya POST yang dirutekan. |
Tidak tersedia pada nomor coexistence
Jawaban Meta sendiri, diperiksa pada akun aktif pada 10 Agustus 2026:
| Path | Jawaban Meta |
|---|---|
/{phone-number-id}/conversational_automation | code 100 — "Tried accessing nonexisting field" |
/{phone-number-id}/calls | code 100 — "Tried accessing nonexisting field" |
/{phone-number-id}/groups | code 131000 pada 500 |
Ini pembatasan Meta pada nomor yang berjalan dalam coexistence dengan aplikasi WhatsApp Business, bukan milik kami — kami meneruskan permintaan dan inilah yang kembali. Meta memperluas ketersediaan dari waktu ke waktu, jadi jika salah satunya penting bagi Anda, layak diuji ulang dan tidak mempercayai tabel ini tanpa batas waktu.
Path yang tidak ada di mana pun
Masing-masing muncul di referensi API pihak ketiga dan bukan endpoint Meta. Keempatnya mengembalikan code 2500, "Unknown path components":
| Yang mungkin Anda baca | Yang dipakai sebagai gantinya |
|---|---|
POST /{phone-number-id}/mark_message_as_read | POST /{phone-number-id}/messages dengan isi status: "read" — lihat Menandai pesan sebagai sudah dibaca |
GET /{phone-number-id}/message_status | Status pengiriman tiba di webhook Anda; lihat Memilih apa yang Anda terima |
/{phone-number-id}/whatsapp_calling_settings | /{phone-number-id}/settings |
/{phone-number-id}/conversational_components | Tidak ada — path nyata terdekat adalah conversational_automation, yang tidak tersedia pada nomor coexistence |
Ada di Meta, tidak ada di daftar kami
GET /{waba-id}/phone_numbers berfungsi di Meta dan tidak diblokir maupun diteruskan di sini, jadi menjawab code 100. Ini salah satu endpoint pertama yang dipanggil klien Cloud API, jadi layak disebut: key Anda sudah mengunci tepat satu nomor telepon, dan id-nya adalah yang Anda taruh di setiap path di halaman ini. Anda bisa membacanya dari tab Overview pada key di dasbor.
Pembacaan node polos
GET /{v}/{waba-id}?fields=… dan GET /{v}/{phone-number-id}?fields=… berfungsi di Meta tetapi tidak di sini. Path satu segmen pada permukaan ini adalah id media — itulah arti GET /{v}/{media-id} — jadi id WABA pada posisi itu dicari sebagai media dan gagal.
Kegagalannya membingungkan jika Anda tidak mengharapkannya: ia memeriksa view_messages lebih dulu, jadi key tanpa izin itu mendapat 403 yang menyebut izin yang tidak ada hubungannya dengan apa yang diminta. Pakai bentuk edge sebagai gantinya — /{waba-id}/message_templates, /{phone-number-id}/settings, dan sisanya di Mengelola akun.
Webhook dalam format Meta
Tab webhook di dasbor terbuka pada format payload. WizMessage (v1) adalah payload yang didokumentasikan di halaman lain; itulah yang dipakai key sampai Anda mengubahnya.

Pilih Meta Graph dan endpoint Anda berhenti menerima payload {version, event, timestamp, data} kami dan mulai menerima amplop yang dikirim Meta kepada kami, diteruskan apa adanya — satu pengiriman per perubahan.
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "102290129340398",
"time": 1770000123,
"changes": [
{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "15550783881",
"phone_number_id": "106540352242922"
},
"contacts": [{ "profile": { "name": "Priya Sharma" }, "wa_id": "919876543210" }],
"messages": [
{
"from": "919876543210",
"id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAEhgg…",
"timestamp": "1770000123",
"type": "text",
"text": { "body": "Has it shipped yet?" }
}
]
},
"field": "messages"
}
]
}
]
}
Jika Anda sudah punya handler webhook Cloud API, itulah bentuk yang untuknya ia ditulis.
Berganti format juga mengubah apa yang diminta tab: kotak centang event diganti dengan nama field milik Meta sendiri, dan sebuah verify token muncul.

Sebelum Anda bisa menyimpannya
Tiga hal diperlukan, dan dasbor akan memberi tahu mana yang hilang:
- Webhook secret. Ini satu-satunya yang menjadi kunci
X-Hub-Signature-256, jadi tanpanya pengiriman tidak bertanda tangan dan tidak bisa diverifikasi. Buat satu pada tab Webhooks milik key. - Verify token. String apa pun yang Anda pilih; endpoint Anda juga harus mengetahuinya.
- URL yang sudah menjawab. Lihat di bawah.
Jabat tangan verifikasi
Saat Anda menyimpan, kami memanggil URL Anda persis seperti yang dilakukan Meta:
GET https://your-server.example/webhooks
?hub.mode=subscribe
&hub.verify_token=<the token you set>
&hub.challenge=482910337
Endpoint Anda harus memeriksa bahwa hub.verify_token cocok dengan yang Anda konfigurasikan, lalu membalas 200 dengan hub.challenge sebagai seluruh isi respons — tanpa pembungkus JSON, tanpa tanda kutip, tanpa apa pun lagi. Challenge-nya adalah angka 9 digit, jadi handler yang melakukan parseInt sebelum menggemakannya mengembalikannya tanpa berubah.
Jika challenge tidak kembali persis sama, penyimpanan ditolak dan tidak ada yang tersimpan. Inilah yang mencegah seseorang mengarahkan antrean pengiriman kami ke URL yang tidak mereka kendalikan.
// express
app.get('/webhooks', (req, res) => {
if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === VERIFY_TOKEN) {
return res.status(200).send(req.query['hub.challenge'])
}
res.sendStatus(403)
})
Memverifikasi tanda tangan
Setiap pengiriman membawa X-Hub-Signature-256, dihitung seperti cara Meta menghitungnya: HMAC-SHA256 dari isi permintaan mentah, berkunci webhook secret Anda, hex, berawalan sha256=.
| Header | Nilai |
|---|---|
X-Hub-Signature-256 | sha256= + HMAC-SHA256 dari isi mentah, berkunci webhook secret Anda, hex huruf kecil. |
X-Webhook-Event | meta.<field> — field mana yang dibawa pengiriman ini. |
X-Webhook-Id | Id unik untuk upaya pengiriman ini. Hapus duplikat berdasarkan id ini. |
User-Agent | facebookplatform/1.0 (+http://developers.facebook.com) |
Catatan: Ini bukan turunan yang sama dengan format WizMessage. Format itu menandatangani
timestamp + "." + rawBodydan mengirim cap waktu di headernya sendiri; yang ini menandatangani isi saja, tanpa cap waktu di mana pun. Jika Anda menjalankan kedua format pada satu server, pisahkan kedua verifier — verifier bersama akan gagal pada format yang tidak ditulis untuknya.
Tandatangani byte mentah seperti yang diterima. Mengurai JSON lalu menserialisasi ulang mengubah urutan kunci dan spasi, dan tanda tangan tidak akan pernah cocok.
const crypto = require('node:crypto')
// express: app.post('/webhooks', express.raw({ type: 'application/json' }), ...)
function verify(req) {
const provided = req.get('X-Hub-Signature-256') || ''
if (!provided.startsWith('sha256=')) return false
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body) // the raw Buffer, not a parsed object
.digest('hex')
// Constant-time compare — a plain === leaks timing information.
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(provided.slice('sha256='.length), 'hex')
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
Memilih apa yang Anda terima
Format WizMessage meminta Anda mencentang event. Yang ini meminta Anda mencentang nama field milik Meta, sesuai dengan yang akan Anda langgani di App Dashboard milik Meta sendiri.
| Field | Yang tiba |
|---|---|
messages | Pesan masuk dan status pengiriman. Baca catatan di bawah. |
message_status | Status pengiriman, sebagai field tersendiri. |
smb_message_echoes | Pesan yang dikirim bisnis dari aplikasi WhatsApp Business di ponselnya. |
smb_app_state_sync | Perubahan buku alamat yang dilakukan di ponsel itu. |
user_id_update | Identitas WhatsApp seorang kontak berubah. |
message_template_status_update | Sebuah template disetujui, ditolak, atau dijeda. |
message_template_quality_update | Skor kualitas sebuah template berubah. |
account_update | Akun WhatsApp dihubungkan, diputus, dibatasi, atau di-offboard. |
history | Percakapan lampau, selama sinkronisasi awal. Payload besar — pilih hanya jika Anda menginginkannya. |
Webhook graph baru dimulai berlangganan pada messages saja. Menghapus semua field mematikan pengiriman tanpa menghapus konfigurasinya.
Catatan:
messagessudah membawa status pengiriman. Meta mengirim keduanya di bawah"field": "messages"— pesan masuk dalam arraymessages, laporan pengiriman dalam arraystatusespada objek value yang sama. Jadi langganan bawaan cukup untuk mengorelasikanwamidyang Anda terima dari sebuah pengiriman.message_statusadalah field tambahan, bukan yang Anda perlukan untuk pelacakan status dasar. Bacavalue.statusessebelum berasumsi Anda kekurangan langganan.
Tiga hal yang berbeda dari format WizMessage
Layak dibaca jika Anda memindahkan integrasi, karena masing-masing diam-diam mengubah apa yang muncul.
- Pengiriman tidak dibatasi oleh sistem chatbot. Pada format WizMessage, pesan masuk hanya sampai ke endpoint Anda ketika sistem chatbot akun diatur ke Webhook. Format Graph mengabaikan pengaturan itu sepenuhnya — mengonfigurasi URL dan berlangganan pada sebuah field adalah seluruh pilihan ikut sertanya.
- Anda menerima
status: "sent"dan status kampanye. Format WizMessage membuang keduanya dengan sengaja. Di sini Anda mendapat apa yang dikirim Meta, yang berarti lalu lintas status yang jauh lebih banyak pada akun yang menjalankan kampanye massal. - Tidak ada
message.sentsintetis. Format WizMessage membuatnya untuk pesan yang dikirim dari API, dasbor, atau ponsel. Meta mencakup hal yang sama dengansmb_message_echoesdan statussent, jadi tidak ada yang diciptakan di sini.
Pengiriman, percobaan ulang, dan mematikan
Identik dengan format WizMessage — tabel yang sama berlaku. Singkatnya: kami menunggu 10 detik, tidak pernah mengikuti redirect, mencoba ulang 5xx/408/429/timeout lima kali dengan backoff, tidak pernah mencoba ulang 4xx lainnya, dan memperlakukan 410 Gone sebagai "matikan webhook ini".
Pengiriman bersifat at-least-once. Hapus duplikat berdasarkan X-Webhook-Id, dan balas 2xx dengan cepat.
Mencobanya tanpa menulis kode
Dialog Test API milik key punya kontrol API Format. Atur ke Meta Graph dan pemilih endpoint diganti dengan target sebenarnya untuk akun Anda — POST /v26.0/{your phone number id}/messages — dengan isi berbentuk Meta yang sudah terisi.

"Simulate Only" menggemakan permintaan kembali tanpa menghubungi Meta; beralih ke Send Real Message mengirimnya dan menunjukkan respons Meta yang sebenarnya, lengkap dengan amplop error-nya.
Dialog itu menargetkan POST /{phone-number-id}/messages dan tidak yang lain, jadi endpoint manajemen akun tidak bisa dijangkau darinya — coba itu dengan curl atau klien Anda sendiri.
Get code membawa kontrol yang sama, jadi cuplikan yang dihasilkan menargetkan permukaan mana pun yang Anda pilih, dalam salah satu dari delapan bahasa. Biarkan "Require signature" mati pada key yang akan Anda pakai di sini — jika aktif, cuplikan Graph yang dihasilkan juga menandatangani permintaan, satu hal yang tidak bisa dilakukan klien Cloud API yang tidak dimodifikasi.