Skip to main content

Webhook Security & Verification

Wagy uses webhooks to send incoming message data from WhatsApp in real-time to your server. To guarantee data security and integrity, we implement a two-way verification mechanism.

1. URL Ownership Verification (Handshake)

When you register or update a Webhook URL in the User Panel, Wagy will send a GET request to that URL to ensure that you are the legitimate owner of the endpoint.

  • Method: GET
  • Query Parameter: challenge (random string)

Response Requirements

Your server must respond to the request with:

  1. Status code 200 OK.
  2. The response body must contain only the value of the challenge parameter (plain text).

Example (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)

Once the URL is verified, Wagy will include the X-Wagy-Signature header in every POST request (message data). This header contains an HMAC-SHA256 signature created using your device's unique Webhook Secret.

Webhook Secret

The Webhook Secret will only be generated and appear in the User Panel after the URL has been successfully verified. Do not share this secret with anyone.

How to Verify Signature

To verify that the request truly originated from Wagy:

  1. Get the raw JSON body of the request.
  2. Calculate the HMAC-SHA256 of the body using your Webhook Secret.
  3. Compare the result with the value of the X-Wagy-Signature header.
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');
}

// Calculate HMAC-SHA256 from raw body
// Note: Make sure you get the raw body before it's parsed by body-parser
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(JSON.stringify(req.body)).digest('hex');

// Compare signature
if (signature !== digest) {
return res.status(401).send('Invalid signature');
}

// Signature valid, process data
console.log('Verified message:', req.body);
res.sendStatus(200);
});

3. Webhook Payload

Every POST request uses an Event-Based Payload structure for consistency across different types of events.

Main Structure

FieldTypeDescription
eventstringType of event (e.g., message.received).
sourcestringEvent source (whatsapp, api, manual, system).
dataobjectMain data wrapper object.
data.idnumberUnique webhook log ID (audit ID).
data.device_idstringWagy device ID.
data.contentobjectEvent-specific data (see examples below).

Example Payloads by Event

Wagy uses an Event-Based Payload structure. The data.content object will have a different structure depending on the event type.

1. Incoming Message (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": "Hello, is stock ready?",
"message_id": "ABC123XYZ",
"timestamp": "2026-05-13T07:05:00Z",
"is_edit": false
},
"created_at": "2026-05-13T07:05:00Z"
}
}

2. Manual Outbound Message (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!",
"message_id": "XYZ789ABC",
"timestamp": "2026-05-13T07:10:00Z",
"is_edit": false
},
"created_at": "2026-05-13T07:10:00Z"
}
}

3. API Delivery Status

  • 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": "Thank you!",
"status": "DELIVERED"
},
"created_at": "2026-05-13T07:15:00Z"
}
}

4. Device Status

  • 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"
}
}

Webhook Security

Wagy sends the X-Wagy-Signature header containing the HMAC-SHA256 of the request body. Use your Webhook Secret to verify the authenticity of the data.

Verification is Important!

Always verify the signature to ensure that the data truly originated from Wagy's server and is not a spoofing attack.