To operate WhatsApp webhooks safely, distinguish three directions: Meta to Wats, Wats to your systems, and an external system to Wats. Each has a different secret, headers, and permission model. Verify signatures on the raw body, persist a stable event ID, deduplicate, return 2xx quickly, and perform downstream work asynchronously with recovery, monitoring, and secret rotation.
A webhook is an event notification, not proof that a business operation completed immediately. Requests can be delayed, duplicated, or delivered after a newer event, and a receiver may be temporarily unavailable. Reliability comes from the receiving contract, durable state, queue, and recovery policy—not simply from exposing a public endpoint.
Separate the event directions before designing them
“WhatsApp webhook” can refer to materially different contracts:
Direction | Sender | Receiver | Primary trust mechanism | Example |
|---|---|---|---|---|
Meta → Wats | Meta's WhatsApp Business Platform | Wats inbound endpoint | Verification token for setup, then | Inbound message or message-status update |
Wats → customer system | Wats | CRM, ERP, or middleware | Per-webhook secret and |
|
External system → Wats | CRM, ERP, or integration service | A Wats API or designated inbound endpoint | API token scoped to capabilities and channels, plus an idempotency contract | Eligible send request or context update |
Do not use a Meta app secret to sign a Wats outgoing webhook, and never place an API token in the payload. A secret authenticates the sender; a token authorizes an API caller. They need separate ownership and lifecycle management.
If the number and Meta assets are not connected yet, start with the WhatsApp Business and Meta integration guide before implementing event consumers.
Meta to Wats: verification before event delivery
Meta's official Cloud API webhooks documentation covers event subscription and delivery. Endpoint setup normally begins with a verification GET request; event deliveries then use POST.
1. GET verification handshake
The request includes:
Parameter | Purpose |
|---|---|
| Must match the expected subscription mode |
| A shared value chosen by the receiver and compared securely |
| Returned unchanged when verification succeeds |
When the mode and token match, the endpoint returns hub.challenge with a successful response. The verification token is not a signature for subsequent deliveries and does not replace the app secret.
2. Event POST requests
For each payload, validate X-Hub-Signature-256 using HMAC SHA-256, the Meta app secret, and the raw request bytes. Meta's payload validation documentation describes the signing model. After verification, extract stable message, status, and channel identifiers and record the event before slow external work.
Meta Wats endpoint Event store / worker
| GET verification | |
|---------------------->| compare verify token |
|<----------------------| 200 + challenge |
| | |
| POST event + signature| |
|---------------------->| verify raw body |
| | persist + deduplicate ------>|
|<----------------------| 2xx after safe acceptance |
| | process async |Here, 2xx means safely accepted, not that every downstream API call has finished. Waiting on a slow CRM before acknowledging Meta increases timeout, redelivery, and duplicate risk.
Wats to CRM or ERP
Wats can emit outgoing events such as message.inbound, message.outbound, conversation.assigned, conversation.escalated, and campaign.completed, filtered by event and channel. Requests include context headers such as:
X-Wats-Event
X-Wats-Webhook-Id
X-Wats-Company-Id
X-Wats-Timestamp
X-Wats-Signature: sha256=<hex-hmac-of-raw-body>The receiver computes HMAC with the secret configured for that Wats webhook—not the Meta secret. It then combines the webhook or event identifiers and event type into a deduplication key, persists the event, and returns 2xx before performing expensive updates.
Wats event → event/channel filter → sign raw payload → HTTPS POST
↓
CRM receiver ← 2xx ← verify → deduplicate → persist → queue
↓
update CRM/ERP asyncUse the WhatsApp CRM and ERP integration guide to establish source-of-truth and identity rules before selecting events.
External system to Wats
When ERP or CRM needs to initiate an action in Wats, the appropriate contract is generally an API call or a purpose-built inbound workflow—not an unstructured webhook bounce. Give the caller a token with only the required scopes and channels, plus expiry and revocation. Carry an idempotency key or external event ID so a retry cannot create two messages or actions.
For example, an ERP emits shipment.updated to an integration service. The service verifies and stores it, then a policy gateway decides whether it is internal context only or eligible for a WhatsApp send. If eligible, it calls Wats using the same operation identifier. Do not let every back-office system push arbitrary customer-facing text directly to the channel.
A WhatsApp automation workflow can orchestrate this path, but authentication, deduplication, and the policy gate should remain explicit controls outside any prompt or free-form text.
A safe, minimal payload example
The following is an illustrative normalized event inside an integration layer, not a claim that every provider uses these exact field names:
{
"event": "message.inbound",
"eventId": "evt_demo_01",
"occurredAt": "2026-05-31T06:05:12Z",
"companyId": "cmp_demo",
"channelId": "chn_demo",
"data": {
"messageId": "wamid.demo_123",
"conversationId": "conv_demo_45",
"direction": "inbound",
"contentType": "text"
}
}Never include an app secret, access token, or customer data the consumer does not need. If CRM genuinely requires message text, apply data minimization, retention rules, and log redaction.
Correct raw-body HMAC validation
This Node.js example validates a header formatted as sha256=<hex>. Pass rawBody exactly as received before JSON parsing, and retrieve the secret from a secret manager or protected environment variable:
import crypto from "node:crypto";
export function validSignature(rawBody, signatureHeader, secret) {
if (!signatureHeader?.startsWith("sha256=")) return false;
const received = Buffer.from(signatureHeader.slice(7), "hex");
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest();
return received.length === expected.length &&
crypto.timingSafeEqual(received, expected);
}Use the Meta app secret with X-Hub-Signature-256 for Meta deliveries, and the configured webhook secret with X-Wats-Signature for Wats deliveries. Reject missing headers, malformed hex, or a mismatch. Log a non-sensitive reason without printing the secret, full signature, or personal payload.
X-Wats-Timestamp also supports a maximum-request-age check to reduce replay risk. Allow a reasonable clock-skew window and keep deduplication as a separate control; a timestamp is not an idempotency key.
An event lifecycle that tolerates failure
Use explicit states instead of completing every dependency inside the request:
received → verified → persisted → queued → processing → processed
└────→ failed → retry / review
duplicate → acknowledge without repeating the business actionReceived: retain the raw body and only necessary headers.
Verified: validate signature, request age, and the expected contract.
Persisted: store source, stable ID, hash, and initial state.
Deduplicated: atomically reserve a unique key before changing business data.
Queued: acknowledge with 2xx and hand the job to a worker.
Processed: record resulting IDs, such as the CRM record or WhatsApp message.
Failed: classify transient versus permanent failure, attempts, and next action.
Use exponential backoff with jitter for transient failure, a finite attempt limit, and then a dead-letter queue or human review. Do not endlessly retry permanent errors such as invalid payloads or missing permission before correcting their cause.
Wats has a recovery path for failed incoming Meta events that were recorded for processing. For Wats outgoing webhooks, do not assume a retry schedule, ordering, or exactly-once behavior unless the contract for the deployed version documents it. Test delivery behavior, keep the receiver idempotent, and monitor for gaps regardless.
Deduplication and out-of-order handling
A useful deduplication key combines a stable source identifier with event type, for example:
meta + message_id + status_type
wats + webhook_id + event_id
erp + source_event_id + requested_actionReserve the key with a unique constraint or atomic transaction. A separate “check then insert” can race under concurrent delivery. Retain the completed outcome so a duplicate can receive 2xx without repeating the effect.
Out-of-order events require domain rules. If read has already been recorded and an older state arrives, do not move the message backwards. If an order update has a lower version than stored state, retain it for audit and ignore its business effect. Time alone may be insufficient; use a source sequence or version when available.
Secret rotation and monitoring
To rotate without downtime, add the new secret to the receiver, accept old and new for a short window, update the sender, observe any remaining old-secret traffic, and then revoke the old secret. Never leave the overlap open indefinitely. Meta app-secret rotation may require accepting active secrets during transition; Wats outgoing rotation requires coordinating the webhook configuration and receiver.
Monitor operational signals that do not expose customer data:
Accepted events and signature rejections.
Duplicate rate and failure reasons by endpoint.
Age of the oldest queued and failed event.
Attempt count and end-to-end processing delay.
Sequence gaps or count differences between source and receiver.
Avoid promising “guaranteed real time.” Event-driven or near-real-time processing is more accurate because networks, queues, and retries introduce variance. Design the interface to tolerate “updating” state and reconcile business-critical records.
How Wats applies these contracts
On the Meta inbound path, Wats validates the GET handshake and POST signature, records and deduplicates events, and tracks processing, failure, and recovery. On the outgoing path, it supports a URL, secret, event and channel filters, endpoint testing, and Wats signature headers over the raw body.
For calls from external systems, Wats API tokens support capability scopes, channel restrictions, expiry, and revocation. If an AI agent participates later in the flow, never give it the secret or authority to accept the webhook; verification and persistence are infrastructure controls that happen first. The rules, AI steps, and AI agents comparison shows where deterministic boundaries belong.
Design for receiving more than once
A reliable webhook is a security contract, state record, and recovery path—not one successful POST in a test client. Separate directions and secrets, validate raw bytes, reserve the deduplication key, acknowledge after safe acceptance, and execute downstream work outside the request. Test duplication, delay, out-of-order arrival, dependency outages, and secret rotation; that is what turns an event endpoint into an operable integration.

