A WhatsApp automation is a public URL that accepts messages and does things because of them. That sentence should make you slightly nervous. Anyone who finds the URL can send it a request that looks like a WhatsApp message, and if your workflow trusts it, they can make your system send replies, write rows to your sheet or create bookings.
Most of the risk sits in three places: the webhook that receives messages, the secrets that let you send them, and the message data you keep afterwards. None of it’s exotic. It’s mostly a matter of doing the dull checks every time.
Check the signature on every POST
When Meta sends a notification to your webhook, the request carries an X-Hub-Signature-256 header. Its value is sha256= followed by a hex signature of the payload, made with your app secret. Meta’s documentation says validating this is recommended rather than required, which is exactly why a lot of workflows skip it.
Skip it and your endpoint will accept a hand-written request from anyone. The check is short:
- Take the raw request body, byte for byte, exactly as it arrived.
- Compute an HMAC with SHA-256 using your app secret as the key.
- Compare the result with the value after
sha256=. - Reject the request if they differ, before any other processing.
Two details cause most of the bugs. First, the signature covers the raw bytes. If your framework parses the JSON and you re-serialise it, key order or whitespace can change and a genuine request will fail. Second, compare with a constant-time function. In PHP that’s hash_equals(), and its manual page explains why an ordinary string comparison can leak information through timing. Pass the value you computed first and the value from the request second.
In n8n, the Webhook node has a Raw Body option, and that’s where you start. Our open source travel-whatsapp-booking-bot verifies the Meta webhook signature. If checking the HMAC inside your workflow turns out to be awkward, put a small proxy in front that verifies the signature and forwards only valid requests.
Do not confuse the verify token with the signature
When you register a webhook, Meta sends a one-off GET request with hub.mode, hub.challenge and hub.verify_token. Your endpoint must check that the token matches the one you set in the app dashboard, and answer with the challenge value. This proves you control the endpoint.
It doesn’t protect later traffic. The verify token only matters during setup, while the signature protects each notification. Treat them as two separate secrets and two separate checks.
Assume every event arrives more than once
If your endpoint returns anything other than HTTP 200, Meta retries delivery with decreasing frequency, for up to seven days according to its webhook setup guide. Retries can also reach several subscribed apps, so duplicates are normal.
That has a security consequence. A replayed message that creates a second booking or sends a second payment link is a bug, and an attacker who captured a genuine request could cause the same effect. Store the message ID from each event and ignore any ID you have already processed. Return 200 quickly, then do the slow work, so a slow database doesn’t trigger a retry storm.
Keep secrets out of workflows and repositories
An automation touches several credentials: the app secret, the access token used to send messages, any spreadsheet or CRM key, and the token for whatever AI service reads the messages. Common mistakes:
- Pasting a token into a workflow node and then exporting the workflow to GitHub.
- Using one long-lived token with every permission instead of one scoped to the task.
- Putting the app secret in a chat message so a colleague can "just test something".
- Never rotating anything, so a token from a former contractor still works.
Keep secrets in your platform’s credential store or environment variables. Give each integration its own credential so you can revoke one without breaking the rest. Write down who can read them, and rotate on a schedule and whenever someone with access leaves. If a secret has appeared in a repository, even briefly, treat it as exposed and replace it. Deleting the commit doesn’t undo the exposure.
Meta’s guide recommends mutual TLS for webhook security and discourages IP allowlisting, because Meta’s addresses change from time to time. Allowlisting can still be a useful extra layer, but don’t rely on it alone.
Decide what you keep, and for how long
The message text is personal data. People send names, phone numbers, passport details, medical notes and photos of cards, usually without being asked. Your automation then copies that data into logs, spreadsheets and AI prompts.
Before launch, answer these questions in writing:
- Which fields do we need to do the job? Store those and drop the rest.
- Where does each copy live? Count the workflow execution history, the sheet, the CRM, the AI provider’s logs and your backups.
- How long do we keep it? Pick a number, and make something delete it automatically.
- Who can read it? Most staff don’t need the raw conversation history.
- Does the AI provider keep prompts, and for how long? Check the contract terms, not the marketing page.
Workflow tools often store every execution, including the full payload, by default. Switch that off or set a short expiry for production workflows. Mask card numbers, passport numbers and similar identifiers before they reach a log. Better still, tell customers not to send them in chat, and have the bot say so.
If you handle data from people in other countries, their local privacy rules may apply to you wherever you’re based. Get proper advice on the legal basis and retention period. This article covers the engineering, not the legal position.
Limit what the automation can do
The signature check stops forged requests. It does nothing about a real customer who sends a message designed to push your AI step into doing something odd. Treat the text of every message as untrusted input. Our article on prompt injection and business automations covers that side.
For a booking bot, a safe default is that it can read availability and draft replies, while confirming a booking, issuing a refund or changing a price stays with a person. Our note on what an AI agent should and shouldn’t decide sets out the reasoning.
A short checklist
- Verify
X-Hub-Signature-256on the raw body for every POST, with a constant-time comparison. - Verify the token on the GET challenge during setup.
- Deduplicate by message ID and return 200 quickly.
- Use scoped credentials in a credential store, rotate them, and never commit them.
- Keep only the fields you need, set a retention period, and automate deletion.
- Switch off full-payload execution logging in production.
- Keep money, bookings and refunds behind a human step.
- Log rejected requests so you can see probing.
If you want this set up or reviewed for an existing workflow, a System Audit covers it.