avatar

Elena Rodriguez

Updated: 2026-07-24

The send API returned 200. The customer replied. Nothing appeared in the CRM. After an hour of checking templates and phone numbers, the team found the real problem: its WhatsApp webhook receiver understood Meta's payload, while the provider was posting a different callback schema.

A webhook is the asynchronous half of a WhatsApp integration. The send request tells you whether the platform accepted a request. The webhook later tells you whether the message was sent, delivered, read, failed, or answered. The code is usually small. The contract around it is where production systems break.

Quick answer

A WhatsApp webhook is a public HTTPS endpoint that receives event data from Meta or a WhatsApp Business Solution Provider. Build the endpoint for the exact provider schema, acknowledge valid requests quickly, store the raw event before downstream work, process asynchronously, and make every side effect idempotent.

What Is a WhatsApp Webhook?

A WhatsApp webhook is an HTTP callback for events that happen after, or independently of, an API request. Typical events include outbound message status, inbound customer messages, replies to a specific message, template changes, phone-number changes, and account notifications.

It helps to separate three facts that often get collapsed into one:

  • Accepted: the sending API accepted your request and returned an ID.
  • Delivered: the channel later reported that the message reached the recipient.
  • Converted: your own system recorded the intended business action.

A delivery webhook cannot prove a purchase, completed onboarding, or solved support case. Join the message ID to your own order, campaign, conversation, or user record before changing business state.

According to the EngageLab Message Callback API documentation (2026) , its WhatsApp callback uses HTTP POST with a JSON body and may return multiple records in one rows array. That detail changes the receiver: code that processes only the first record will quietly drop events.

WhatsApp Message Callback API documentation for POST JSON callbacks

Choose the WhatsApp Webhook Contract Before You Code

“WhatsApp webhook” describes the job, not one universal payload. If you connect directly to Meta Cloud API, use Meta's verification, envelope, field subscriptions, and signature rules. If you use a Business Solution Provider, use that provider's callback URL, payload, acknowledgement, authentication, and retry rules.

Decision point Meta Cloud API BSP callback example
Configuration owner Meta app and WABA subscription Provider console and account
Envelope object → entry → changes → value Provider-defined, such as total → rows[]
Verification GET challenge with a verify token Provider-defined; may not use a GET challenge
Request authenticity Validate Meta's documented signature Use the provider's documented mechanism only
Status names Meta-defined message statuses May include provider-side planning and validation states
Support path Meta developer tooling Provider logs, console, and support

Meta's official WhatsApp Business Platform Postman collection shows the whatsapp_business_account envelope and requires subscribing an app to the WABA. The provider schema in this guide uses a batched rows array instead. Both carry WhatsApp events. Their parsers are not interchangeable.

The most expensive copy-paste bug

A handler can return 200 while reading the wrong path. Retry behavior is contract-specific, and the provider callback reference in this guide currently documents no retry mechanism, so the event is simply gone while your logs look healthy. Save a real payload fixture from the provider before writing business logic.

Set Up a WhatsApp Webhook in 7 Steps

The walkthrough below uses the EngageLab WhatsApp console, because it is the callback contract this guide's payload examples follow; the sequence is the same on any provider that exposes a callback URL setting. Each step names the screen, the action, and the signal that tells you it worked.

Step 1. Create one HTTPS receiver per provider and environment

Deploy an empty endpoint that accepts POST and returns 200 before you touch any console setting. Use separate production and test URLs, and a path such as /webhooks/whatsapp/provider so ownership stays visible in routing tables and logs. Verify with a manual POST from outside your network: if a plain HTTPS request cannot reach the endpoint, the provider's traffic will not either. A self-signed or expired TLS certificate is the most common silent blocker at this step.

Step 2. Register the callback URL in the console

