TwaBot Webhooks

Receive real-time WhatsApp events in your CRM, app, or backend system.

Plan Requirement: Webhooks are available on the Growth and Pro plans.

TwaBot webhooks push real-time event data to your server whenever something happens — a customer sends a message, your agent replies, a broadcast completes, etc. This lets your CRM stay in sync with WhatsApp conversations without polling.

How It Works

When an event occurs (e.g., a customer sends you a WhatsApp message), TwaBot sends an HTTP POST request to the URL you configured, with a JSON payload containing the event details.

Your CRM Server                    TwaBot                     WhatsApp
      |                               |                           |
      |                               |  <-- Customer Message --  |
      |  <-- POST (webhook event) --  |                           |
      |                               |                           |
      |  -- 200 OK -->                |                           |

Your server should respond with a 200 status code within 10 seconds. TwaBot does not wait for or process your response body — only the status code matters.

Setup Guide

1
Create a webhook endpoint on your server

Build an HTTP endpoint (e.g., https://yourcrm.com/webhooks/twabot) that accepts POST requests with JSON body.

2
Go to Webhooks in your TwaBot dashboard

Log in → Sidebar → Webhooks

3
Click "Add Webhook"

Fill in: Name (e.g., "My CRM"), your server URL, select which events to receive, and optionally set a signing secret.

4
Start receiving events

TwaBot will POST event data to your URL in real-time whenever a subscribed event occurs.

Testing Webhooks

Before using your production server, you can test webhook delivery using free tools:

Option 1: webhook.site (Recommended)

1. Go to https://webhook.site
2. Copy the unique URL it gives you
3. Paste that URL as your webhook endpoint in TwaBot
4. Send yourself a WhatsApp message
5. See the real-time payload on webhook.site

Option 2: ngrok (For local development)

If your CRM runs locally, use ngrok to expose it:

# Start your local server on port 3000
ngrok http 3000

# Use the ngrok URL as your webhook endpoint
# e.g., https://abc123.ngrok.io/webhooks/twabot

Option 3: Postman (Mock server)

Create a mock server in Postman and use its URL as your webhook endpoint to inspect incoming payloads.

Webhook Events

TwaBot supports 5 event types. You can subscribe to one or more events per webhook.

EventTriggered When
message.receivedA customer sends you a WhatsApp message
message.sentAn agent or system sends a reply to a customer
conversation.createdA new customer starts a conversation (first message)
broadcast.completedA broadcast campaign finishes sending all messages
ai.repliedThe AI auto-reply sends a response to a customer

message.received

Triggered when a customer sends a WhatsApp message to your business number.

Payload Example
{
  "event": "message.received",
  "timestamp": "2026-04-02T10:30:45.000Z",
  "data": {
    "message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
    "from": "919876543210",
    "from_name": "Rahul Kumar",
    "type": "text",
    "text": "Hi, I want to check my order status",
    "conversation_id": 1234,
    "business_id": 56
  }
}

For media messages, the data object will include media_type, media_id, and mime_type instead of text.

message.sent

Triggered when an agent, system, or API sends a message to a customer.

Payload Example
{
  "event": "message.sent",
  "timestamp": "2026-04-02T10:31:12.000Z",
  "data": {
    "message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
    "to": "919876543210",
    "type": "text",
    "text": "Hi Rahul! Your order ORD-12345 is out for delivery.",
    "sent_by": "agent",
    "conversation_id": 1234,
    "business_id": 56
  }
}

conversation.created

Triggered when a new customer messages you for the first time (no existing conversation).

Payload Example
{
  "event": "conversation.created",
  "timestamp": "2026-04-02T10:30:45.000Z",
  "data": {
    "conversation_id": 1234,
    "customer_phone": "919876543210",
    "customer_name": "Rahul Kumar",
    "first_message": "Hi, I saw your ad on Instagram",
    "business_id": 56
  }
}
CRM Tip: Use this event to automatically create a new lead/contact in your CRM whenever a new customer reaches out.

broadcast.completed

Triggered when a broadcast campaign finishes sending all messages.

Payload Example
{
  "event": "broadcast.completed",
  "timestamp": "2026-04-02T11:45:00.000Z",
  "data": {
    "broadcast_id": 89,
    "template_name": "diwali_offer",
    "total_recipients": 5000,
    "sent": 4850,
    "failed": 150,
    "business_id": 56
  }
}

ai.replied

Triggered when the AI auto-reply feature sends a response to a customer.

Payload Example
{
  "event": "ai.replied",
  "timestamp": "2026-04-02T10:30:48.000Z",
  "data": {
    "message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
    "to": "919876543210",
    "ai_response": "Your order ORD-12345 is currently out for delivery and should arrive by 5 PM today.",
    "conversation_id": 1234,
    "business_id": 56
  }
}

Signature Verification

If you set a secret when creating your webhook, TwaBot signs every payload using HMAC-SHA256. This lets you verify that the request is genuinely from TwaBot and hasn't been tampered with.

The signature is sent in the X-TwaBot-Signature header.

How to verify (Node.js example)

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  return signature === expected;
}

