Raw format of WhatsApp Business API webhooks — how to configure, validate, and process message and status payloads
The Meta WhatsApp Business API (WABA) sends events directly to an endpoint you register in the Facebook App. When you connect a WhatsApp number in Timely.ai, our platform receives those events, processes them, and transforms them into the workspace’s internal webhooks. This page documents the raw format that Meta sends — useful for debugging, direct integrations, or when you need to understand what arrives before any transformation.
Paste the URL of the endpoint that will receive the events. In production, always use HTTPS with a valid certificate.If you are testing locally, use ngrok or Hookdeck to expose your server:
Choose a random, secure string (e.g. my_secret_token_123). Meta makes a GET request to your endpoint at setup time with the parameters hub.mode, hub.verify_token, and hub.challenge. Your server must respond with the value of hub.challenge if the token matches.
Difference between Meta webhook and Timely webhook
Aspect
Meta webhook (raw)
Timely webhook (platform)
Source
Meta → your server
Timely → your server
Authentication
X-Hub-Signature-256 with App Secret
X-Timely-Signature with your secret
Format
Meta envelope with entry[].changes[]
Normalized payload by event type
Contact resolved
No — only the wa_id
Yes — includes Timely contact_id
Transformation
None — raw Meta data
Enriched with workspace context
If you use Timely as an intermediary (the default case), you do not need to register a webhook with Meta — Timely handles that for you. This documentation is for cases where you connect WABA directly or need to debug the full flow.
Meta signs each request with the App Secret of your Facebook App using HMAC-SHA256. The header sent is X-Hub-Signature-256.
Node.js
import crypto from "crypto";const APP_SECRET = process.env.META_APP_SECRET!;function verifyMetaSignature(rawBody: Buffer, signature: string): boolean { const expected = "sha256=" + crypto .createHmac("sha256", APP_SECRET) .update(rawBody) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) );}app.post("/webhook/whatsapp", (req, res) => { const sig = req.headers["x-hub-signature-256"] as string; if (!verifyMetaSignature(req.rawBody, sig)) { return res.sendStatus(401); } // process the event... res.sendStatus(200);});
Never process a Meta webhook without validating X-Hub-Signature-256. The App Secret is in your Facebook App dashboard — never expose it in source code or logs.
To download the media, make a GET https://graph.facebook.com/v19.0/{MEDIA_ID} request with your access token. Timely.ai performs this download automatically and stores the public URL in the media_url field of the normalized message.