+
A

Oleh Avi

Dipublikasikan pada 31 Jul 2026

Webhook WhatsApp: Cara Menyiapkan dan Menangani Peristiwa Masuk dengan Chakra Chat

Setiap integrasi WhatsApp dua arah dimulai dengan webhook. Panduan ini membahas dua jenis webhook Chakra, payload peristiwa nyata, verifikasi tanda tangan HMAC, penanganan pengiriman duplikat, dan pembuatan router pesan — semua yang dibutuhkan n8n, Zapier, dan pembuat integrasi kustom.

Setiap integrasi WhatsApp dua arah — chatbot, sinkronisasi CRM, otomatisasi n8n, atau alur notifikasi kustom — dimulai dari tempat yang sama: webhook. Cloud API WhatsApp bersifat asinkron. Saat Anda mengirim pesan, Anda menerima konfirmasi bahwa pesan telah diterima, bukan konfirmasi bahwa pesan sudah sampai. Semua yang terjadi setelah itu — terkirim, tanda terima telah dibaca, balasan, kegagalan — akan dikirim kembali kepada Anda sebagai peristiwa melalui webhook yang Anda kendalikan.

Panduan ini membahas cara Chakra menyediakan peristiwa tersebut, dua jenis webhook berbeda yang tersedia, struktur payload-nya, cara memastikan peristiwa benar-benar berasal dari Chakra, cara menangani pengiriman duplikat yang pada akhirnya akan Anda temui, serta pola router sederhana untuk menangani semuanya dengan rapi di satu tempat.

Dua Jenis Webhook, Bukan Satu

Chakra menyediakan dua cara terpisah untuk menerima peristiwa masuk, dan memilih cara yang tepat akan menentukan seberapa banyak pekerjaan penguraian data yang perlu Anda lakukan.

Webhook Peristiwa Pass-ThroughWebhook Peristiwa Chakra
FormatPayload Cloud API Meta mentah, tanpa perubahanPayload sederhana yang dinormalisasi oleh Chakra
Cakupan peristiwaSemua yang dikirim Meta — pesan, status, pengiriman template, pembaruan akunKumpulan pilihan: message, status, message_echo, smb_message_echo
Paling cocok untukTim yang sudah familier dengan skema webhook Meta atau sedang memindahkan integrasi yang sudah adaTim yang ingin mengurangi beban penguraian data dan mendapatkan format yang konsisten, terlepas dari perubahan skema Meta
Penggunaan umumAlur n8n/Zapier yang dibuat berdasarkan format webhook Meta yang terdokumentasiIntegrasi backend kustom, sinkronisasi CRM, logika perutean yang lebih sederhana

Anda dapat mengonfigurasi salah satu atau keduanya — pengaturannya terpisah, dan Anda dapat menggunakan webhook Chakra untuk integrasi utama serta webhook pass-through untuk alat tertentu seperti n8n yang mengharapkan format asli Meta.

Menyiapkan Webhook Pass-Through

Webhook ini mengirimkan format peristiwa Meta sendiri, tanpa perubahan, persis seperti yang dijelaskan dalam referensi webhook Cloud API Meta.

Untuk mengonfigurasinya:

  1. Buka halaman WhatsApp Setup di dasbor Chakra Anda
  2. Klik tab More
  3. Temukan kolom berlabel "Pass-through webhook URL for Meta events"
  4. Tempel URL endpoint Anda
  5. Klik Save

Inilah cara yang digunakan oleh sebagian besar integrasi n8n dan Zapier, karena platform tersebut biasanya mengharapkan (atau dibuat berdasarkan) format webhook Meta sendiri.

Menyiapkan Webhook Peristiwa Chakra

Ini adalah format Chakra yang dinormalisasi — struktur yang konsisten terlepas dari perubahan yang dilakukan Meta.

Untuk mengonfigurasinya:

  1. Buka WhatsApp Setup → More
  2. Aktifkan "Enable Chakra Webhooks" toggle
  3. Masukkan endpoint Anda di kolom "Chakra webhook URL"
  4. Pilih jenis peristiwa yang Anda inginkan di bagian "Which events to send" — hanya jenis yang Anda centang yang akan dikirim
  5. Klik Save

Endpoint Anda harus dapat diakses secara publik dan tidak memerlukan autentikasi di sisi Anda (verifikasi dilakukan melalui tanda tangan HMAC — selengkapnya di bawah).

Jenis Peristiwa dan Struktur Payload

