Lompat ke konten
28 min read

Menghubungkan dengan Meta Graph API

Arahkan integrasi WhatsApp Cloud API yang sudah ada ke WizMessage tanpa menulis ulang — path milik Meta, isi permintaan milik Meta, format error milik Meta, webhook dalam format Meta sendiri, dan endpoint manajemen akun untuk template, profil bisnis, kode QR, dan flow.

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

API WizMessage dibandingkan dengan API yang kompatibel dengan Meta GraphPilih yang sesuai dengan yang sudah Anda milikiAPI WizMessage/api/v1/externalvariabel template bernamaantarmuka pemetaan di dasborkolom reference, pengecekan statusformat JSON kami sendiriterbaik saat memulai dari nolMeta Graph API/v26.0/{phone-number-id}/…isi permintaan milik Metaformat error milik Metakelola template dan profilwebhook dalam format Metaterbaik saat Anda sudah punya kode Cloud APIKey yang sama, izin yang sama, batas laju yang sama. Pakai keduanya jika Anda mau.
Dua permukaan, satu akun. Tidak ada yang akan dihapus.

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.
  • v26 saja ditolak. Angka minor tidak opsional. v26.0 boleh, v26 adalah 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.

HeaderNilai
AuthorizationBearer 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 190 dengan 401. 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:

HeaderNilai
X-Subscription-WarningSubscription 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"
	}
}
Apa yang terjadi pada akses API setelah langganan berakhirsatu API keynormaltanpa headerjendela tenggang7 hari, panggilan tetap berhasilX-Subscription-Warning di setiap responsditolakcode 10 pada 403panggilan pertama setelah berakhirjam dimulai di sinijendela berakhirtanpa peringatan lagiMemperpanjang kapan saja memulihkan akses dalam beberapa menit.
Berakhirnya langganan sendiri tidak mengubah apa pun. Jam dimulai pada panggilan Anda berikutnya.

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-Warning adalah satu-satunya pemberitahuan di muka — catat, atau hal pertama yang Anda ketahui adalah 403.

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: to menerima 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.164 dan menolak yang lain, sedangkan yang ini meneruskan nilai Anda langsung ke Meta. Satu-satunya pengecualian adalah POST /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 contacts milik 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.

Bagaimana error sampai kepada Anda tergantung apakah Meta yang menghasilkannyaSebuah error di permukaan iniMeta menolaknyaditeruskan apa adanyacode dan subcode Metafbtrace_id milik Meta sendiristatusnya juga milik MetaKami menolaknyaauth, izin, batas lajuditulis dalam kode Metajadi satu handler mencakup keduanya401 · 403 · 429 · 400 · 500Jangan berasumsi type selalu OAuthException — Meta memakai beberapa.
Error milik Meta sendiri diteruskan tanpa diubah. Milik kami meminjam kode Meta.

Error yang kami munculkan

codeHTTPKapan
190401Key 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.
10403Key tidak memiliki izin yang diperlukan panggilan ini (lihat Izin akses), atau langganan akun berakhir melewati masa tenggangnya.
4429Jatah per menit atau harian key sudah habis.
100400Permintaan cacat, id di path yang bukan milik Anda, atau endpoint yang tidak dilayani permukaan ini.
2500Sesuatu 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 type pada contoh itu — GraphMethodException, bukan OAuthException. Meta memakai beberapa jenis exception dan kami tidak menormalkannya. Bercabanglah pada error.code, yang berupa angka dan stabil; perlakukan type sebagai 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.

MethodPathFungsinya
POST/{version}/{phone-number-id}/mediaMengunggah file. Multipart, mengembalikan {"id": "…"}.
GET/{version}/{media-id}Objek metadata milik Meta untuk file itu.
GET/{version}/{media-id}/downloadByte-nya.
DELETE/{version}/{media-id}Menghapusnya, mengembalikan {"success": true}.
Mengunggah file sekali dan mengirimnya berulang kaliPOST /{v}/{pnid}/mediafile itu sendiri{ id }dapat dipakai ulangPOST /{v}/{pnid}/messagesrujuk id-nyaTerkirimstatus tiba lewat webhook
Unggahan mengembalikan id. Id itulah yang Anda taruh di pesan.

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:

