GhalaGhalaHelp Center
Back to ghala.ioghala.ioSign in
  • Getting Started
    • Ghala Documentation
    • Send Your First WhatsApp Message via API
    • Setting Up WhatsApp Webhooks
  • Commerce
    • Start Selling on WhatsApp
    • Set Up the AI Sales Agent
    • Get Paid with Snippe
  • Ai Automation
    • Configuring AI Auto-Reply for WhatsApp
    • Setting Up Human Handover Protocol
  • Campaigns Messaging
    • WhatsApp Message Templates: Complete Guide
    • Bulk WhatsApp Messaging: Complete Campaign Guide
  • Contacts Crm
    • WhatsApp Contact Management Guide
  • Best Practices
    • WhatsApp Customer Support Best Practices
    • Message Template Best Practices
  • Api Reference
    • Ghala API Reference

Products

  • Ghala
  • Sarufi
  • Snippe
  • Sema

Explore

  • Use Cases
  • Pricing
  • Blog

Developers

  • Docs
  • API Reference
  • API Quickstart
  • Webhooks Guide

Contact

  • SkyCity Mall, 9th Floor, Dar es Salaam, Tanzania
  • info@ghala.io
  • +255 699 920 009
© 2026 Neurotech Company LimitedTerms of ServicePrivacy PolicySitemap
  1. Help Center
  2. Getting Started
  3. Setting Up WhatsApp Webhooks

Setting Up WhatsApp Webhooks

Receive incoming messages, delivery updates, and read receipts on your own endpoint in real time.

How webhooks work on Ghala

By default, your number's events flow into Ghala; that's what powers the inbox, AI auto-reply, and analytics. You don't need to configure anything for that.

When you're building your own integration, Ghala can point your number's webhooks at your endpoint instead. Events then arrive at your URL in the standard WhatsApp Cloud API webhook format, directly from Meta. No polling, no translation layer to learn.

Trade-off to know: while your override is active, events for that number go to you instead of Ghala, so the Ghala inbox and AI auto-reply pause for it. Remove the override any time to hand the number back to Ghala.

Step 1: Build an endpoint that passes verification

Your endpoint must be public HTTPS and answer Meta's one-time verification handshake: a GET request with hub.mode, hub.verify_token, and hub.challenge query parameters. Check the token, echo the challenge.

A complete Express example:

import express from "express"

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

const VERIFY_TOKEN = process.env.VERIFY_TOKEN // you choose this value

// Meta calls this once when the webhook is configured
app.get("/webhook", (req, res) => {
  const mode = req.query["hub.mode"]
  const token = req.query["hub.verify_token"]
  const challenge = req.query["hub.challenge"]
  if (mode === "subscribe" && token === VERIFY_TOKEN) {
    return res.status(200).send(challenge)
  }
  res.sendStatus(403)
})

// Events arrive here
app.post("/webhook", (req, res) => {
  res.sendStatus(200) // acknowledge first, process after

  const value = req.body.entry?.[0]?.changes?.[0]?.value
  for (const message of value?.messages ?? []) {
    console.log("incoming:", message.from, message.text?.body)
  }
  for (const status of value?.statuses ?? []) {
    console.log("status:", status.id, status.status)
  }
})

app.listen(3000)

For local development, expose your server with a tunnel (ngrok, cloudflared) and use the tunnel URL.

Step 2: Point your number at it

In the dashboard, open Developer → Webhooks, enter your callback URL and the verify token you chose, and save. Ghala configures the override with Meta and runs the verification handshake immediately; if your endpoint echoes the challenge, you're live.

Step 3: Handle the events

Everything arrives as POST with this envelope. The two payloads you'll care about:

An incoming customer message

{
  "entry": [{
    "changes": [{
      "field": "messages",
      "value": {
        "contacts": [{ "profile": { "name": "Amina" }, "wa_id": "255712345678" }],
        "messages": [{
          "id": "wamid.HBgM...",
          "from": "255712345678",
          "timestamp": "1721300000",
          "type": "text",
          "text": { "body": "Do you deliver to Dodoma?" }
        }]
      }
    }]
  }]
}

Other type values (image, audio, document, location, interactive) carry a matching object instead of text.

A status update for a message you sent

{
  "entry": [{
    "changes": [{
      "field": "messages",
      "value": {
        "statuses": [{
          "id": "wamid.HBgM...",
          "status": "delivered",
          "timestamp": "1721300005",
          "recipient_id": "255712345678"
        }]
      }
    }]
  }]
}

status progresses sent → delivered → read, or lands on failed with an errors array explaining why.

Rules of thumb

  • Acknowledge fast. Return 200 within a few seconds and process asynchronously; slow responses are treated as failures.
  • Expect retries. Failed deliveries are retried with backoff, so the same event can arrive more than once. Deduplicate by message id.
  • Keep your verify token secret and use an unguessable value; it's what stops strangers from registering your endpoint. Deliveries also carry Meta's X-Hub-Signature-256 header.
  • Reply through the API. Answering an incoming message within its 24-hour window is a regular send-message call; that pair of webhook-in, API-out is a complete bot.
PreviousSend Your First WhatsApp Message via APINextStart Selling on WhatsApp

On this page

  • How webhooks work on Ghala
  • Step 1: Build an endpoint that passes verification
  • Step 2: Point your number at it
  • Step 3: Handle the events
  • Rules of thumb