In the WhatsApp console, open Configuration Management , then Callback Settings . Paste the receiver URL, enable the event groups you plan to consume (message status, message response, and notification), and save. The expected result is the URL and event toggles persisting after a page refresh; if the save silently reverts, check that the URL is HTTPS and publicly resolvable, because consoles typically validate reachability at save time.

WhatsApp console callback settings for message status and response events

Step 3. Send a controlled test and watch both ends

Send one approved template to a number you control, and attach your own order or user ID in custom_args at send time. Then watch two places at once: the console History view should show the message moving through sent and delivered, and your receiver log should show one POST per status change carrying the same message ID. If History updates but no POST arrives, the callback URL is wrong for this environment or was never saved; if the POST arrives without your custom ID, the correlation field was not set at send time.

WhatsApp console message history showing delivery rate, read rate, status, and cost per send

Step 4. Capture the raw POST body

From the test in Step 3, save a redacted copy of each event shape you will handle: a status callback, an inbound customer reply, and a notification. These fixtures become your parser tests and your replay data. Do not build from a documentation screenshot alone; the fixture you captured is the contract your account actually receives today.

Step 5. Return the required acknowledgement

The current callback reference requires an HTTP 200 response within three seconds. Measure your acknowledgement latency from the receiver's own logs, not from downstream dashboards, and treat anything close to the limit as a failure: a database write that takes two seconds today will exceed the window under load.

Step 6. Move processing off the request path

Persist or enqueue the raw event first, then acknowledge, then let an asynchronous worker call the CRM, bot, warehouse, or notification service. The success signal is that a deliberately slowed downstream system does not change your acknowledgement latency at all.

Step 7. Rehearse failure before going live

Replay a stored fixture twice and confirm exactly one ticket, reply, or CRM update results. Post malformed JSON and confirm a clean rejection without a crash. Restart the worker mid-queue and confirm no event is lost or double-processed. A receiver that passes these three rehearsals is ready for production traffic; one that only passed a dashboard test is not.

If you have not completed the outbound side yet, use the separate WhatsApp API send-message guide . Keep this page focused on what happens after the API accepts a request.

Map WhatsApp Webhook Events to Business Actions

A useful event model has three families. Route them separately even if the provider delivers them to one endpoint.

Family Examples Safe system action Do not assume
Status sent , delivered , read , failure, timeout Append transport state, update delivery analytics, trigger controlled fallback Read means purchase or intent
Response New message, reply, media, button, interactive response Create or update a conversation, route to bot/agent, store message context Every message belongs to an open workflow
Notification Balance, template, phone-number, or WABA account changes Alert operations and pause only the affected automation Every notification should stop all traffic

Status events need a monotonic state policy

Network delivery can be delayed or duplicated. Do not overwrite a later state with an earlier event just because it arrived later. Store an event history, define valid transitions, and keep failure context. Provider-specific states such as target validation, planning, and delivery timeout are useful for funnel analysis, but they should not leak directly into customer-facing copy.

On the provider callback used in this guide, those provider-side states are fully enumerated. The Message Callback reference defines nine per-recipient statuses, which map cleanly onto a delivery funnel:

Status Meaning Funnel use
plan Send scheduled for one recipient; plan_user_total carries the campaign size Campaign denominator
target_valid Number passed provider-side and Meta-side validation List quality rate
sent Message accepted by the Meta WhatsApp service Submission rate
delivered Meta confirmed delivery to the device Delivery rate
read Read receipt confirmed Engagement signal
target_invalid Number rejected by provider-side or Meta-side validation Stage-1 loss
sent_failed Meta returned an error at submission Stage-2 loss
delivered_failed Submitted, but Meta reported delivery failure Stage-3 loss
delivered_timeout No Meta confirmation within 5 minutes Ambiguity bucket, tracked separately from failure
EngageLab WhatsApp callback message status values including plan, target validation, and sent states

Failure statuses also carry attribution. loss_step marks the stage (1 invalid target, 2 send failure, 3 delivery failure) and loss_source names the side that dropped it, provider or Meta, so a falling delivery rate is diagnosable per stage instead of landing in one failure bucket.

