Building Reliable WhatsApp Webhooks That Never Drop a Message
Practical patterns for handling WhatsApp Cloud API webhooks reliably: fast acknowledgment, idempotency, retries, and ordering for production systems.
The WhatsApp Cloud API does not ask whether your server is ready — it just fires. Every inbound message, delivery receipt, and status change arrives as an HTTP POST to your webhook endpoint. Handle it well and your bot feels instant. Handle it poorly and you silently lose customer messages while everything looks fine. Webhook reliability is the unglamorous foundation that decides whether your automation can be trusted.
Acknowledge Fast, Process Later
The single most common mistake is doing real work inside the webhook handler. Meta expects a 200 OK quickly. If your endpoint blocks while it calls a database, runs an LLM, or hits a third-party API, you risk a timeout — and Meta will retry the same event, sometimes repeatedly.
The fix is a strict split:
- Receive: validate the signature, write the raw payload to a queue or durable table, return
200immediately. - Process: a separate worker picks up the event and does the slow work.
This keeps your acknowledgment path under a few milliseconds and decouples your business logic from Meta's timing expectations.
Assume Every Event Arrives Twice
Because retries exist, duplicates are not an edge case — they are normal traffic. If you send a confirmation reply on every webhook hit, a single inbound message can trigger three replies.
Every WhatsApp message carries a unique id. Use it as an idempotency key:
Before processing an event, record its message ID. If you have seen it, drop it. If not, process and store it atomically.
A unique constraint in your database does this for free — the second insert fails, and you simply skip it. This one habit eliminates the majority of "why did the bot reply twice?" bugs.
Respect Ordering Without Trusting It
Webhooks can arrive out of order. A "delivered" status might land before the "sent" status it logically follows. Don't build state machines that assume perfect sequencing. Instead:
- Key your state by conversation and message ID, not by arrival order.
- Treat status updates as upserts toward a final state, ignoring backward transitions.
- Use the timestamps Meta includes in the payload, not your own receive time, when sequence matters.
Verify Signatures, Always
Your webhook URL is public, which means anyone can POST to it. Meta signs every request with an HMAC SHA-256 hash in the X-Hub-Signature-256 header, computed from your app secret. Compute the same hash over the raw request body and compare. Reject anything that doesn't match.
A subtle trap: you must hash the exact raw bytes Meta sent. If your framework parses and re-serializes the JSON before you hash it, the signature will never match. Capture the raw body first.
Make Failure Visible
Silent failure is the enemy. A dropped webhook leaves no error on the customer's screen — they just never get a reply. Build in observability:
- Log every received event ID and its processing outcome.
- Alert on queue backlog growth, not just on crashes.
- Keep a dead-letter queue for events that fail repeatedly so you can replay them after a fix.
The Takeaway
Reliable WhatsApp automation is less about clever bot logic and more about disciplined plumbing. Acknowledge instantly, dedupe by message ID, treat ordering as advisory, verify every signature, and make failures loud. Get these five right, and the rest of your integration has a foundation it can actually stand on.
Build with Abati Technology
We build software that ships — WhatsApp API, developer tools, POS, and mobile apps. Let's talk about your project.
Get in Touch →