Webhook Security & Verification
Wagy menggunakan webhook untuk mengirimkan data pesan masuk (incoming messages) dari WhatsApp secara real-time ke server Anda. Untuk menjamin keamanan dan integritas data, kami mengimplementasikan mekanisme verifikasi dua arah.
1. Verifikasi Kepemilikan URL (Handshake)
Saat Anda mendaftarkan atau memperbarui URL Webhook di User Panel, Wagy akan mengirimkan request GET ke URL tersebut untuk memastikan bahwa Anda adalah pemilik sah dari endpoint tersebut.
- Method:
GET - Query Parameter:
challenge(string acak)
Persyaratan Respons
Server Anda harus merespons request tersebut dengan:
- Status code
200 OK. - Body respons harus berisi hanya nilai dari parameter
challengetersebut (plain text).
Contoh (Node.js/Express):
app.get('/webhook', (req, res) => {
const challenge = req.query.challenge;
if (challenge) {
return res.status(200).send(challenge);
}
res.status(400).send('No challenge provided');
});
2. Signature Verification (HMAC-SHA256)
Setelah URL terverifikasi, Wagy akan menyertakan header X-Wagy-Signature pada setiap request POST (data pesan). Header ini berisi signature HMAC-SHA256 yang dibuat menggunakan Webhook Secret unik milik perangkat Anda.
Kunci rahasia (Webhook Secret) hanya akan dihasilkan dan muncul di User Panel setelah URL berhasil diverifikasi. Jangan berikan kunci ini kepada siapapun.
Cara Verifikasi Signature
Untuk memverifikasi bahwa request benar-benar berasal dari Wagy:
- Ambil raw JSON body dari request.
- Hitung HMAC-SHA256 dari body tersebut menggunakan Webhook Secret Anda.
- Bandingkan hasilnya dengan nilai header
X-Wagy-Signature.
- Node.js
- PHP
const crypto = require('crypto');
app.post('/webhook', (req, res) => {
const signature = req.headers['x-wagy-signature'];
const secret = 'YOUR_WEBHOOK_SECRET_FROM_PANEL';
if (!signature) {
return res.status(401).send('Missing signature');
}
// Hitung HMAC-SHA256 dari raw body
// Catatan: Pastikan Anda mengambil raw body sebelum di-parse oleh body-parser
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(JSON.stringify(req.body)).digest('hex');
// Bandingkan signature
if (signature !== digest) {
return res.status(401).send('Invalid signature');
}
// Signature valid, proses data
console.log('Verified message:', req.body);
res.sendStatus(200);
});
<?php
$secret = 'YOUR_WEBHOOK_SECRET_FROM_PANEL';
$signature = $_SERVER['HTTP_X_WAGY_SIGNATURE'] ?? '';
// Ambil raw body dari request
$payload = file_get_contents('php://input');
if (empty($signature)) {
http_response_code(401);
die('Missing signature');
}
// Hitung HMAC-SHA256
$computedSignature = hash_hmac('sha256', $payload, $secret);
// Bandingkan signature (gunakan hash_equals untuk mencegah timing attack)
if (!hash_equals($signature, $computedSignature)) {
http_response_code(401);
die('Invalid signature');
}
// Signature valid, proses data
$data = json_decode($payload, true);
// ... logika bisnis Anda ...
http_response_code(200);
3. Payload Webhook
Setiap request POST menggunakan struktur Event-Based Payload agar konsisten untuk berbagai jenis kejadian.
Struktur Utama
| Field | Tipe | Deskripsi |
|---|---|---|
event | string | Jenis kejadian (misal: message.received). |
source | string | Sumber kejadian (whatsapp, api, manual, system). |
data | object | Objek pembungkus data utama. |
data.id | number | ID unik log webhook (audit ID). |
data.device_id | string | ID perangkat Wagy. |
data.content | object | Data spesifik event (lihat contoh di bawah). |
Contoh Payload Berdasarkan Event
Wagy menggunakan struktur Event-Based Payload. Objek data.content akan memiliki struktur yang berbeda tergantung pada tipe eventnya.
1. Pesan Masuk (Inbound)
- Event:
message.received,message.edited - Source:
whatsapp
{
"event": "message.received",
"source": "whatsapp",
"data": {
"id": 1001,
"device_id": "OFFICE-01",
"owner_jid": "62812345678@s.whatsapp.net",
"content": {
"pn_jid": "628999888777@s.whatsapp.net",
"lid_jid": "203998263034091@lid",
"content": "Halo, apakah stok ready?",
"message_id": "ABC123XYZ",
"timestamp": "2026-05-13T07:05:00Z",
"is_edit": false
},
"created_at": "2026-05-13T07:05:00Z"
}
}
2. Pesan Keluar Manual (Handset)
- Event:
message.manual_sent,message.manual_edited - Source:
manual
{
"event": "message.manual_sent",
"source": "manual",
"data": {
"id": 1002,
"device_id": "OFFICE-01",
"owner_jid": "62812345678@s.whatsapp.net",
"content": {
"pn_jid": "628999888777@s.whatsapp.net",
"lid_jid": "203998263034091@lid",
"content": "Ready kak!",
"message_id": "XYZ789ABC",
"timestamp": "2026-05-13T07:10:00Z",
"is_edit": false
},
"created_at": "2026-05-13T07:10:00Z"
}
}
3. Status Pengiriman API
- Event:
message.sent,message.delivered,message.read,message.failed,message.cancelled - Source:
api
{
"event": "message.delivered",
"source": "api",
"data": {
"id": 1003,
"device_id": "OFFICE-01",
"owner_jid": "62812345678@s.whatsapp.net",
"content": {
"message_id": "MSG_ID_WA_123",
"queue_id": 12345,
"pn_jid": "628999888777@s.whatsapp.net",
"lid_jid": "203998263034091@lid",
"content": "Terima kasih!",
"status": "DELIVERED"
},
"created_at": "2026-05-13T07:15:00Z"
}
}
4. Status Perangkat
- Event:
device.status_update - Source:
system
{
"event": "device.status_update",
"source": "system",
"data": {
"id": 1004,
"device_id": "OFFICE-01",
"owner_jid": "62812345678@s.whatsapp.net",
"content": {
"device_id": "OFFICE-01",
"status": "CONNECTED",
"label": "Admin Sales"
},
"created_at": "2026-05-13T07:20:00Z"
}
}
Keamanan Webhook
Wagy mengirimkan header X-Wagy-Signature yang berisi HMAC-SHA256 dari body request. Gunakan Webhook Secret Anda untuk memverifikasi keaslian data.
Selalu verifikasi signature untuk memastikan data benar-benar berasal dari server Wagy dan bukan serangan spoofing.