Format Pass-Through (Format Asli Meta)

Semua data tiba dengan struktur standar Meta, yaitu entry → changes → value , dengan field yang menunjukkan jenis peristiwanya. Dua jenis yang paling umum:

Pesan teks masuk (field: "messages"):

{  "object": "whatsapp_business_account",  "entry": [    {      "id": "83784929738012",      "changes": [        {          "value": {            "messaging_product": "whatsapp",            "metadata": {              "display_phone_number": "917259775805",              "phone_number_id": "694794789348887"            },            "contacts": [              { "profile": { "name": "John" }, "wa_id": "919911223344" }            ],            "messages": [              {                "from": "919911223344",                "id": "wamid.HBgMOTEST3AxMjU4NDMzFQIAEhgUM0VCMDM3RjlEQTNDQTBEMjQ2Q275",                "timestamp": "1756322278",                "type": "text",                "text": { "body": "Hi, can you please help me out with an issue" }              }            ]          },          "field": "messages"        }      ]    }  ]}

Pesan gambar masuk — strukturnya sama, tetapi type:

{  "messages": [    {      "from": "919911223344",      "id": "wamid.HBgMOTEST3AxMjU4NDMzFQIAEhgUM0VCMDM3RjlEQTNDQTBEMjQ2Q275",      "timestamp": "1756322278",      "type": "image",      "image": {        "mime_type": "image/jpeg",        "sha256": "t8K9fuzwAoeBSOoIZWkBlI1hTflUHqSwCvGwJ+exm74=",        "id": "1402363984735353"      }    }  ]}

Perhatikan bahwa peristiwa gambar hanya memberikan ID media, bukan file itu sendiri — Anda perlu mengambil file media secara terpisah melalui endpoint pengambilan media Chakra menggunakan ID tersebut.

Peristiwa pengiriman template (field: "message_template_sends") — dipicu saat pesan template yang dipanggil melalui API dikirim. Peristiwa ini berguna untuk mencatat secara persis apa yang dikirim dan kepada siapa, tanpa perlu mengelola log pengiriman terpisah:

{  "field": "message_template_sends",  "value": {    "message_template_sends": [      {        "id": "wamid.HBgMOTE5OTAxMjU4NDMzFQIAERgSMjNBQkQ5NDA5NTFEN0MzOTU2AA==",        "timestamp": 1763821065,        "to": "919911223344",        "type": "template",        "template": {          "language": { "policy": "deterministic", "code": "en_US" },          "name": "new_year_promo_24",          "components": [            { "type": "body", "parameters": [{ "type": "text", "text": "John" }] }          ]        }      }    ]  }}

Karena webhook ini meneruskan format Meta tanpa perubahan, peristiwa status terkirim/dibaca, peristiwa kesalahan, dan pembaruan tingkat akun mengikuti struktur yang sama seperti yang dijelaskan dalam referensi webhook Cloud API Meta — sebaiknya simpan halaman tersebut di bookmark bersama panduan ini.

Format Peristiwa Chakra

Setiap peristiwa tiba dengan tepat dua kunci tingkat teratas:

{  "event": "message",  "payload": { /* MessagePayload or DeliveryStatusPayload, depending on event type */ }}

event memiliki salah satu dari empat nilai:

  • message — pesan masuk yang diterima di nomor Anda
  • status — pembaruan status pengiriman untuk pesan yang sebelumnya dikirim melalui API (terkirim, diterima, dibaca, gagal)
  • message_echo — pesan keluar yang dikirim dari dalam platform Chakra (misalnya, balasan agen dari kotak masuk bersama), berguna jika Anda menyinkronkan seluruh riwayat percakapan ke sistem eksternal
  • smb_message_echo — pesan keluar yang dikirim dari aplikasi WhatsApp Business, khususnya relevan jika Anda menggunakan Coexistence dan perlu merekam pesan yang dikirim dari aplikasi, bukan hanya dari API

Bentuk yang telah dinormalisasi inilah yang membuat webhook Chakra lebih mudah digunakan sebagai dasar router — Anda selalu membuat percabangan berdasarkan field event yang sama, apa pun bentuk skema dasar Meta pada bulan tersebut.

Memverifikasi Keaslian Webhook dengan HMAC

Karena endpoint webhook Anda bersifat publik dan tidak memerlukan autentikasi untuk permintaan masuk, penting untuk memverifikasi bahwa suatu event benar-benar berasal dari Chakra (dan belum dirusak) sebelum Anda menindaklanjutinya.

