WhatsApp webhook not arriving: localhost, ngrok and n8n
When a WhatsApp webhook does not arrive, the cause is almost always one of five things: the URL is not public, HTTPS fails, the right event is not turned on, the endpoint responds slowly or errors out, or n8n is using the test URL. You can find which one in a few minutes, in that order, using the API's test endpoint and recent deliveries list. This guide follows that order, with the commands.
Published on · 6 min read
Where do you start the diagnosis?
Split the problem into two questions. First: is the event leaving ZapZap? Second: does it reach your system and get processed? The API answers the first with the recent deliveries list, which shows per event whether it was delivered, the HTTP status and the latency. If the list is empty for real messages, the problem is before sending (event turned off or instance disconnected). If there are deliveries with an error status, the problem is in your endpoint.
First, confirm the instance. Check the instance status: if it is not connected, there are no new messages to generate events. Then confirm the configuration with get webhook: the URL, whether it is enabled and which events are selected.
Quatro comandos de diagnóstico
BASE="https://api.zapzapapi.com/api/v1"
H1="x-api-key: $ZAPZAP_API_KEY"
H2="x-api-secret: $ZAPZAP_API_SECRET"
curl "$BASE/$INSTANCE_ID/instance/status" -H "$H1" -H "$H2" # conectada?
curl "$BASE/instances/$INSTANCE_ID/webhook" -H "$H1" -H "$H2" # URL, enabled, events
curl "$BASE/instances/$INSTANCE_ID/webhook/deliveries?limit=20" -H "$H1" -H "$H2" # o que saiu e com qual status
curl "$BASE/$INSTANCE_ID/webhook/errors" -H "$H1" -H "$H2" # motivos de falhaCan I use localhost or 127.0.0.1 as the webhook URL?
No. The ZapZap server is on the internet and cannot see your machine. On top of that, the API rejects local and private addresses when you save the configuration (localhost, 127.0.0.1, 192.168.x.x and the like), so the configuration call already returns a validation error. The URL must be HTTPS, public and reachable from outside.
When you save, the API also runs a reachability check on your URL (a HEAD request with a short time limit). Any HTTP response, even a 404 or 405, counts as reachable. If the server does not respond within that window, the configuration fails with an unreachable URL message. If you got that error, the endpoint was down, behind a firewall or too slow at that moment.
How do you test locally with ngrok or another tunnel?
A tunnel publishes your local port on a public HTTPS URL. The flow: start the server, open the tunnel pointing at the port, copy the generated HTTPS URL and set it as webhook_url. Tools like ngrok and cloudflared (Cloudflare Tunnel) do this. Each has its own account and URL rules, so check their docs.
Two common traps. First: the tunnel URL changes when you restart (on plans without a fixed domain), and the configured webhook now points at nothing. Reconfigure every time you start a new tunnel. Second: a stopped tunnel or a local server that is down causes failures, and consecutive failures can disable the webhook.
To validate the whole path, run the webhook test right after configuring. It sends a signed event and returns status and latency. If the test works and real messages do not, go back to the turned-on events item.
Subir o túnel e configurar a URL (exemplo com ngrok)
# terminal 1: servidor local na porta 3000
node server.js
# terminal 2: túnel HTTPS para a porta 3000
ngrok http 3000
# copie a URL https://....ngrok-free.app e use abaixo
curl -X PUT "https://api.zapzapapi.com/api/v1/instances/$INSTANCE_ID/webhook" \
-H "x-api-key: $ZAPZAP_API_KEY" \
-H "x-api-secret: $ZAPZAP_API_SECRET" \
-H "Content-Type: application/json" \
-d '{"webhook_url":"https://SEU-SUBDOMINIO.ngrok-free.app/webhooks/zapzap","enabled":true,"events":["messages","connection"]}'Why does the n8n webhook receive nothing?
The n8n Webhook node has two URLs: the Test URL and the Production URL. The test one only listens while you click 'Listen for test event' in the editor, and it stays active for a short time. The production one only works with the workflow activated. The classic mistake is registering the test URL in ZapZap and then closing the editor: from then on nothing arrives.
For a live automation, register the Production URL and activate the workflow. For development, register the Test URL, click listen and send a real message to the number. With n8n in Docker or behind a reverse proxy, the address it shows may be internal (localhost:5678). Set n8n's WEBHOOK_URL environment variable to your public HTTPS domain, otherwise it displays a URL ZapZap cannot reach. Also check that the proxy forwards the body and does not block the POST.
If you would rather start from a ready-made flow, the n8n integration page has a tutorial and templates to import, and the n8n templates already come with the webhook trigger wired up.
Is the right event turned on?
Webhook events are off by default and you choose which to receive. If you only turned on connection, messages never arrive. To receive messages, turn on messages. To know whether a message was delivered or read, turn on messages_update. The full catalog is in set webhook.
Another filter that surprises people: excludeMessages. If you set it to exclude your own messages, the ones you send do not generate a webhook event. That is what you want in bots, but it confuses anyone looking for the echo of their own send. Check that field in the get webhook response.
My endpoint responds, but deliveries show errors. Now what?
Read the deliveries list. A 4xx or 5xx status means your server returned an error; check its logs. A timeout means the response exceeded the 10-second limit per attempt. The cause is almost always heavy work before res.send: an AI call, a slow database write or sending a message inside the request. Return 200 first and process afterward, as in the guide on webhooks in production.
Failures are resent on a retry ladder, and consecutive permanent failures can lead to the webhook being disabled. If the webhook was disabled, fix the cause, run the webhook test and set the URL again. A 401 or 403 on your side is usually signature verification done over already-parsed JSON instead of the raw body, or an outdated secret.
If all of this checks out and it still does not arrive, contact support with the delivery_id of a delivery, which identifies the event on both sides.
Quick checklist for when nothing arrives
[ ] The instance is connected (status connected)
[ ] The URL is HTTPS, public and responds within a few seconds, with no login wall or firewall in the way
[ ] The configuration saved without errors and get webhook shows the URL and enabled as true
[ ] The messages event (and connection, if you want drop alerts) is in the list
[ ] The webhook test returns a 2xx status and low latency
[ ] Recent deliveries show attempts with a 2xx status for real messages
[ ] On n8n: Production URL with an active workflow, or Test URL with the editor listening
[ ] Your server does not verify the signature over already-parsed JSON
If an item fails, fix it and repeat the test before moving on. Domain changes, expired certificates and new firewall rules are frequent causes of a webhook that worked and stopped. On a team, record the webhook URL and who owns the certificate, because an expired certificate takes HTTPS down and deliveries start failing with no warning on your side.
Quick summary
- localhost and private IPs are rejected. The webhook URL must be HTTPS, public and reachable.
- Use an HTTPS tunnel for development and reconfigure the URL every time the tunnel changes.
- On n8n, the Test URL only listens during 'Listen for test event'. In production, use the Production URL with the workflow active.
- Check the events turned on (messages, messages_update, connection) and the excludeMessages filter.
- Diagnose with the instance status, get webhook, recent deliveries and webhook errors.
Keep reading
Also read
Frequently asked questions
Why is my WhatsApp webhook not reaching localhost?
Because the API server cannot see your machine and, on top of that, the configuration rejects local and private addresses. Use an HTTPS tunnel (ngrok, cloudflared) or a staging server with a public URL.
I saved the URL and got an unreachable URL error. What now?
When you save, the API runs a reachability check with a short time limit. Make sure the server is up, with valid HTTPS, no firewall blocking it and a fast response to the request.
Does the n8n webhook work in test mode?
Only while you click listen for test event in the editor. For continuous use, activate the workflow and register the Production URL in ZapZap. On Docker or behind a proxy, set WEBHOOK_URL to the public domain.
I get the connection event but not messages. Why?
Because the messages event is not turned on in the webhook configuration. Update the events list with messages and, if you want delivery and read status, messages_update.
How do I see whether ZapZap tried to deliver?
Through the instance's recent deliveries endpoint: it lists event, HTTP status, latency and attempt number. The webhook errors show the reason for the latest failures.
Test the webhook with a signed event
Create your account, verify your phone and get the welcome credit, no deposit needed (one per phone, subject to the current campaign). Connect a number and run the webhook test to see your URL's latency.