Two payload fields are easy to miss and worth wiring on day one. custom_args returns whatever you attached at send time in every status callback, so campaign and user IDs correlate without a lookup table. The conversation and pricing objects carry Meta's conversation origin and billing category, which lets the same events double as spend attribution.

Inbound messages need conversation context

An inbound text is not just a string. Keep the channel message ID, sender, receiving number, message type, timestamp, referenced message, and business account. That context lets a support agent see what the customer replied to and prevents a bot from answering an old campaign as if it were a new support question.

Notifications belong to operations

A template status change, a lower messaging limit, or an account warning can affect many journeys at once. Route these events to an operations queue with an owner and runbook. The worst response is a generic alert that everyone sees and no one owns.

Build a Provider-Specific Node.js Receiver

The example below is intentionally small. It validates the batch shape, stores each raw row with a deduplication key, acknowledges the callback, and leaves business work to an asynchronous worker.

app.post('/webhooks/whatsapp/provider', async (req, res) => {
  const batch = req.body;

  if (!batch || !Array.isArray(batch.rows)) {
    return res.status(400).end();
  }

  for (const row of batch.rows) {
    const eventKind = row.status ? 'status'
      : row.response ? 'response'
      : row.notification ? 'notification'
      : 'unknown';

    const eventKey = [
      row.message_id || 'no-message-id',
      eventKind,
      row.itime || 'no-time',
      row.status?.message_status || row.response?.event ||
        row.notification?.event || 'unknown'
    ].join(':');

    await eventStore.insertIfAbsent({
      eventKey,
      provider: 'whatsapp-provider',
      receivedAt: new Date(),
      payload: row
    });
  }

  res.status(200).end();
});

This is a receiver pattern, not a drop-in application. Add a request-size limit, schema validation, log redaction, retention controls, database timeouts, and the provider's current authenticity mechanism. If database persistence cannot reliably finish inside the acknowledgement window, write to a durable queue or log first.

Use a Receive–Persist–Acknowledge Architecture

1. Receive

Limit size, keep the raw body when required, and identify the provider.

2. Persist

Store the event or put it on a durable queue with a dedupe key.

3. Acknowledge

Return the documented success status before slow downstream work.

4. Process

Normalize, update state, call downstream systems, and retry safely.

Idempotency belongs at the side effect, not only at ingress. If two workers race, a unique database constraint, conditional update, or transactional outbox should still prevent a duplicate refund, duplicate ticket, or double reply. Keep a replay tool that can requeue a stored event without bypassing the same guard.

For monitoring, track callback requests, accepted events, invalid payloads, acknowledgement latency, duplicates, worker lag, processing failures, unknown event types, and the age of the oldest unprocessed event. Alert on the gap between “received” and “processed,” not only on HTTP errors.

Secure the WhatsApp Webhook Without Inventing a Signature

The current provider callback reference says the callback does not contain authentication information and advises against permission verification on the receiving API. That is materially different from a Meta Cloud API integration, where the documented signature should be validated against the raw request body.

For a provider callback without a documented signature, use compensating controls and confirm the latest options with support before launch:

  • Use an unguessable provider-specific path and never expose it in client code or public logs.
  • Require HTTPS, enforce a small request-size limit, and accept only the documented content type and method.
  • Ask whether stable source IP ranges, mTLS, or a newer signed-callback option is available. Do not hard-code an IP list that the provider cannot maintain.
  • Validate the full schema, fixed channel fields, timestamps, and identifiers before processing.
  • Treat every event as untrusted input. Escape content, scan media through a controlled download path, and redact message bodies from routine logs.
  • Rate-limit obvious abuse, but do not make the acknowledgement path so strict that valid provider traffic is dropped.
  • Give workers least-privilege credentials. A callback event should not carry authority to perform an irreversible action by itself.