Penyiapan:

  1. Buka Admin → Team → Secrets dan atur secret HMAC
  2. Setelah diatur, setiap panggilan webhook menyertakan header X-Chakra-Signature-256

Logika verifikasi: buat hash HMAC-SHA256 dari string isi permintaan mentah menggunakan secret Anda sebagai kunci, lalu bandingkan dengan nilai header.

const crypto = require("crypto");function verifyChakraSignature(rawBody, signatureHeader, secret) {  const expectedHash = crypto    .createHmac("sha256", secret)    .update(rawBody) // must be the raw string, not a parsed object    .digest("hex");  // timing-safe comparison  return crypto.timingSafeEqual(    Buffer.from(expectedHash, "utf8"),    Buffer.from(signatureHeader, "utf8")  );}app.post("/webhooks/chakra", express.raw({ type: "*/*" }), (req, res) => {  const rawBody = req.body.toString("utf8");  const signature = req.headers["x-chakra-signature-256"];  if (!verifyChakraSignature(rawBody, signature, process.env.CHAKRA_HMAC_SECRET)) {    return res.status(401).send("Invalid signature");  }  const event = JSON.parse(rawBody);  handleEvent(event);  res.sendStatus(200);});

Hal yang sering membingungkan: Anda harus membuat hash dari string isi mentah, bukan versi yang telah diurai menjadi JSON lalu diserialisasi kembali — sebagian besar framework mengurai isi secara otomatis sebelum handler Anda dijalankan, jadi biasanya Anda memerlukan middleware raw-body (seperti yang ditunjukkan di atas dengan express.raw) khusus untuk rute ini. Perhatikan juga bahwa nilai header tidak memiliki awalan sha256= , berbeda dengan konvensi HMAC beberapa penyedia lain — jangan hapus awalan yang memang tidak ada.

Menangani Event Duplikat

Pengiriman webhook WhatsApp pada praktiknya bersifat setidaknya sekali, bukan tepat sekali — percobaan ulang jaringan, endpoint yang sementara tidak aktif, atau percobaan ulang dari sisi Meta dapat menyebabkan event yang sama diterima lebih dari sekali. Membangun deduplikasi sejak awal mencegah pemrosesan pesan berulang (mengirim dua balasan, menghitung status pengiriman dua kali, atau memicu workflow dua kali).

Kunci deduplikasi yang andal adalah ID pesan (wamid... untuk pesan, atau field ID yang sesuai untuk event status) — simpan ID yang baru diproses dan periksa sebelum mengambil tindakan:

const processedIds = new Map(); // swap for Redis/DB in productionconst DEDUP_TTL_MS = 10 * 60 * 1000; // 10 minutes is generally enoughfunction isDuplicate(messageId) {  const seenAt = processedIds.get(messageId);  if (seenAt && Date.now() - seenAt < DEDUP_TTL_MS) return true;  processedIds.set(messageId, Date.now());  return false;}function handleEvent(event) {  const messageId = event.payload?.id || event.payload?.messages?.[0]?.id;  if (messageId && isDuplicate(messageId)) {    return; // already processed, skip silently  }  routeEvent(event);}

Untuk penggunaan produksi, gunakan Redis atau database Anda dengan TTL, bukan Map dalam memori — Map dalam satu proses tidak akan bertahan setelah aplikasi dimulai ulang atau berfungsi di beberapa instans server.

Membuat Router Pesan Sederhana

Setelah event diverifikasi dan dideduplikasi, langkah praktis berikutnya adalah mengarahkannya ke handler yang tepat berdasarkan jenisnya. Berikut router minimal untuk format event Chakra:

function routeEvent(event) {  switch (event.event) {    case "message":      return handleInboundMessage(event.payload);    case "status":      return handleStatusUpdate(event.payload);    case "message_echo":      return handleAgentReply(event.payload);    case "smb_message_echo":      return handleAppSentMessage(event.payload);    default:      console.warn("Unhandled event type:", event.event);  }}function handleInboundMessage(payload) {  const messageType = payload.type; // "text", "image", "interactive", etc.  switch (messageType) {    case "text":      return processTextMessage(payload);    case "image":    case "document":      return processMediaMessage(payload); // fetch media by ID separately    case "interactive":      return processButtonOrListReply(payload);    default:      console.log("Unrecognized message type:", messageType);  }}function handleStatusUpdate(payload) {  // payload.status: "sent" | "delivered" | "read" | "failed"  updateMessageRecordInDb(payload.id, payload.status, payload.errorCode);}

Pola percabangan yang sama juga mudah diterapkan jika Anda menggunakan webhook pass-through — buat percabangan berdasarkan changes[].field (messages, message_template_sends, message_status yang setara) alih-alih event, lalu telusuri ke value.messages[0].type dengan cara yang sama.

Catatan tentang Event Panggilan

Jika Anda menggunakan Calling API Chakra bersama fitur pesan, event terkait panggilan (dimulai, tersambung, berakhir, tidak terjawab) dikirim melalui mekanisme pass-through yang sama, mengikuti skema webhook event panggilan milik Meta. Jika router Anda sudah membuat percabangan berdasarkan field untuk event terkait pesan, memperluasnya agar menangani field calls mengikuti pola yang sama — lihat dokumentasi resmi Cloud API Meta untuk mengetahui nama field event panggilan yang tepat, karena skema tersebut diteruskan tanpa perubahan, bukan dinormalisasi oleh Chakra.

Menggunakan Ini dengan n8n

Untuk n8n, penyiapan yang umum menggunakan webhook pass-through, karena workflow WhatsApp dan templat komunitas n8n umumnya dibuat berdasarkan format asli Meta:

  1. Siapkan node pemicu Webhook di n8n, lalu salin URL pengujian/produksinya
  2. Tempelkan URL tersebut ke field "Pass-through webhook URL for Meta events" di WhatsApp Setup → More
  3. Tambahkan node filter/if di awal workflow untuk mengabaikan event nonpesan (pembaruan status, konfirmasi pengiriman templat) jika alur Anda hanya menangani pesan masuk
  4. Dari sana, arahkan ke node AI Agent, node CRM, atau komponen lain yang dibutuhkan workflow Anda — payload Meta mentah tersedia untuk dirujuk di seluruh alur

Di sinilah langkah filter di awal sangat berguna: tanpa filter tersebut, setiap event status dan pengiriman akan memicu seluruh workflow Anda tanpa perlu, sehingga menambah biaya dan notifikasi yang tidak perlu pada platform yang mengenakan biaya per eksekusi.

Beberapa Catatan Praktis Sebelum Diluncurkan

  • Berikan respons 200 dengan cepat, lalu proses secara asinkron. Segera konfirmasi penerimaan webhook dan lakukan pemrosesan yang lambat (penulisan ke database, pemanggilan API lain) setelah merespons atau melalui tugas latar belakang — respons yang lambat berisiko membuat Meta atau Chakra menganggap pengiriman gagal lalu mencoba lagi, sehingga beban penanganan duplikat bertambah.
  • Catat payload mentah selama pengembangan. Skema Meta memiliki sedikit variasi antarjenis pesan, dan menyimpan contoh mentah akan menghemat waktu saat Anda menghadapi kasus khusus yang belum ditangani router.
  • Jangan simpan secret HMAC di version control, tentu saja, tetapi rotasikan juga jika Anda mencurigai secret tersebut telah terekspos — membuat secret baru di Admin → Team → Secrets akan langsung membatalkan tanda tangan lama.
  • Pantau waktu aktif endpoint Anda. Jika webhook tidak aktif saat suatu event terjadi, pesan atau pembaruan status akan terlewat di sisi Anda, tanpa pengisian ulang otomatis — pantau endpoint ini seperti layanan lain yang penting bagi produksi.

Menyatukan Semuanya

Webhook adalah fondasi yang kurang terlihat, tetapi menjadi tumpuan semua hal lain dalam integrasi WhatsApp — pastikan format event, verifikasi, dan deduplikasi sudah benar sejak awal, maka setiap chatbot, sinkronisasi CRM, atau otomatisasi yang Anda bangun di atasnya akan mewarisi keandalan tersebut. Mulailah dengan jenis webhook yang sesuai dengan perangkat yang Anda gunakan (format Chakra yang dinormalisasi untuk backend khusus, pass-through untuk n8n atau alat yang menggunakan skema Meta), verifikasi tanda tangan sejak hari pertama alih-alih menambahkannya belakangan, dan lakukan deduplikasi berdasarkan ID pesan sebelum router menjalankan tindakan apa pun yang mengubah status.

Jika Anda sedang membangun ini, dokumentasi API lengkap Chakra mencakup referensi lengkap event dan endpoint yang disebutkan di sepanjang panduan ini.