What is webhook testing?
Webhook testing is checking that your webhook endpoint does the right thing with real-shaped events before customers depend on it — not merely that it passes the verification handshake. A URL that echoes hub.challenge proves only that Meta can reach it; testing proves that it checks signatures, walks batched payloads, handles every message type and status you care about, ignores what it does not, and returns 200 quickly.
Each tool catches different bugs. From WhatsApp > Configuration in the App Dashboard you can send a test payload for a subscribed field — sample data, good for checking routing and parsing. The test business phone number Meta provides when you add WhatsApp to a developer app can message up to five verified recipient numbers, so you can trigger real inbound messages and statuses end to end. A local HTTPS tunnel exposes a laptop server during development, and replaying captured payloads — re-signed in X-Hub-Signature-256 with your app secret — reruns yesterday's bug in a unit test or against staging.
Testing differs from verification: the webhook verification request is a one-time GET proving the URL is yours, while testing exercises the POST path that carries every event afterwards. Meta also notes that some webhooks are not sent while an app is in Development mode, so a test that passes on a Dev-mode app can still miss events once real traffic flows.
Key takeaways
- Webhook testing proves your endpoint handles real-shaped events correctly, not just that it passes the verification handshake.
- The App Dashboard sends a sample payload for any subscribed field from WhatsApp > Configuration.
- Meta's test business number can message up to five verified recipient numbers, enough for end-to-end tests.
- Replays must be re-signed in
X-Hub-Signature-256over the raw stored bytes, or the signature check fails. - Test the failure paths too: duplicates, tampered signatures, a crashing handler and out-of-order statuses.
Replay a captured event against your endpoint
https://dev-7f3a.tunnel.example.com/webhooks/whatsappPOST 200 in 240 ms(simulated)
Request
3 headers · bodyResponse
200 OKResponse: 200 OK after 240 ms.
Why does webhook testing matter?
Webhook bugs are silent. A handler that crashes on a reaction message or a batched status does not page anyone — it returns 500 while Meta retries for days and customers wait for replies that never come. Testing the real shapes, including failures, duplicates and out-of-order statuses, costs an afternoon before launch; discovering after a campaign that a whole message type never reached the inbox costs far more.
How does webhook testing work?
Unit-test with signed fixtures
Save one sample payload per field and message type you support — text, interactive reply, media, statuses with errors — and run the handler against them in CI, signed with a test secret.
Expose a development endpoint
Run the handler behind an HTTPS tunnel or on a staging URL, and point a separate test app (or an override on a test number) at it — never a production app. Meta's docs also describe a throwaway endpoint that only logs payloads: if it shows events your code misses, the bug is in your code.
Send dashboard test payloads
From WhatsApp > Configuration, send a test payload for each subscribed field and confirm it is routed, logged and acknowledged with a 200.
Run real traffic with the test number
Message the test number from a verified recipient phone, reply from your app (a Postman collection for the Cloud API makes the outbound calls quick) and watch
messagesandstatusesarrive — including areadthat beats itsdelivered.Replay, duplicate and break things
Replay a captured payload twice to prove deduplication, send a tampered body to prove the signature check returns 401, and stop the worker to see the queue absorb a burst.
Webhook testing in code
// replay.mjs — re-send a captured payload to a dev or staging endpoint, correctly signedimport crypto from "node:crypto";import fs from "node:fs";const [file, url] = process.argv.slice(2);const body = fs.readFileSync(file); // raw bytes, exactly as capturedconst signature = "sha256=" + crypto.createHmac("sha256", process.env.APP_SECRET) // the secret your handler verifies with.update(body).digest("hex");const started = Date.now();const res = await fetch(url, {method: "POST",headers: { "Content-Type": "application/json", "X-Hub-Signature-256": signature },body,});console.log(res.status, `${Date.now() - started} ms`); // expect 200, fast
Sign the stored bytes, not a re-stringified object, or the signature will never match.
Example: Two bugs caught before launch day
Before launching WhatsApp support, a Bhopal insurance broker's two-person team captured a week of masked payloads from its staging number into fixtures. Replaying them in CI found two bugs: the handler threw on interactive button replies because it only expected text, and a step that re-serialised the body made every replayed signature fail. Both were fixed before go-live. On launch day the only surprise was a sticker message, which the handler logged as unhandled and acknowledged with a 200 — exactly as designed.
Common wrong turns
Common mistakes with webhook testing
Testing only the handshake
A green verification check says nothing about POST handling. Exercise real payloads for every field you subscribe to.
Replaying re-serialised JSON
Pretty-printing or re-stringifying a captured payload changes its bytes, so the signature no longer matches. Store and replay the raw body.
Fixtures full of customer data
Captured payloads contain phone numbers, names and message text. Mask them before fixtures go into a repository or a ticket.
Pointing production at a laptop
A tunnel URL saved on a production app or number sends real customers' messages to a dev machine. Use a separate test app, or an override on a test number only.
Never testing failure
Happy-path tests miss the bugs that cause retry storms. Try a 500, a slow handler, a duplicate and an out-of-order status before launch.
Key facts at a glance
Webhook testing: key facts and limits
- Dashboard test
- Sample payload per field from WhatsApp > Configuration
- Test phone number
- Messages up to 5 verified recipient numbers
- Replay signature
- HMAC-SHA256 of the raw body with the app secret
- Endpoint
- Public HTTPS with a valid CA-signed certificate
- App mode
- Some webhooks are not sent in Dev mode
- Expected answer
- 200 for every event, including unhandled types
Good practice
Best practices for webhook testing
- Keep one fixture per message type and status, plus one batched payload with several entries.
- Make the replay script part of incident response so you can rerun the exact payload that failed.
- For load testing, replay captured payloads at several times your expected peak and watch queue depth.
- Check logs for 'unhandled field' entries after every test run.