// In your Express route handler:
app.post('/webhooks/twabot', (req, res) => {
  const signature = req.headers['x-twabot-signature'];
  const isValid = verifySignature(req.body, signature, 'your_webhook_secret');

  if (!isValid) {
    console.error('Invalid webhook signature!');
    return res.status(401).send('Unauthorized');
  }

  // Process the event
  const { event, data } = req.body;
  console.log('Received event:', event, data);

  res.status(200).send('OK');
});

How to verify (Python example)

import hmac
import hashlib
import json

def verify_signature(payload, signature, secret):
    expected = hmac.new(
        secret.encode('utf-8'),
        json.dumps(payload).encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

# In your Flask/FastAPI handler:
@app.route('/webhooks/twabot', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-TwaBot-Signature', '')
    if not verify_signature(request.json, signature, 'your_webhook_secret'):
        return 'Unauthorized', 401

    event = request.json['event']
    data = request.json['data']
    print(f'Received event: {event}', data)

    return 'OK', 200

How to verify (PHP example)

<?php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_TWABOT_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret';

$expected = hash_hmac('sha256', $payload, $secret);

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    echo 'Unauthorized';
    exit;
}

$data = json_decode($payload, true);
$event = $data['event'];

// Process the event
error_log("Received event: " . $event);

http_response_code(200);
echo 'OK';
?>
Important: The signature is computed on the full JSON payload string. Make sure you're comparing against the raw JSON body, not a re-serialized version.

Retries & Failures

TwaBot tracks delivery failures for each webhook:

BehaviorDetails
Timeout10 seconds — your server must respond within this window
Failure trackingEach failed delivery increments fail_count
Success resetA successful delivery resets fail_count to 0
Delivery modeAsync (non-blocking) — TwaBot does not retry failed deliveries automatically
No automatic retries: If your server is down, the event is lost. Ensure your webhook endpoint is reliable and returns 200 quickly. You can monitor fail_count in your TwaBot dashboard.

POST /api/client/webhooks

Create a new webhook. Requires login session (dashboard authentication).

Request Body

ParameterTypeRequiredDescription
namestringRequiredWebhook name (e.g., "My CRM")
urlstringRequiredYour server URL (must be HTTPS in production)
eventsarrayRequiredArray of event names to subscribe to
secretstringOptionalSigning secret for HMAC-SHA256 verification

Example Request

curl -X POST https://twabot.com/api/client/webhooks \
  -H "Content-Type: application/json" \
  -H "Cookie: token=your_session_token" \
  -d '{
    "name": "My CRM Webhook",
    "url": "https://yourcrm.com/webhooks/twabot",
    "events": ["message.received", "conversation.created"],
    "secret": "my_secret_key_123"
  }'
Success Response (201)
{
  "id": 7,
  "message": "Webhook created."
}

GET /api/client/webhooks

List all webhooks for your business.

Success Response (200)
{
  "webhooks": [
    {
      "id": 7,
      "name": "My CRM Webhook",
      "url": "https://yourcrm.com/webhooks/twabot",
      "events": ["message.received", "conversation.created"],
      "is_active": 1,
      "last_triggered_at": "2026-04-02T10:30:45.000Z",
      "fail_count": 0,
      "created_at": "2026-04-01T09:00:00.000Z"
    }
  ]
}

PUT /api/client/webhooks/:id

Update an existing webhook. All fields are optional — only the fields you include will be updated.

Request Body (all optional)

ParameterTypeDescription
namestringNew webhook name
urlstringNew server URL
eventsarrayNew event subscriptions
secretstringNew signing secret (or null to remove)
is_activebooleantrue to enable, false to disable
Success Response (200)
{
  "message": "Webhook updated."
}

DELETE /api/client/webhooks/:id

Permanently delete a webhook.

Success Response (200)
{
  "message": "Webhook deleted."
}

Best Practices

1. Respond quickly

Your endpoint should return 200 OK as fast as possible (ideally under 2 seconds). If you need to do heavy processing, acknowledge the webhook first, then process asynchronously in the background.

2. Use a signing secret

Always set a signing secret and verify the X-TwaBot-Signature header. This prevents unauthorized parties from sending fake events to your endpoint.

3. Handle duplicates

In rare cases, an event might be delivered more than once. Use the message_id or conversation_id to deduplicate on your side.

4. Use HTTPS

Always use HTTPS for your webhook URL to ensure event data is encrypted in transit.

5. Monitor fail_count

Check your webhook's fail_count in the TwaBot dashboard regularly. A high count means your server is having issues receiving events.

6. Log incoming events

Log all incoming webhook events on your side for debugging. This helps when troubleshooting missing or incorrect data.

Need help? Contact us at support@twabot.com

Also see: REST API Documentation →