If your policy requires cryptographic source authentication, make that a provider-selection requirement. A secret URL is a compensating control, not a signature.

Troubleshoot WhatsApp Webhook Failures by Symptom

Symptom Likely cause First check
No callbacks arrive Wrong environment URL, callback not saved, WABA/app not subscribed Provider console configuration and a controlled test message
Repeated callbacks on providers that retry Slow response or non-200 acknowledgement p95 acknowledgement latency and receiver logs
Events missing after receiver downtime No retry documented in the callback contract used here Reconcile the window from console message history and statistics
HTTP 200 but no CRM update Wrong payload path, only first batch row processed, worker failure Stored raw fixture vs parser and queue lag
Duplicate tickets or replies Retry delivery or worker race without idempotency Unique event key and side-effect guard
Read arrives before delivered Network delay or event ordering across workers Transition policy and event-time history
Inbound media fails Expired URL, authorization, size, or unsupported type Provider media-download flow and retention policy
Unknown events increase Provider added a status or notification type Dead-letter sample and current changelog/docs

Do not test only from a dashboard “send sample” button. Run at least one real outbound template, one customer reply, one media message if supported, and one controlled downstream failure. The happy path proves the URL exists. Failure tests prove the system can be operated.

When a Managed WhatsApp API Stack Is the Better Fit

A direct Meta integration gives engineering teams control over the native contract. A managed provider can reduce onboarding, sender, template, analytics, and support work, but it also introduces a provider-specific callback surface. Compare the full operating model: regions, throughput, support, data handling, callback security, replay tooling, console visibility, and migration path.

EngageLab WhatsApp Business API is relevant when a team wants WhatsApp sending, templates, status visibility, and callback configuration within one managed platform. Validate the exact callback contract and security controls for your account during a pilot.

WhatsApp Business API product page for customer messaging and automation

If acquisition is the starting point, connect the receiver design to the click-to-WhatsApp ads setup . If cost is the blocker, use the dedicated WhatsApp Business API pricing guide instead of turning a technical webhook page into a pricing explainer.

Pilot the callback before you commit the integration

Bring three real payloads, one failure scenario, and your security requirements. A short technical pilot exposes more than a polished feature matrix.

Start a WhatsApp API pilot Review callback requirements

WhatsApp Webhook FAQ

What is the difference between a WhatsApp API response and a webhook?

The API response is synchronous and usually tells you whether a send request was accepted. A webhook is asynchronous and reports later events such as delivery, read, failure, an inbound message, or an account change. Store both message IDs and custom business IDs so the two sides can be joined.

Does a WhatsApp webhook have to be public?

The provider must be able to reach it over HTTPS, so the endpoint is normally internet-accessible. That does not mean it should be broadly trusted. Use the documented authenticity mechanism, strict request limits, schema validation, least privilege, and provider-specific paths.

Why do I receive duplicate WhatsApp webhook events?

Providers may retry when they do not receive the expected acknowledgement, and network failures can make the sender unsure whether your system processed an event. Workers can also race. Design for at-least-once behavior: persist a stable dedupe key and make each downstream side effect idempotent.

How quickly should a WhatsApp webhook return a response?

Follow the exact provider contract. The callback reference used in this guide requires HTTP 200 within three seconds. Keep acknowledgement fast by persisting or enqueueing the event and performing CRM, bot, analytics, and notification work asynchronously.

Can one endpoint handle Meta and multiple WhatsApp providers?

It can, but a shared public route makes authentication, parsing, and incident ownership harder. Separate provider-specific ingress routes are safer. Normalize events only after each adapter has validated its own contract, then send a common internal event to downstream systems.

A reliable WhatsApp webhook is not the handler that works once. It is the event boundary that still behaves when payloads are batched, requests repeat, workers restart, statuses arrive late, and downstream systems fail. Start with the exact provider contract, acknowledge quickly, preserve the raw event, and keep business actions idempotent.