This document provides comprehensive documentation for the webhook payload structure used by the Go WhatsApp Web Multidevice application.
The webhook system sends HTTP POST requests to configured URLs whenever WhatsApp events occur. Each webhook request includes event data in JSON format and security headers for verification.
All webhook requests include an HMAC SHA256 signature for security verification:
- Header:
X-Hub-Signature-256 - Format:
sha256={signature} - Algorithm: HMAC SHA256
- Default Secret:
secret(configurable via--webhook-secretorWHATSAPP_WEBHOOK_SECRET)
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
const receivedSignature = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
received_signature = signature.replace('sha256=', '')
return hmac.compare_digest(expected_signature, received_signature)All webhook payloads share these common fields:
| Field | Type | Description |
|---|---|---|
sender_id |
string | User part of sender JID (phone number, without @s.whatsapp.net) |
chat_id |
string | User part of chat JID |
from |
string | Full JID of the sender (e.g., 628123456789@s.whatsapp.net) |
timestamp |
string | RFC3339 formatted timestamp (e.g., 2023-10-15T10:30:00Z) |
pushname |
string | Display name of the sender |
{
"sender_id": "628123456789",
"chat_id": "628987654321",
"from": "628123456789@s.whatsapp.net",
"timestamp": "2023-10-15T10:30:00Z",
"pushname": "John Doe",
"message": {
"text": "Hello, how are you?",
"id": "3EB0C127D7BACC83D6A1",
"replied_id": "",
"quoted_message": ""
}
}{
"sender_id": "628123456789",
"chat_id": "628987654321",
"from": "628123456789@s.whatsapp.net",
"timestamp": "2023-10-15T10:35:00Z",
"pushname": "John Doe",
"message": {
"text": "I'm doing great, thanks!",
"id": "3EB0C127D7BACC83D6A2",
"replied_id": "3EB0C127D7BACC83D6A1",
"quoted_message": "Hello, how are you?"
}
}{
"sender_id": "628123456789",
"chat_id": "628987654321",
"from": "628123456789@s.whatsapp.net",
"timestamp": "2023-10-15T10:40:00Z",
"pushname": "John Doe",
"reaction": {
"message": "👍",
"id": "3EB0C127D7BACC83D6A1"
},
"message": {
"text": "",
"id": "88760C69D1F35FEB239102699AE9XXXX",
"replied_id": "",
"quoted_message": ""
}
}Receipt events are triggered when messages receive acknowledgments such as delivery confirmations and read receipts.
These events use the message.ack event type and provide information about message status changes.
Triggered when a message is successfully delivered to the recipient's device.
{
"event": "message.ack",
"payload": {
"chat_id": "120363402106XXXXX@g.us",
"from": "6289685XXXXXX@s.whatsapp.net in 120363402106XXXXX@g.us",
"ids": [
"3EB00106E8BE0F407E88EC"
],
"receipt_type": "delivered",
"receipt_type_description": "means the message was delivered to the device (but the user might not have noticed).",
"sender_id": "6289685XXXXXX@s.whatsapp.net"
},
"timestamp": "2025-07-18T22:44:20Z"
}Triggered when a message is read by the recipient (they opened the chat and saw the message).
{
"event": "message.ack",
"payload": {
"chat_id": "120363402106XXXXX@g.us",
"from": "6289685XXXXXX@s.whatsapp.net in 120363402106XXXXX@g.us",
"ids": [
"3EB00106E8BE0F407E88EC"
],
"receipt_type": "read",
"receipt_type_description": "the user opened the chat and saw the message.",
"sender_id": "6289685XXXXXX@s.whatsapp.net"
},
"timestamp": "2025-07-18T22:44:44Z"
}| Field | Type | Description |
|---|---|---|
event |
string | Always "message.ack" for receipt events |
payload.chat_id |
string | Chat identifier (group or individual chat) |
payload.from |
string | Sender information with chat context |
payload.ids |
array | Array of message IDs that received the acknowledgment |
payload.receipt_type |
string | Type of receipt: "delivered", "read", etc. |
payload.receipt_type_description |
string | Human-readable description of the receipt type |
payload.sender_id |
string | JID of the message sender |
timestamp |
string | RFC3339 formatted timestamp when the receipt was received |
Group events are triggered when group metadata changes, including member join/leave events, admin promotions/demotions, and group settings updates. These events use the group.participants event type and provide comprehensive information about group changes.
Triggered when users join or are added to a group.
{
"event": "group.participants",
"payload": {
"chat_id": "120363402106XXXXX@g.us",
"type": "join",
"jids": [
"6289685XXXXXX@s.whatsapp.net",
"6289686YYYYYY@s.whatsapp.net"
]
},
"timestamp": "2025-07-28T10:30:00Z"
}Triggered when users leave or are removed from a group.
{
"event": "group.participants",
"payload": {
"chat_id": "120363402106XXXXX@g.us",
"type": "leave",
"jids": [
"6289687ZZZZZZ@s.whatsapp.net"
]
},
"timestamp": "2025-07-28T10:32:00Z"
}Triggered when users are promoted to admin.
{
"event": "group.participants",
"payload": {
"chat_id": "120363402106XXXXX@g.us",
"type": "promote",
"jids": [
"6289688AAAAAA@s.whatsapp.net"
]
},
"timestamp": "2025-07-28T10:33:00Z"
}Triggered when users are demoted from admin.
{
"event": "group.participants",
"payload": {
"chat_id": "120363402106XXXXX@g.us",
"type": "demote",
"jids": [
"6289689BBBBBB@s.whatsapp.net"
]
},
"timestamp": "2025-07-28T10:34:00Z"
}| Field | Type | Description |
|---|---|---|
event |
string | Always "group.participants" for group events |
payload.chat_id |
string | Group identifier (e.g., "120363402106XXXXX@g.us") |
payload.type |
string | Action type: "join", "leave", "promote", or "demote" |
payload.jids |
array | Array of user JIDs affected by this action |
timestamp |
string | RFC3339 formatted timestamp when the group event occurred |
{
"sender_id": "628123456789",
"chat_id": "628123456789",
"from": "628123456789@s.whatsapp.net",
"timestamp": "2025-07-13T11:05:51Z",
"pushname": "John Doe",
"message": {
"text": "",
"id": "********************",
"replied_id": "",
"quoted_message": ""
},
"image": {
"media_path": "statics/media/1752404751-ad9e37ac-c658-4fe5-8d25-ba4a3f4d58fd.jpe",
"mime_type": "image/jpeg",
"caption": "gijg"
}
}{
"sender_id": "628123456789",
"chat_id": "628123456789",
"from": "628123456789@s.whatsapp.net",
"timestamp": "2025-07-13T11:07:24Z",
"pushname": "Notification System",
"message": {
"text": "",
"id": "********************",
"replied_id": "",
"quoted_message": ""
},
"video": {
"media_path": "statics/media/1752404845-b9393cd1-8546-4df9-8a60-ee3276036aba.m4v",
"mime_type": "video/mp4",
"caption": "okk"
}
}{
"sender_id": "628123456789",
"chat_id": "628987654321",
"from": "628123456789@s.whatsapp.net",
"timestamp": "2023-10-15T10:55:00Z",
"pushname": "John Doe",
"message": {
"text": "",
"id": "3EB0C127D7BACC83D6A5",
"replied_id": "",
"quoted_message": ""
},
"audio": {
"media_path": "statics/media/1752404905-b9393cd1-8546-4df9-8a60-ee3276036aba.m4v",
"mime_type": "audio/ogg",
"caption": "okk"
}
}{
"sender_id": "628123456789",
"chat_id": "628987654321",
"from": "628123456789@s.whatsapp.net",
"timestamp": "2023-10-15T11:00:00Z",
"pushname": "John Doe",
"message": {
"text": "",
"id": "3EB0C127D7BACC83D6A6",
"replied_id": "",
"quoted_message": ""
},
"document": {
"media_path": "statics/media/1752404965-b9393cd1-8546-4df9-8a60-ee3276036aba.m4v",
"mime_type": "application/pdf",
"caption": "okk"
}
}{
"chat_id": "628968XXXXXXXX",
"from": "628968XXXXXXXX@s.whatsapp.net",
"message": {
"text": "",
"id": "446AC2BAF2061B53E24CA526DBDFBD4E",
"replied_id": "",
"quoted_message": ""
},
"pushname": "Aldino Kemal",
"sender_id": "628968XXXXXXXX",
"sticker": {
"media_path": "statics/media/1752404986-ff2464a6-c54c-4e6c-afde-c4c925ce3573.webp",
"mime_type": "image/webp",
"caption": ""
},
"timestamp": "2025-07-13T11:09:45Z"
}{
"chat_id": "6289XXXXXXXXX",
"contact": {
"displayName": "3Care",
"vcard": "BEGIN:VCARD\nVERSION:3.0\nN:;3Care;;;\nFN:3Care\nTEL;type=Mobile:+62 132\nEND:VCARD",
"contextInfo": {
"expiration": 7776000,
"ephemeralSettingTimestamp": 1751808692,
"disappearingMode": {
"initiator": 0,
"trigger": 1,
"initiatedByMe": true
}
}
},
"from": "6289XXXXXXXXX@s.whatsapp.net",
"message": {
"text": "",
"id": "56B3DFF4994284634E7AAFEEF6F1A0A2",
"replied_id": "",
"quoted_message": ""
},
"pushname": "Aldino Kemal",
"sender_id": "6289XXXXXXXXX",
"timestamp": "2025-07-13T11:10:19Z"
}{
"sender_id": "628123456789",
"chat_id": "628987654321",
"from": "John Doe",
"timestamp": "2023-10-15T11:15:00Z",
"pushname": "John Doe",
"message": {
"text": "",
"id": "3EB0C127D7BACC83D6A9",
"replied_id": "",
"quoted_message": ""
},
"location": {
"degreesLatitude": -6.2088,
"degreesLongitude": 106.8456,
"name": "Jakarta, Indonesia",
"address": "Central Jakarta, DKI Jakarta, Indonesia"
}
}{
"chat_id": "6289XXXXXXXXX",
"from": "6289XXXXXXXXX@s.whatsapp.net",
"location": {
"degreesLatitude": -7.8050297,
"degreesLongitude": 110.4549165,
"JPEGThumbnail": "base64_image_thumbnail",
"contextInfo": {
"expiration": 7776000,
"ephemeralSettingTimestamp": 1751808692,
"disappearingMode": {
"initiator": 0,
"trigger": 1,
"initiatedByMe": true
}
}
},
"message": {
"text": "",
"id": "94D13237B4D7F33EE4A63228BBD79EC0",
"replied_id": "",
"quoted_message": ""
},
"pushname": "Aldino Kemal",
"sender_id": "6289685XXXXXX",
"timestamp": "2025-07-13T11:11:22Z"
}{
"action": "message_revoked",
"chat_id": "6289XXXXXXXXX",
"from": "6289XXXXXXXXX@s.whatsapp.net",
"message": {
"text": "",
"id": "F4062F2BBCB19B7432195AD7080DA4E2",
"replied_id": "",
"quoted_message": ""
},
"pushname": "Aldino Kemal",
"revoked_chat": "6289XXXXXXXXX@s.whatsapp.net",
"revoked_from_me": true,
"revoked_message_id": "94D13237B4D7F33EE4A63228BBD79EC0",
"sender_id": "6289XXXXXXXXX",
"timestamp": "2025-07-13T11:13:30Z"
}When a message is edited, the webhook includes the original message ID to track which message was modified.
{
"action": "message_edited",
"chat_id": "6289XXXXXXXXX",
"original_message_id": "94D13237B4D7F33EE4A63228BBD79EC0",
"edited_text": "hhhiawww",
"from": "6289XXXXXXXXX@s.whatsapp.net",
"message": {
"text": "hhhiawww",
"id": "D6271D8223A05B4DA6AE9FE3CD632543",
"replied_id": "",
"quoted_message": ""
},
"pushname": "Aldino Kemal",
"sender_id": "6289XXXXXXXXX",
"timestamp": "2025-07-13T11:14:19Z"
}Fields:
original_message_id: The ID of the message that was edited (use this to update the correct message in your database)edited_text: The new text content after editingmessage.id: The ID of the edit event itself (different from the original message ID)
{
"sender_id": "628123456789",
"chat_id": "628987654321",
"from": "John Doe",
"timestamp": "2023-10-15T11:40:00Z",
"pushname": "John Doe",
"message": {
"text": "",
"id": "3EB0C127D7BACC83D6B2",
"replied_id": "",
"quoted_message": ""
},
"image": {
"media_path": "statics/media/1752405060-b9393cd1-8546-4df9-8a60-ee3276036aba.m4v",
"mime_type": "image/jpeg",
"caption": "okk"
},
"view_once": true
}{
"sender_id": "628123456789",
"chat_id": "628987654321",
"from": "John Doe",
"timestamp": "2023-10-15T11:45:00Z",
"pushname": "John Doe",
"message": {
"text": "This is a forwarded message",
"id": "3EB0C127D7BACC83D6B3",
"replied_id": "",
"quoted_message": ""
},
"forwarded": true
}-
Configure webhook URL(s):
./whatsapp rest --webhook="https://yourapp.com/webhook" -
Set webhook secret:
./whatsapp rest --webhook-secret="your-secret-key" -
Multiple webhooks:
./whatsapp rest --webhook="https://app1.com/webhook,https://app2.com/webhook"
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.raw({type: 'application/json'}));
app.post('/webhook', (req, res) => {
const signature = req.headers['x-hub-signature-256'];
const payload = req.body;
const secret = 'your-secret-key';
// Verify signature
if (!verifyWebhookSignature(payload, signature, secret)) {
return res.status(401).send('Unauthorized');
}
// Parse and process webhook data
const data = JSON.parse(payload);
console.log('Received webhook:', data);
// Handle different event types
if (data.event === 'message.ack') {
console.log(`Message ${data.payload.receipt_type}:`, {
chat_id: data.payload.chat_id,
message_ids: data.payload.ids,
description: data.payload.receipt_type_description
});
} else if (data.event === 'group.participants') {
console.log(`Group ${data.payload.type} event:`, {
chat_id: data.payload.chat_id,
type: data.payload.type,
affected_users: data.payload.jids
});
// Handle specific group actions
switch (data.payload.type) {
case 'join':
console.log(`${data.payload.jids.length} users joined group ${data.payload.chat_id}`);
// Auto-greet new members
data.payload.jids.forEach(jid => {
console.log(`Welcome ${jid} to the group!`);
});
break;
case 'leave':
console.log(`${data.payload.jids.length} users left group ${data.payload.chat_id}`);
// Update member database
break;
case 'promote':
console.log(`${data.payload.jids.length} users promoted in group ${data.payload.chat_id}`);
// Notify about new admins
break;
case 'demote':
console.log(`${data.payload.jids.length} users demoted in group ${data.payload.chat_id}`);
// Handle admin removal
break;
}
} else if (data.action === 'message_deleted_for_me') {
console.log('Message deleted:', data.deleted_message_id);
} else if (data.action === 'message_revoked') {
console.log('Message revoked:', data.revoked_message_id);
} else if (data.message) {
console.log('New message:', data.message.text);
}
res.status(200).send('OK');
});
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
const receivedSignature = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
app.listen(3001, () => {
console.log('Webhook server listening on port 3001');
});The webhook system includes retry logic with exponential backoff:
- Timeout: 10 seconds per request
- Max Attempts: 5 retries
- Backoff: Exponential (1s, 2s, 4s, 8s, 16s)
Ensure your webhook endpoint:
- Responds within 10 seconds
- Returns HTTP 2xx status for successful processing
- Handles duplicate events gracefully
- Validates signatures for security
# Single webhook URL
WHATSAPP_WEBHOOK=https://yourapp.com/webhook
# Multiple webhook URLs (comma-separated)
WHATSAPP_WEBHOOK=https://app1.com/webhook,https://app2.com/webhook
# Webhook secret for HMAC verification
WHATSAPP_WEBHOOK_SECRET=your-super-secret-key# Single webhook
./whatsapp rest --webhook="https://yourapp.com/webhook"
# Multiple webhooks
./whatsapp rest --webhook="https://app1.com/webhook,https://app2.com/webhook"
# Custom secret
./whatsapp rest --webhook-secret="your-secret-key"- Always verify signatures to ensure webhook authenticity
- Handle duplicates - the same event might be sent multiple times
- Process quickly - respond within 10 seconds to avoid timeouts
- Log errors for debugging webhook integration issues
- Use HTTPS for webhook URLs to ensure secure transmission
- Store media files locally if you need to process them later
- Implement proper error handling for different event types
-
Webhook not receiving events:
- Check webhook URL is accessible from the internet
- Verify webhook configuration
- Check firewall and network settings
-
Signature verification fails:
- Ensure webhook secret matches configuration
- Use raw request body for signature calculation
- Check HMAC implementation
-
Timeouts:
- Optimize webhook processing speed
- Implement asynchronous processing
- Return response quickly, process in background
-
Missing media files:
- Check media storage path configuration
- Ensure sufficient disk space
- Verify file permissions
Enable debug mode to see webhook logs:
./whatsapp rest --debug=true --webhook="https://yourapp.com/webhook"This will show detailed logs of webhook delivery attempts and errors.