Skip to main content
Webhooks enable your application to receive real-time notifications when WhatsApp events occur. GOWA sends HTTP POST requests to your configured webhook URL with event data in JSON format.

How webhooks work

Configuration

Single webhook

./whatsapp rest --webhook="https://your-webhook.site/handler"

Multiple webhooks

Send events to multiple endpoints (comma-separated):
--webhook="https://webhook1.com/handler,https://webhook2.com/handler"
Each webhook receives the same event payload.

Event filtering

Control which events are forwarded to your webhook:
--webhook-events="message,message.ack,group.participants"
Or environment variable:
WHATSAPP_WEBHOOK_EVENTS=message,message.ack,group.participants

Available events

EventDescription
messageText, media, contact, location messages
message.reactionEmoji reactions to messages
message.revokedDeleted/revoked messages
message.editedEdited messages
message.ackDelivery and read receipts
message.deletedMessages deleted for the user
group.participantsGroup member join/leave/promote/demote
group.joinedYou were added to a group
newsletter.joinedYou subscribed to a newsletter
newsletter.leftYou unsubscribed from a newsletter
newsletter.messageNew message in a newsletter
newsletter.muteNewsletter mute setting changed
call.offerIncoming call received
If webhook-events is not configured (empty), all events are forwarded.

HMAC signature verification

Every webhook request includes an HMAC-SHA256 signature for verification.

Configure secret

--webhook-secret="your-secret-key"
Default secret: secret

Verify signature

const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const signature = req.headers['x-webhook-signature'];
  const body = JSON.stringify(req.body);
  
  const hmac = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  
  return hmac === signature;
}

app.post('/webhook', (req, res) => {
  if (!verifyWebhook(req, 'your-secret-key')) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process event
  const { event, device_id, payload } = req.body;
  console.log(`Event: ${event} from ${device_id}`);
  
  res.sendStatus(200);
});
Always verify signatures in production to prevent spoofed webhook calls.

Webhook payload structure

All webhook payloads follow this structure:
{
  "event": "message",
  "device_id": "[email protected]",
  "payload": {
    // Event-specific data
  }
}

Message event

{
  "event": "message",
  "device_id": "[email protected]",
  "payload": {
    "id": "3EB0C0A6B4B8F8D7E9F1",
    "from": "[email protected]",
    "timestamp": 1704067200,
    "type": "conversation",
    "body": "Hello from customer",
    "from_me": false,
    "chat_jid": "[email protected]"
  }
}

Message reaction

{
  "event": "message.reaction",
  "device_id": "[email protected]",
  "payload": {
    "message_id": "3EB0C0A6B4B8F8D7E9F1",
    "from": "[email protected]",
    "reaction": "❤️",
    "timestamp": 1704067200
  }
}

Message acknowledgment

{
  "event": "message.ack",
  "device_id": "[email protected]",
  "payload": {
    "message_id": "3EB0C0A6B4B8F8D7E9F1",
    "status": "read",
    "timestamp": 1704067200
  }
}
Status values:
  • server - Delivered to server
  • delivery - Delivered to recipient device
  • read - Read by recipient

Group participants event

{
  "event": "group.participants",
  "device_id": "[email protected]",
  "payload": {
    "group_jid": "[email protected]",
    "action": "add",
    "participants": [
      "[email protected]",
      "[email protected]"
    ],
    "timestamp": 1704067200
  }
}
Action values:
  • add - Participants added
  • remove - Participants removed
  • promote - Promoted to admin
  • demote - Demoted from admin

Call offer event

{
  "event": "call.offer",
  "device_id": "[email protected]",
  "payload": {
    "from": "[email protected]",
    "call_id": "ABCDEF123456",
    "timestamp": 1704067200,
    "is_video": false
  }
}
For complete payload schemas for all event types, see the Webhook Payload Documentation.

Multi-device webhooks

With multiple devices, the device_id field identifies which account received the event:
app.post('/webhook', (req, res) => {
  const { event, device_id, payload } = req.body;
  
  // Route by device
  switch (device_id) {
    case '[email protected]':
      handleSalesEvent(event, payload);
      break;
    case '[email protected]':
      handleSupportEvent(event, payload);
      break;
  }
  
  res.sendStatus(200);
});

TLS certificate verification

By default, GOWA verifies TLS certificates when sending webhooks. For self-signed certificates or Cloudflare tunnels:
--webhook-insecure-skip-verify=true
Or environment variable:
WHATSAPP_WEBHOOK_INSECURE_SKIP_VERIFY=true
Security warning: Only disable TLS verification in development or when using trusted tunnels like Cloudflare. In production, use proper SSL certificates (e.g., Let’s Encrypt).

Retry behavior

GOWA retries failed webhook deliveries:
  • Timeout: 30 seconds per request
  • Retry attempts: 3 times
  • Backoff: Exponential (1s, 2s, 4s)
  • Failed delivery: Event is dropped after 3 attempts
Your webhook endpoint should respond with 200 OK within 30 seconds to avoid timeouts.

Best practices

Respond quickly

Return 200 OK immediately, process events asynchronously with queues

Verify signatures

Always validate HMAC signatures to prevent spoofing

Filter events

Only subscribe to events you need to reduce processing overhead

Handle duplicates

Store processed message IDs to handle potential duplicate deliveries

Troubleshooting

  1. Check webhook URL is publicly accessible
  2. Verify WHATSAPP_WEBHOOK environment variable
  3. Check GOWA logs for delivery errors
  4. Test webhook endpoint with curl
tls: failed to verify certificate: x509: certificate signed by unknown authority
Solution: Use --webhook-insecure-skip-verify=true for development, or add proper SSL certificate in production.
  1. Ensure webhook secret matches on both sides
  2. Verify you’re hashing the raw JSON body (not parsed object)
  3. Check HMAC algorithm is SHA256
  4. Compare raw signature header with computed value
  1. Check WHATSAPP_WEBHOOK_EVENTS filter configuration
  2. Verify device is connected (GET /devices/{id}/status)
  3. Check GOWA server logs for event processing errors

Next steps

Webhook integration guide

Complete webhook setup tutorial

Payload schemas

Full event payload documentation

Receiving messages

Handle incoming messages

Configuration

All webhook configuration options

Build docs developers (and LLMs) love