PanggilanIzin
POST /{v}/{pnid}/messages dengan "type": "template"send_template
sama, type berupa image, video, audio, document, atau stickersend_media
sama, selain itu — text, interactive, reaction, location, contacts, dan mark-as-readsend_text
POST /{v}/{pnid}/media — unggahsend_media
DELETE /{v}/{media-id}send_media
GET /{v}/{media-id} dan /downloadview_messages

Manajemen akun — lihat Mengelola akun:

PanggilanIzin
GET /{v}/{waba-id}/message_templatesview_templates
POST dan DELETE /{v}/{waba-id}/message_templatesmanage_templates
GET /{v}/{waba-id}/template_analyticsview_analytics
GET pada profil bisnis, kode QR, pengaturan commerce, daftar blokir, pengaturan telepon, dan flowview_templates
POST pada keenam hal yang samamanage_profile
DELETE /{v}/{pnid}/message_qrdls/{code} dan DELETE /{v}/{pnid}/block_usersmanage_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_media di sini. Pada API WizMessage, unggahan tidak memerlukan izin sama sekali, dengan alasan bahwa send_media mengatur pengiriman file ke pelanggan, bukan menaruh file di server Meta. Permukaan ini mengambil garis yang lebih ketat. Jika Anda memindahkan key dan unggahan mulai mengembalikan 403, inilah alasannya.

Catatan: view_templates mengatur 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 mengembalikan 403 dengan pesan tentang template, yang bukan petunjuk yang membantu.

Catatan: Tiga chip hanya untuk Meta Graph. manage_templates, manage_profile, dan view_analytics tidak 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.

Endpoint manajemen akun dikelompokkan menurut izin yang dibutuhkan masing-masingMembaca apa punkedelapan sumber dayaprofil, kode QR, commercedaftar blokir, pengaturan, flowtemplateview_templatesMenulis akunprofil dan kehadiranprofil bisnis, kode QRcommerce, daftar blokirpengaturan, flowmanage_profileMenulis templatemembuat dan menghapusmessage_templatesanalytics terpisahmanage_templatesAnalytics template punya chip sendiri: view_analytics.
Membaca adalah satu chip. Menulis terbagi menurut apa yang Anda tulis.

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 pada DELETE dibuang 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_token dan appsecret_proof. Keduanya dibuang, karena Meta mengutamakan token di query string daripada header Authorization dan 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 100 yang sama seperti dijelaskan di Ketika sebuah id bukan milik Anda.

Catatan: paging.next menunjuk ke Meta, bukan ke kami. Respons dikembalikan persis seperti yang dikirim Meta, jadi tautan next dalam respons berhalaman adalah URL graph.facebook.com — dan API key Anda tidak mengautentikasi di sana. Halaman dengan mengambil paging.cursors.after dan 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
MethodIzinMengembalikan
GETview_templates{ data, paging } milik Meta
POSTmanage_templatesRespons pembuatan milik Meta
DELETEmanage_templatesRespons penghapusan milik Meta

GET menerima limit (1–100, bawaan 25) dan after untuk paginasi kursor.

Catatan: GET hanya menerima limit dan after. Filter Meta lainnya pada edge ini — name, status, fields, language, category, name_or_content — dibuang tanpa pemberitahuan, bukan ditolak. Permintaan ?status=APPROVED mengembalikan 200 yang memuat setiap template, disetujui atau tidak. Saring di sisi Anda, atau pakai after untuk 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 menyebutnya whatsapp_calling_settings — path itu tidak ada di Meta dan mengembalikan code 2500 di 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.

Mengapa sebuah endpoint mungkin tidak tersedia di permukaan iniendpoint yang tidak ada di siniKami menolaknyaregistration, subscribed_appsakan merusak akun Andatidak ada jalan pintasMeta menolaknyatidak ada pada nomor coexistenceconversational_automation, callsjawaban Meta sendiriTidak ada sama sekaliempat path dari referensi APIMeta menjawab code 2500pakai path yang sebenarnyaKelompok keempat hanya belum ada di daftar kami — lihat di bawah.
Empat alasan, dan hanya satu yang berasal dari kami.

Ditolak dengan sengaja

Ini diblokir pada setiap method, sebelum apa pun diteruskan:

PathAlasan
register, deregister, request_code, verify_codeNomor 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_appsSemua 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_messagesPengiriman pemasaran melewati alat kampanye di dasbor, yang menegakkan laju kirim per akun.
/{template-id}/unpauseMeta 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 POSTPath 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:

PathJawaban Meta
/{phone-number-id}/conversational_automationcode 100 — "Tried accessing nonexisting field"
/{phone-number-id}/callscode 100 — "Tried accessing nonexisting field"
/{phone-number-id}/groupscode 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 bacaYang dipakai sebagai gantinya
POST /{phone-number-id}/mark_message_as_readPOST /{phone-number-id}/messages dengan isi status: "read" — lihat Menandai pesan sebagai sudah dibaca
GET /{phone-number-id}/message_statusStatus 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_componentsTidak 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.

Tab Webhooks dengan format payload diatur ke WizMessage, menampilkan kotak centang event v1

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.

Tab Webhooks dalam format Meta Graph, menampilkan verify token dan sembilan nama field Meta yang bisa dilanggan

Sebelum Anda bisa menyimpannya

Tiga hal diperlukan, dan dasbor akan memberi tahu mana yang hilang:

Apa yang harus ada sebelum webhook Meta Graph bisa disimpanWebhook secretmenjadi kunci tanda tanganVerify tokenmembuktikan URL itu milik AndaURL yang aktifkami memanggilnya sebelum menyimpanTersimpanpengiriman dimulaiURL yang belum menjawab tidak bisa disimpan. Deploy dulu, baru konfigurasikan.
Ketiganya diperiksa saat Anda menekan simpan, dengan urutan ini.
  • 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=.

HeaderNilai
X-Hub-Signature-256sha256= + HMAC-SHA256 dari isi mentah, berkunci webhook secret Anda, hex huruf kecil.
X-Webhook-Eventmeta.<field> — field mana yang dibawa pengiriman ini.
X-Webhook-IdId unik untuk upaya pengiriman ini. Hapus duplikat berdasarkan id ini.
User-Agentfacebookplatform/1.0 (+http://developers.facebook.com)

Catatan: Ini bukan turunan yang sama dengan format WizMessage. Format itu menandatangani timestamp + "." + rawBody dan 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.

FieldYang tiba
messagesPesan masuk dan status pengiriman. Baca catatan di bawah.
message_statusStatus pengiriman, sebagai field tersendiri.
smb_message_echoesPesan yang dikirim bisnis dari aplikasi WhatsApp Business di ponselnya.
smb_app_state_syncPerubahan buku alamat yang dilakukan di ponsel itu.
user_id_updateIdentitas WhatsApp seorang kontak berubah.
message_template_status_updateSebuah template disetujui, ditolak, atau dijeda.
message_template_quality_updateSkor kualitas sebuah template berubah.
account_updateAkun WhatsApp dihubungkan, diputus, dibatasi, atau di-offboard.
historyPercakapan 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: messages sudah membawa status pengiriman. Meta mengirim keduanya di bawah "field": "messages" — pesan masuk dalam array messages, laporan pengiriman dalam array statuses pada objek value yang sama. Jadi langganan bawaan cukup untuk mengorelasikan wamid yang Anda terima dari sebuah pengiriman. message_status adalah field tambahan, bukan yang Anda perlukan untuk pelacakan status dasar. Baca value.statuses sebelum 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.

Tiga perilaku yang berbeda antara kedua format webhookTanpa gerbang chatbotterkirim dalam kedua kasusv1 memerlukan mode bot = WebhookGraph mengabaikan pengaturan ituAnda menerima sentdan status kampanyev1 menyaring keduanyaGraph meneruskan semuanyaTanpa sent sintetisevent milik Meta sendirismb_message_echoesdan statusHarapkan lebih banyak lalu lintas pada format Graph daripada yang dihasilkan akun yang sama pada v1.
Ketiganya membuat format Graph mengirim lebih banyak, bukan lebih sedikit.
  • 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.sent sintetis. Format WizMessage membuatnya untuk pesan yang dikirim dari API, dasbor, atau ponsel. Meta mencakup hal yang sama dengan smb_message_echoes dan status sent, 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.

Dialog Test API dalam mode Meta Graph, menampilkan endpoint messages yang hanya-baca dan isi permintaan berbentuk Meta

"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.