Guides/For developers

n8n WhatsApp Business API integration: no-code automation through WACO

Connect WhatsApp to n8n, Make, or Zapier: receive messages via webhook, reply via the API. Importable n8n workflow with signature verification and de-duplication built in. 6 min read

n8n, Make, and Zapier need just two things to talk to WhatsApp through WACO — and both come with every plan: a webhook that pushes every incoming message into your flow, and an API to reply or send templates. No custom node to install; the built-in Webhook and HTTP Request nodes are enough. This guide uses n8n as the example; the same pattern applies to Make and Zapier.

What you can build: auto-replies when a customer mentions a keyword, logging every incoming chat to Google Sheets or Notion, a WhatsApp message when a new order lands in your shop, a Slack/Telegram alert when a message fails to deliver, or a bridge between WhatsApp and an ERP/CRM that has no official integration.

What you need

Step 1 — Import the example workflow

Copy the JSON below, then in n8n choose Workflows → Import from clipboard. It has three nodes: Webhook receives WACO events, Code verifies the signature and filters, HTTP Request replies through the WACO API. The example logic: when a customer mentions "price", send a link to the price list.

{
  "name": "WACO → n8n: auto-reply",
  "nodes": [
    { "id": "1", "name": "WACO Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [0, 0],
      "parameters": { "httpMethod": "POST", "path": "waco", "responseMode": "onReceived", "options": { "rawBody": true } } },
    { "id": "2", "name": "Verify & filter", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [260, 0],
      "parameters": { "jsCode": "// 1) Verify the WACO signature over the RAW body (not the parsed JSON)\nconst crypto = require('crypto');\nconst SECRET = 'whsec_REPLACE_WITH_YOUR_SECRET';\nconst raw = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst received = String($input.first().json.headers['x-waco-signature'] || '');\nconst computed = 'sha256=' + crypto.createHmac('sha256', SECRET).update(raw).digest('hex');\nif (received.length !== computed.length || !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(computed))) return [];\nconst ev = JSON.parse(raw.toString('utf8'));\n// 2) Incoming messages only (status & echo events are ignored in this example)\nif (ev.event !== 'message.received') return [];\n// 3) De-duplicate: a WACO webhook can be delivered twice -> remember processed ids\nconst st = $getWorkflowStaticData('global');\nst.seen = st.seen || {};\nconst now = Date.now();\nfor (const k of Object.keys(st.seen)) if (now - st.seen[k] > 86400000) delete st.seen[k];\nif (st.seen[ev.pesan.id]) return [];\nst.seen[ev.pesan.id] = now;\n// 4) Your logic: example auto-reply when a customer asks for prices\nconst text = String(ev.pesan.teks || '').toLowerCase();\nif (!/price|harga|how much/.test(text)) return [];\nreturn [{ json: { dari: ev.dari, nama: ev.nama, balasan: 'Hi ' + (ev.nama || '') + ', here is our price list: https://example.com/prices' } }];" } },
    { "id": "3", "name": "Reply via WACO", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [520, 0],
      "parameters": { "method": "POST", "url": "https://waco.id/api/v1/balas",
        "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth",
        "sendBody": true, "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ nomor: $json.dari, teks: $json.balasan }) }}",
        "options": {} } }
  ],
  "connections": {
    "WACO Webhook": { "main": [[{ "node": "Verify & filter", "type": "main", "index": 0 }]] },
    "Verify & filter": { "main": [[{ "node": "Reply via WACO", "type": "main", "index": 0 }]] }
  }
}

Step 2 — Fill in the secret & API key

  1. Open the Verify & filter node and replace whsec_REPLACE_WITH_YOUR_SECRET with your webhook secret.
  2. Open the Reply via WACO node → Credential → create a new Header Auth: Name Authorization, Value Bearer waco_YOUR_KEY.
  3. Activate the workflow, then copy the Production URL from the Webhook node (it ends in /webhook/waco).
The n8n Webhook node has two URLs: Test only listens while you press "Listen for test event"; Production listens as long as the workflow is active. Register the Production URL with WACO.

Step 3 — Register the URL with WACO

On the Developer page, paste the Production URL as your webhook URL and save. Send a chat to your WhatsApp number containing the word "price" — within a second or two the reply from n8n arrives, and the run shows up under Executions in n8n.

Three things that keep this flow safe to run forever

On self-hosted n8n, allow the Code node to use the crypto module by setting the environment variable NODE_FUNCTION_ALLOW_BUILTIN=crypto. On n8n Cloud it is already available.

Common variations

Make and Zapier

Identical pattern: Webhooks → Custom webhook (Make) or Webhooks by Zapier → Catch Hook receives the event; HTTP → Make a request or Webhooks by Zapier → POST calls the WACO API with Authorization: Bearer waco_…. Signature verification uses each platform's code module (Make: Custom JS, Zapier: Code by Zapier) with the same HMAC logic as above.

Frequently asked

Does this need a special plan? No. The API and webhooks are included in every WACO plan; message fees are paid to Meta as usual.

Can it run alongside the team inbox? Yes. Webhooks run side by side: your team keeps replying from the inbox while n8n receives a copy of the same events.

Is there an official WACO node in the n8n catalogue? Not yet — and it is not needed; HTTP Request covers the whole API. The full spec is in the OpenAPI reference.

Still stuck after following this guide? Contact us from the dashboard — attach a screenshot if you have one, it speeds everything up.