AI for WhatsApp

WhatsApp webhook tutorial: receive messages on your own server

A webhook is a URL on your server that gets a call every time something happens on your WhatsApp number: a new message, a message read, a member joining a group, a lead collected. This tutorial shows the setup and the one security step you should not skip, checking the signature.

How the webhook works

You save one HTTPS URL on the Developer API screen and choose the events you want. Leave every box unticked to receive all events. Each call is a JSON POST with the headers X-Waha-Event, X-Waha-Session and X-Webhook-Signature.

If your server does not answer successfully, the call is retried up to 3 times. Answer quickly and do the heavy work afterwards.

Events you can listen for

  • Messages: new message sent or received, inbound only, delivered or read, and reactions.
  • AI automations: question asked, lead details collected, automation completed.
  • Account and groups: connection status, group joined, group left, group updated, group members changed.

Step by step

  • Create a small endpoint on your server that accepts a POST with a JSON body.
  • Save its public HTTPS URL on the Developer API screen and pick your events.
  • Copy the signing secret. It is shown once when you save the webhook.
  • Send a test call from the same screen. Your server should receive a webhook.test event.
  • Verify the signature on every call, then process the event.

Verify the signature

The signature is an HMAC SHA-256 of the raw request body, using your signing secret. Compare it with the X-Webhook-Signature header. Use the raw bytes, not a re-serialised copy of the JSON, or the comparison will fail.

Node (Express)
import crypto from "crypto";
import express from "express";

const app = express();
const SECRET = process.env.WEBHOOK_SECRET;

app.post("/hooks/whatsapp", express.raw({ type: "application/json" }), (req, res) => {
  const expected = crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
  const given = req.get("X-Webhook-Signature") || "";
  const ok = given.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
  if (!ok) return res.sendStatus(401);

  const { event, payload } = JSON.parse(req.body.toString());
  if (event === "message.any") saveMessage(payload);
  if (event === "automation.completed") saveLead(payload);
  res.sendStatus(200); // answer fast, work after
});
Python (Flask)
import hashlib, hmac, os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["WEBHOOK_SECRET"].encode()

@app.post("/hooks/whatsapp")
def hook():
    raw = request.get_data()
    expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(request.headers.get("X-Webhook-Signature", ""), expected):
        abort(401)
    data = request.get_json()
    if data["event"] == "message.any":
        save_message(data["payload"])
    return "", 200

Handling group messages

Group messages arrive on the same message events. The from field holds the group id, which ends in @g.us, so a one-line check separates group traffic from one-to-one chats. Store the group id, the sender, the time and the text, and you have the raw material for a team dashboard.

Common mistakes

  • Parsing the body before verifying the signature, which changes the bytes.
  • Doing slow work before answering, which triggers retries and duplicates.
  • Not handling duplicates. Retries can deliver an event more than once, so key your records on the message id.
  • Exposing the endpoint without checking the signature.

Frequently asked questions

Do I need a public URL?

Yes. The webhook calls your server over the internet, so it needs an HTTPS URL that is reachable. For local testing, use a tunnel tool.

Can I get only group messages?

You choose events, not groups, so receive the message events and filter on the group id in your own code.

What happens if my server is down?

Failed calls are retried up to 3 times. After that the event is not resent, so store messages as they arrive and use the group messages endpoint to fill any gap.

Keep reading

Put an AI agent on your WhatsApp

Scan a QR code and it is live in about a minute. 7 days free, no card needed.

Start free trial