Panduan/Untuk developer

Menerima balasan & status pesan lewat webhook

Aktifkan webhook, baca payload, dan verifikasi tanda tangan WACO. 5 menit baca

Mengirim saja membuat aplikasi Anda buta: ia tidak tahu saat pelanggan membalas, dan tidak tahu apakah pesan benar-benar sampai. Webhook menutup itu — WACO mengirim (POST) tiap kejadian ke URL Anda begitu terjadi.

Berjalan berdampingan dengan ruang percakapan. Webhook aktif untuk semua akun — termasuk yang memakai ruang percakapan tim. Event yang sama dikirim ke keduanya: tim Anda membalas dari ruang percakapan, sistem Anda menerima salinannya lewat webhook. Keduanya tidak saling menunggu — gangguan di salah satunya tidak menahan yang lain.

Mengaktifkan

Di halaman Developer, isi URL webhook Anda (wajib HTTPS) dan simpan. WACO menampilkan sebuah secret (whsec_…) untuk memverifikasi tanda tangan — catat sekali, dipakai selamanya.

Kejadian yang Anda terima

Dua jenis, dibedakan oleh medan event:

Balasan masuk — message.received

{
  "event": "message.received",
  "waktu": "2026-08-13T05:20:00.000Z",
  "dari": "628111222333",
  "nama": "Budi",
  "nomor_bisnis": "628158825011",
  "pesan": { "id": "wamid.ABC", "tipe": "text", "teks": "Halo, mau daftar" }
}

Untuk tombol interaktif, pesan.teks berisi label yang ditekan.

Media — gambar, dokumen, audio, video, stiker

Kalau yang masuk media, pesan.tipe menyebut jenisnya dan detailnya di pesan.data. Caption (bila ada) ikut muncul di pesan.teks:

{
  "event": "message.received",
  "waktu": "2026-08-13T05:20:00.000Z",
  "dari": "628111222333",
  "nama": "Budi",
  "pesan": {
    "id": "wamid.ABC",
    "tipe": "image",
    "teks": "ini foto KTP-nya",
    "data": {
      "id": "1085073820629812",
      "mime_type": "image/jpeg",
      "sha256": "e5f8…",
      "caption": "ini foto KTP-nya"
    }
  }
}

Medan pesan.data mengikuti jenisnya: image/videoid, mime_type, sha256, caption?; document menambah filename; audio menambah voice; sticker menambah animated.

Mengunduh file media

pesan.data.id adalah ID media, bukan URL — filenya tidak ikut di webhook. Unduh lewat endpoint ini dengan kunci API Anda; gateway yang mengambilnya dari Meta, jadi Anda tidak perlu kredensial apa pun selain kunci waco_…:

curl -L https://waco.id/api/v1/media/1085073820629812 \
  -H "Authorization: Bearer waco_KUNCI_ANDA" \
  -o ktp.jpg

Jawabannya adalah file itu sendiri (Content-Type sesuai mime_type). Anda hanya bisa mengunduh media yang masuk ke nomor Anda sendiri.

Status pesan — message.status

{
  "event": "message.status",
  "waktu": "2026-08-13T05:21:00.000Z",
  "pesan_id": "wamid.ABC",
  "status": "delivered",
  "ke": "628111222333"
}

status bernilai sent, delivered, read, atau failed. pesan_id cocok dengan message_id yang dikembalikan saat Anda mengirim, jadi Anda bisa memetakannya ke pengiriman Anda.

Pesan dari aplikasi WhatsApp di HP — message.echo (opsional)

Kalau nomor Anda memakai mode berdampingan (masih dipakai di aplikasi WhatsApp Business di HP sekaligus lewat API), staf kadang membalas langsung dari HP. Secara bawaan balasan itu tidak dikirim ke webhook Anda, sehingga di sistem Anda percakapannya terlihat sepihak.

Nyalakan “Ikutkan pesan yang dikirim dari aplikasi WhatsApp di HP” di halaman Developer untuk menerima salinannya:

{
  "event": "message.echo",
  "waktu": "2026-08-30T04:11:00.000Z",
  "ke": "628111222333",
  "nomor_bisnis": "628770001111",
  "sumber": "aplikasi_whatsapp",
  "pesan": {
    "id": "wamid.ABC",
    "tipe": "text",
    "teks": "Baik pak, saya cek dulu ya"
  }
}

Bedanya dengan message.received: arahnya keluar, jadi bidangnya ke (bukan dari) dan tidak ada nama. Untuk media, pesan.data berisi objek aslinya dan bisa diunduh lewat GET /api/v1/media/{id} seperti pesan masuk. pesan.id memakai wamid yang sama dengan message.status, jadi keduanya bisa dicocokkan.

Catatan: pesan yang Anda kirim sendiri lewat API tidak ikut di sini — Anda sudah punya message_id-nya dari respons /kirim. Fitur ini mati secara bawaan supaya integrasi yang sudah jalan tidak tiba-tiba menerima jenis event baru.

Verifikasi tanda tangan — WAJIB

Setiap POST membawa header X-WACO-Signature: sha256=…, yaitu HMAC-SHA256 atas body mentah memakai secret Anda. Hitung ulang dan bandingkan sebelum mempercayai payload — kalau tidak, siapa pun yang tahu URL Anda bisa memalsukannya.

Node.js (pakai body mentah, bukan JSON yang sudah di-parse):

const crypto = require('crypto');

app.post('/webhook/waco', express.raw({ type: 'application/json' }), (req, res) => {
  const diterima = req.header('X-WACO-Signature') || '';
  const dihitung = 'sha256=' +
    crypto.createHmac('sha256', process.env.WACO_WEBHOOK_SECRET)
          .update(req.body).digest('hex');
  const ok = diterima.length === dihitung.length &&
    crypto.timingSafeEqual(Buffer.from(diterima), Buffer.from(dihitung));
  if (!ok) return res.sendStatus(401);

  const event = JSON.parse(req.body.toString());
  // ... proses event ...
  res.sendStatus(200);
});

PHP:

$body = file_get_contents('php://input');
$diterima = $_SERVER['HTTP_X_WACO_SIGNATURE'] ?? '';
$dihitung = 'sha256=' . hash_hmac('sha256', $body, getenv('WACO_WEBHOOK_SECRET'));
if (!hash_equals($dihitung, $diterima)) { http_response_code(401); exit; }

$event = json_decode($body, true);
// ... proses event ...
http_response_code(200);

Yang perlu diketahui

Memantau pengiriman

Riwayat pengiriman webhook tampil di halaman Developer (15 event terakhir). Arti statusnya:

Loop dua-arah: terima balasan di webhook ini, lalu jawab dengan balas bebas (/api/v1/balas) selama masih dalam jendela 24 jam — tanpa template.
Masih menemui kendala setelah mengikuti panduan ini? Hubungi kami dari dashboard — sertakan tangkapan layar bila ada, itu mempercepat semuanya.