Skip to content

Latest commit

 

History

History
775 lines (648 loc) · 20.1 KB

File metadata and controls

775 lines (648 loc) · 20.1 KB

Webhook Payload Documentation

This document provides comprehensive documentation for the webhook payload structure used by the Go WhatsApp Web Multidevice application.

Overview

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.

Security

HMAC Signature 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-secret or WHATSAPP_WEBHOOK_SECRET)

Verification Example (Node.js)

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')
    );
}

Verification Example (Python)

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)

Common Payload Fields

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

Message Events

Text Message

{
  "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": ""
  }
}

Reply 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?"
  }
}

Reaction Message

{
  "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

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.

Message Delivered

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

Message Read

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

Receipt Event Fields

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

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.

Group Member Join

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

Group Member Leave

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

Group Member Promotion

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

Group Member Demotion

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

Group Event Fields

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

Media Messages

Image Message

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

Video Message

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

Audio Message

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

Document Message

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

Sticker Message

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

Special Message Types

Contact Message

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

Location Message

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

Live Location Message

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

Protocol Messages

Message Revoked

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

Message Edited

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 editing
  • message.id: The ID of the edit event itself (different from the original message ID)

Special Flags

View Once Message

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

Forwarded Message

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

Integration Guide

Setting Up Webhook Endpoint

  1. Configure webhook URL(s):

    ./whatsapp rest --webhook="https://yourapp.com/webhook"
  2. Set webhook secret:

    ./whatsapp rest --webhook-secret="your-secret-key"
  3. Multiple webhooks:

    ./whatsapp rest --webhook="https://app1.com/webhook,https://app2.com/webhook"

Webhook Endpoint Implementation (Express.js)

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');
});

Error Handling

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

Configuration

Environment Variables

# 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

Command Line Flags

# 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"

Best Practices

  1. Always verify signatures to ensure webhook authenticity
  2. Handle duplicates - the same event might be sent multiple times
  3. Process quickly - respond within 10 seconds to avoid timeouts
  4. Log errors for debugging webhook integration issues
  5. Use HTTPS for webhook URLs to ensure secure transmission
  6. Store media files locally if you need to process them later
  7. Implement proper error handling for different event types

Troubleshooting

Common Issues

  1. Webhook not receiving events:

    • Check webhook URL is accessible from the internet
    • Verify webhook configuration
    • Check firewall and network settings
  2. Signature verification fails:

    • Ensure webhook secret matches configuration
    • Use raw request body for signature calculation
    • Check HMAC implementation
  3. Timeouts:

    • Optimize webhook processing speed
    • Implement asynchronous processing
    • Return response quickly, process in background
  4. Missing media files:

    • Check media storage path configuration
    • Ensure sufficient disk space
    • Verify file permissions

Debug Logging

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.