/devices/{device_id}/webhook.
How delivery works
Each target URL is delivered independently, with its own secret and retries.Configure
--webhook, --webhook-secret, --webhook-events, --webhook-ignore-jids, --webhook-insecure-skip-verify.
Events
Event names in the allowlist are case-insensitive.
Payload
Every request has the same envelope:
Replies add
replied_to_id and quoted_body. Receipts use ids (array) and receipt_type. Group events carry chat_id, type and jids.
Media
WithWHATSAPP_AUTO_DOWNLOAD_MEDIA=true (default), media is saved under statics/media/ and the media field holds the path:
{"path": "...", "caption": "..."} and the caption is also in body. With auto-download off, it is {"url": "https://mmg.whatsapp.net/...", "caption": "..."}; fetch the file later with GET /message/{message_id}/download?phone=<chat>.
The full catalogue of payloads (polls, locations, contacts, view-once, ads referrals and more) is in the upstream webhook payload reference.
Verify signatures
Each request carriesX-Hub-Signature-256: sha256=<hex>, an HMAC-SHA256 of the raw request body keyed with the webhook secret. Compute it over the exact bytes received, before parsing JSON.
Delivery and retries
- Each attempt times out after 10 seconds.
- Any non-2xx response or network error is retried. GOWA makes up to 5 attempts, waiting 1 s, 2 s, 4 s and 8 s between them.
- After the last failure the event is dropped and logged. There is no persistent queue or dead-letter store.
- Several URLs are delivered independently.
2xx quickly, process in the background, and make handlers idempotent; a retried event can arrive more than once.
Filter events
By type:WHATSAPP_WEBHOOK_EVENTS=message,message.ack forwards only those events.
By chat or sender: WHATSAPP_WEBHOOK_IGNORE_JIDS matches chat_id, from, chat_lid and from_lid. Entries starting with @ are wildcards for a whole address space (@g.us, @s.whatsapp.net, @lid); anything else is an exact JID.
@s.whatsapp.net also matches group messages, because their from is a user JID. Use @g.us to mute groups. The two filters combine: an event is sent only if its type is allowed and its JID is not ignored.
Per-device webhooks
Give a slot its own webhook, secret and event allowlist:POST /devices. Read the current setting with GET /devices/{device_id}/webhook. Send "webhook_url": "" to clear it.
By default a per-device URL replaces the global WHATSAPP_WEBHOOK for that slot. With WHATSAPP_WEBHOOK_DEVICE_MERGE_GLOBAL=true it is added to the global URLs instead: the device URL uses the device secret and allowlist, the global URLs keep the global ones, and a URL present in both receives the event once. Ignore-JID rules apply to both.
Testing locally
Expose a local receiver with a tunnel (for examplengrok http 8080) and set WHATSAPP_WEBHOOK to the tunnel URL. With --debug=true, GOWA logs each delivery attempt and failure.
Other ways to receive events
- WebSocket
/ws?device_id=<id>: pushes pairing and status events to the dashboard. It is not a substitute for webhooks. - Polling:
GET /chatsandGET /chat/{chat_jid}/messagesread GOWA’s local chat storage. See Receiving messages.
