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.