Skip to content

Set Up Webhooks

Webhooks let your application receive WhatsApp events from a ChatBreez instance in real time. Instead of polling the API for new messages, ChatBreez sends an HTTP POST request to your server whenever a matching event occurs.

This guide covers how to register a webhook from the dashboard and how to build an endpoint that listens for and processes those events.

How It Works

  1. You add a webhook URL on the instance detail page in the ChatBreez dashboard.
  2. A WhatsApp user sends a message to your connected instance.
  3. ChatBreez sends a POST request to your webhook URL with a JSON payload describing the event.
  4. Your application reads the payload, handles the event, and responds with 200 OK.
WhatsApp user → ChatBreez instance → POST https://your-app.com/webhooks/chatbreez → Your application

Your webhook URL must be publicly reachable over HTTPS. ChatBreez cannot deliver events to localhost unless you expose it with a tunnel (for example, ngrok or Cloudflare Tunnel).

Set Up From The Dashboard

Configure webhooks only from the dashboard. There is no public API for registering webhook URLs.

  1. Sign in to chatbreez.com.
  2. Open the dashboard.
  3. Go to your WhatsApp instances.
  4. Select the instance you want to configure.
  5. On the instance detail page, enter your webhook URL.
  6. Save the webhook.

After saving, ChatBreez starts sending incoming message events to that URL for the selected instance.

Listen To Webhook Events

To integrate ChatBreez with your application, create an HTTP endpoint that accepts POST requests, reads the JSON body, and acts on the event.

1. Create a webhook endpoint

Add a route in your application that listens for POST requests. Choose any path you control, as long as it matches the URL you saved in the dashboard.

Example endpoint:

http
POST https://example.com/webhooks/chatbreez

2. Read the request body

Each webhook request has a JSON body with three top-level fields:

FieldTypeDescription
eventstringThe event type (for example, messages.upsert).
instancestringThe ChatBreez instance name that received the message.
dataobjectEvent-specific payload. For message events, this contains the message details.

3. Handle the event

Use the event field to decide what your application should do. ChatBreez currently sends incoming message events for configured instances.

Supported event:

EventDescription
messages.upsertA new message was received or updated on the instance.

Inspect data for the message content, sender, and message ID. For text messages, the body is usually in data.message.conversation. Other message types (images, documents, audio) use different fields inside data.message.

Example payload:

json
{
  "event": "messages.upsert",
  "instance": "my-instance",
  "data": {
    "key": {
      "remoteJid": "[email protected]",
      "fromMe": false,
      "id": "MESSAGE_ID"
    },
    "message": {
      "conversation": "Hello"
    }
  }
}

Field reference for messages.upsert:

FieldDescription
data.key.remoteJidSender WhatsApp ID. The phone number is the part before @s.whatsapp.net.
data.key.fromMetrue if the message was sent by your instance; false if it was received from a contact.
data.key.idUnique message ID. Use this for idempotency and deduplication.
data.message.conversationPlain-text message body, when the message is a text message.

4. Respond quickly

Return a 2xx response as soon as your server accepts the event. ChatBreez treats non-success responses as delivery failures.

Do not run slow work (database writes, third-party API calls, AI processing) before responding. Acknowledge the webhook first, then process the event in the background.

json
{ "success": true }

A 200 OK with any small JSON body is enough.

Integration Examples

These examples show a complete flow: receive the webhook, read the event, handle a new message, and return 200 OK.

javascript
const express = require('express');

const app = express();
app.use(express.json());

app.post('/webhooks/chatbreez', (req, res) => {
  const { event, instance, data } = req.body;

  if (event === 'messages.upsert' || event === 'MESSAGES_UPSERT') {
    const sender = data?.key?.remoteJid?.split('@')[0];
    const text = data?.message?.conversation;
    const messageId = data?.key?.id;

    // Skip messages sent by your own instance
    if (!data?.key?.fromMe && text) {
      console.log(`New message on ${instance} from ${sender}: ${text}`);

      // Queue background work here instead of awaiting it in the request
      processMessage({ instance, sender, text, messageId });
    }
  }

  res.status(200).json({ success: true });
});

function processMessage({ instance, sender, text, messageId }) {
  // Save to database, trigger automation, reply via ChatBreez API, etc.
}

app.listen(3000, () => {
  console.log('Listening for ChatBreez webhooks on port 3000');
});
php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;

Route::post('/webhooks/chatbreez', function (Request $request) {
    $event = $request->input('event');
    $instance = $request->input('instance');
    $data = $request->input('data', []);

    if (in_array($event, ['messages.upsert', 'MESSAGES_UPSERT'], true)) {
        $fromMe = $data['key']['fromMe'] ?? false;
        $text = $data['message']['conversation'] ?? null;
        $sender = isset($data['key']['remoteJid'])
            ? explode('@', $data['key']['remoteJid'])[0]
            : null;

        if (!$fromMe && $text) {
            Log::info('New WhatsApp message', [
                'instance' => $instance,
                'sender' => $sender,
                'text' => $text,
                'message_id' => $data['key']['id'] ?? null,
            ]);

            // Dispatch a job for slower processing
            // ProcessIncomingMessage::dispatch($instance, $sender, $text);
        }
    }

    return response()->json(['success' => true]);
});
python
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post("/webhooks/chatbreez")
def chatbreez_webhook():
    payload = request.get_json(silent=True) or {}
    event = payload.get("event")
    instance = payload.get("instance")
    data = payload.get("data") or {}

    if event in ("messages.upsert", "MESSAGES_UPSERT"):
        key = data.get("key") or {}
        message = data.get("message") or {}

        if not key.get("fromMe") and message.get("conversation"):
            sender = (key.get("remoteJid") or "").split("@")[0]
            text = message["conversation"]
            message_id = key.get("id")

            print(f"New message on {instance} from {sender}: {text}")
            # enqueue_background_job(instance, sender, text, message_id)

    return jsonify({"success": True}), 200

if __name__ == "__main__":
    app.run(port=3000)

Test Locally

During development, your app usually runs on localhost, which ChatBreez cannot reach directly. Use a tunnel to expose your local server:

  1. Start your webhook receiver locally (for example, on port 3000).
  2. Start a tunnel:
bash
ngrok http 3000
  1. Copy the generated HTTPS URL (for example, https://abc123.ngrok-free.app).
  2. In the ChatBreez dashboard, set your webhook URL to:
http
POST https://abc123.ngrok-free.app/webhooks/chatbreez
  1. Send a WhatsApp message to your connected instance and confirm your server logs the event.

Best Practices

  • Respond fast. Return 200 OK within a few seconds. Move heavy processing to a queue or background job.
  • Make handlers idempotent. The same event may be delivered more than once. Store data.key.id before processing and skip duplicates.
  • Ignore your own messages. Check data.key.fromMe so you do not react to messages sent by your instance.
  • Use HTTPS in production. Webhook URLs must use https://.
  • Keep logs. Log event, instance, and data.key.id to debug missed or duplicate deliveries.

Troubleshooting

IssueWhat to check
Webhook does not receive eventsConfirm the instance is connected and the webhook URL is saved on the instance detail page.
Request fails when saving the webhookMake sure the URL is provided and starts with https://.
Events arrive more than onceMake your handler idempotent by storing the message ID before processing.
Webhook times outReturn 200 OK quickly, then process long-running tasks in the background.
Local testing does not workUse a tunnel such as ngrok and paste the public HTTPS URL into the dashboard.