<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Abhishek Rajpurohit]]></title><description><![CDATA[Abhishek Rajpurohit]]></description><link>https://abhishekrajpurohit.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 24 Oct 2026 01:22:17 GMT</lastBuildDate><atom:link href="https://abhishekrajpurohit.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Self-Host a Free WhatsApp API on Your Own Machine with n8n and OpenWA]]></title><description><![CDATA[Run your own WhatsApp API with OpenWA and n8n: Docker setup, Cloudflare Tunnel, webhooks and an AI auto-reply. No cloud bills.]]></description><link>https://abhishekrajpurohit.hashnode.dev/self-host-a-free-whatsapp-api-on-your-own-machine-with-n8n-and-openwa</link><guid isPermaLink="true">https://abhishekrajpurohit.hashnode.dev/self-host-a-free-whatsapp-api-on-your-own-machine-with-n8n-and-openwa</guid><category><![CDATA[whatsapp]]></category><category><![CDATA[self-hosted]]></category><category><![CDATA[Docker]]></category><category><![CDATA[automation]]></category><category><![CDATA[n8n]]></category><dc:creator><![CDATA[Abhishek Rajpurohit]]></dc:creator><pubDate>Wed, 21 Oct 2026 17:23:57 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>Originally published on <a href="https://abhishekrajpurohit.in/blog/complete-guide-self-hosting-n8n-openwa-whatsapp-api">my blog</a>, which has the updated version.</p>
</blockquote>
<p>This is the single-page version of a 6-part series. Every step is here — read top to bottom and you will have a working WhatsApp AI bot running on your own machine by the end.</p>
<h2>Table of Contents</h2>
<ul>
<li><p><a href="#part-1-n8n-cloudflare-tunnel-setup">Part 1 — n8n + Cloudflare Tunnel Setup</a></p>
</li>
<li><p><a href="#part-2-openwa-docker-setup">Part 2 — OpenWA Docker Setup</a></p>
</li>
<li><p><a href="#part-3-connecting-n8n-to-openwa">Part 3 — Connecting n8n to OpenWA</a></p>
</li>
</ul>
<hr />
<h2>Prerequisites</h2>
<ul>
<li><p>Docker installed (or Podman with socket activation)</p>
</li>
<li><p>A domain proxied through Cloudflare (free tier works)</p>
</li>
<li><p>cloudflared installed on the host</p>
</li>
<li><p>A Linux machine — this guide uses <code>~/projects</code> as the workspace</p>
</li>
<li><p>A phone with WhatsApp installed</p>
</li>
</ul>
<hr />
<h2>Part 1 — n8n + Cloudflare Tunnel Setup</h2>
<blockquote>
<p>Read the dedicated post: <a href="https://abhishekrajpurohit.in/blog/n8n-cloudflare-tunnel-setup">n8n + Cloudflare Tunnel Setup</a></p>
</blockquote>
<h3>Step 1: Directory and Docker Compose</h3>
<p>Create the project directory:</p>
<pre><code class="language-bash">mkdir -p ~/projects/n8n
cd ~/projects/n8n
</code></pre>
<p>Create <code>docker-compose.yml</code>:</p>
<pre><code class="language-yaml">services:
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    ports:
      - "5679:5678"
    environment:
      - N8N_HOST=n8n.yourdomain.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - N8N_EDITOR_BASE_URL=https://n8n.yourdomain.com
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      - N8N_DEFAULT_BINARY_DATA_MODE=filesystem
      - N8N_PROXY_HOPS=1
      - N8N_RUNNERS_ENABLED=true
      - GENERIC_TIMEZONE=UTC
    volumes:
      - n8n_data:/home/node/.n8n
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:5678/healthz"]
      interval: 30s
      timeout: 10s
      retries: 5

volumes:
  n8n_data:
    driver: local

networks:
  default:
    name: openwa-network
</code></pre>
<p>Replace <code>n8n.yourdomain.com</code> with your actual subdomain throughout.</p>
<p><strong>Important — the shared Docker network:</strong> Both n8n and OpenWA must be on the same Docker network so n8n can reach OpenWA at <code>http://openwa-api:2785</code> by container name. The compose above creates a network named <code>openwa-network</code>. When you bring up OpenWA in Part 2, make sure its compose also joins this network — add the following to your <code>docker-compose.dev.yml</code> under OpenWA:</p>
<pre><code class="language-yaml">networks:
  default:
    name: openwa-network
    external: true
</code></pre>
<p>If they are on separate networks, n8n will get connection refused when trying to send WhatsApp messages — and the error won't tell you why.</p>
<blockquote>
<p>Follow the <a href="https://abhishekrajpurohit.in/blog/n8n-cloudflare-tunnel-setup">Cloudflare Tunnel Setup</a> guide to install cloudflared, create the tunnel, configure ingress rules, and route your domain.</p>
</blockquote>
<blockquote>
<p>Once the tunnel is live and n8n is reachable at <code>https://n8n.yourdomain.com</code>, continue below.</p>
</blockquote>
<hr />
<blockquote>
<p><strong>n8nctl</strong> — a simple script to start/stop n8n and the Cloudflare tunnel together. Get it <a href="https://abhishekrajpurohit.in/blog/n8n-cloudflare-tunnel-setup#managing-the-stack-with-n8nctl">here</a>.</p>
</blockquote>
<hr />
<h2>Part 2 — OpenWA Docker Setup</h2>
<blockquote>
<p>Read the dedicated post: <a href="https://abhishekrajpurohit.in/blog/complete-guide-self-hosting-n8n-openwa-whatsapp-api#part-2-openwa-docker-setup">OpenWA Docker Setup — From Zero to First Login</a></p>
</blockquote>
<p>OpenWA is an open-source WhatsApp API gateway built on NestJS and whatsapp-web.js. It gives you a REST API to send and receive messages without paying Meta for WhatsApp Business API access.</p>
<p>GitHub: <a href="https://github.com/rmyndharis/OpenWA">github.com/rmyndharis/OpenWA</a></p>
<h3>Clone and One Command</h3>
<pre><code class="language-bash">cd ~/projects
git clone https://github.com/rmyndharis/OpenWA.git
cd OpenWA
docker compose -f docker-compose.dev.yml up -d
</code></pre>
<p>No environment files to create. The dev compose has zero-config defaults for everything: NestJS API + dashboard on port 2785, SQLite, Chromium headless, named volume for all data, ports bound to 127.0.0.1.</p>
<p><strong>Before you start</strong> — append this to the bottom of <code>docker-compose.dev.yml</code> in the OpenWA directory so it joins the same network as n8n:</p>
<pre><code class="language-yaml">networks:
  default:
    name: openwa-network
</code></pre>
<p>Then run the compose command above. Without this, n8n cannot reach OpenWA by container name and you will get silent connection refused errors.</p>
<h3>Wait for It to Start</h3>
<pre><code class="language-bash">docker compose -f docker-compose.dev.yml logs -f
</code></pre>
<p>Look for <code>OpenWA is running on [http://localhost:2785](http://localhost:2785)</code>. Takes 10–20 seconds on first run.</p>
<h3>Login to the Dashboard</h3>
<p>Open <a href="http://localhost:2785">http://localhost:2785</a>. The first-boot screen prompts for the admin API key.</p>
<pre><code class="language-bash"># Find the container name
docker ps --format '{{.Names}}'

# Read the key
sudo docker exec container-name cat /app/data/.api-key
</code></pre>
<p>Paste the key into the dashboard. Store it in a password manager — you need it for every API call and for n8n credentials.</p>
<h3>Session State Machine</h3>
<p><code>disconnected</code> → <code>connecting</code> → <code>authenticating</code> (scan QR) → <code>ready</code></p>
<p>Stuck at "authenticating"? Pin a known-good WhatsApp Web version in .env:</p>
<pre><code class="language-plaintext">WWEBJS_WEB_VERSION=2.3000.1023204257
</code></pre>
<hr />
<h2>Part 3 — Connecting n8n to OpenWA</h2>
<blockquote>
<p>Read the dedicated post: <a href="https://abhishekrajpurohit.in/blog/n8n-openwa-integration">Connecting n8n to OpenWA — First Integration</a></p>
</blockquote>
<p>n8n and OpenWA run on the same machine. OpenWA never needs a public URL. n8n → OpenWA is always <code>localhost:2785</code>. OpenWA → n8n (webhooks) requires your public Cloudflare Tunnel URL.</p>
<h3>Step 1: Register the Webhook in OpenWA</h3>
<p>Go to <a href="http://localhost:2785">http://localhost:2785</a> → <strong>Webhooks</strong> → <strong>Create Webhook</strong>.</p>
<p>Fill in:</p>
<ul>
<li><p><strong>Session</strong> — select your session (e.g. <code>default</code>)</p>
</li>
<li><p><strong>URL</strong> — <code>https://n8n.yourdomain.com/webhook-test/&lt;webhook-name&gt;</code> (test) or <code>https://n8n.yourdomain.com/webhook/&lt;webhook-name&gt;</code> (production)</p>
</li>
<li><p><strong>Events</strong> — pick the ones you need (see below)</p>
</li>
</ul>
<blockquote>
<p><strong>Tip:</strong> Register the <strong>test URL first</strong> (<code>/webhook-test/&lt;webhook-name&gt;</code>). Use the test URL while building and debugging your workflow — it doesn't require authentication. Once everything works, switch to the production URL (<code>/webhook/&lt;webhook-name&gt;</code>).</p>
</blockquote>
<p><strong>Available events:</strong></p>
<table>
<thead>
<tr>
<th>Event</th>
<th>When it fires</th>
</tr>
</thead>
<tbody><tr>
<td><code>message.received</code></td>
<td>A message arrives on your WhatsApp</td>
</tr>
<tr>
<td><code>message.sent</code></td>
<td>A message is sent from your session</td>
</tr>
<tr>
<td><code>message.ack</code></td>
<td>Message delivery/read acknowledgement</td>
</tr>
<tr>
<td><code>message.failed</code></td>
<td>Message failed to send</td>
</tr>
<tr>
<td><code>message.revoked</code></td>
<td>A message was deleted by sender</td>
</tr>
<tr>
<td><code>session.status</code></td>
<td>Session status changes</td>
</tr>
<tr>
<td><code>session.qr</code></td>
<td>QR code generated (scan required)</td>
</tr>
<tr>
<td><code>session.authenticated</code></td>
<td>Session authenticated successfully</td>
</tr>
<tr>
<td><code>session.disconnected</code></td>
<td>Session disconnects</td>
</tr>
<tr>
<td><code>group.join</code></td>
<td>Someone joins a group</td>
</tr>
<tr>
<td><code>group.leave</code></td>
<td>Someone leaves a group</td>
</tr>
<tr>
<td><code>group.update</code></td>
<td>Group info updated</td>
</tr>
<tr>
<td><code>*</code></td>
<td>All events</td>
</tr>
</tbody></table>
<p>For a basic bot, select <code>message.received</code> only. Adding <code>*</code> fires on everything — including your own outgoing messages, which will cause an infinite loop if your workflow replies unconditionally.</p>
<h3>Step 2: Add the OpenWA Credential in n8n</h3>
<p>Open <a href="https://n8n.yourdomain.com">https://n8n.yourdomain.com</a> → Credentials → Add Credential → <strong>HTTP Header Auth</strong>:</p>
<ul>
<li><p>Name: <code>X-API-Key</code></p>
</li>
<li><p>Value: your OpenWA API key</p>
</li>
</ul>
<blockquote>
<p><strong>Before Step 3:</strong> Open your n8n Webhook node and click <strong>Listen</strong>. Then send a message from WhatsApp (or have someone message you). n8n will receive the payload. If you see the incoming data in the Webhook node's output — you are connected. Proceed to Step 3.</p>
</blockquote>
<h3>Step 3: Configure the HTTP Request Node</h3>
<p>Add an <strong>HTTP Request</strong> node in your n8n workflow:</p>
<ul>
<li><p>Method: POST</p>
</li>
<li><p>URL: <code>http://localhost:2785/api/send/text</code></p>
</li>
<li><p>Authentication: HTTP Header Auth → select the credential created in Step 3</p>
</li>
<li><p>Body:</p>
</li>
</ul>
<pre><code class="language-json">{
  "session": "default",
  "to": "{{ $json.message.from }}",
  "text": "Got your message!"
}
</code></pre>
<hr />
<h2>Part 4 — WhatsApp + n8n Workflow Varieties</h2>
<blockquote>
<p>Read the dedicated post: <a href="https://abhishekrajpurohit.in/blog/whatsapp-n8n-workflow-varieties">3 WhatsApp + n8n Workflow Varieties</a></p>
</blockquote>
<p>Three workflows from the same webhook trigger. All use: OpenWA at <code>http://openwa-api:2785</code> (Docker network hostname, not localhost), Ollama <code>llama3.2:3b</code> for AI, 30-message Simple Memory per chatId.</p>
<h3>Variety 1: Personal AI Chat Bot</h3>
<p>IF node fires when <code>body.data.contact.pushName</code> equals the name you set. Everyone else is silently ignored.</p>
<p><strong>Three mistakes:</strong></p>
<ul>
<li><p><strong>Wrong pushName</strong> — check the webhook payload for the actual value, not your contact list name</p>
</li>
<li><p><strong>Memory is per chatId</strong> — you're already covered for multiple people automatically</p>
</li>
<li><p><strong>No system prompt</strong> — change "You are a helpful assistant" to something specific immediately</p>
</li>
</ul>
<h3>Variety 2: Group @agent Bot</h3>
<p>The bot lives in a WhatsApp group. Someone @mentions it to get a response — without the mention filter, every message in the group fires the workflow and the bot replies to its own replies → infinite loop.</p>
<p><strong>How it works:</strong></p>
<ul>
<li><p>IF node fires when <code>body.data.isGroup == true</code> AND message contains <code>"@agent"</code></p>
</li>
<li><p>Everyone in the group can chat with the bot by tagging it</p>
</li>
<li><p>The bot sees all messages but only responds when @mentioned</p>
</li>
</ul>
<p><strong>Two mistakes to avoid:</strong></p>
<ul>
<li><p><strong>No @agent filter</strong> — bot fires on every group message including its own replies → infinite loop</p>
</li>
<li><p><strong>Generic system prompt</strong> — groups are noisy; use short, direct response instructions. Something like: <em>"You are a helpful group assistant. Keep answers brief and relevant."</em></p>
</li>
<li><p><strong>30 messages in a busy group</strong> — 30 messages across 15 people destroys context. Drop to 10.</p>
</li>
</ul>
<h3>Variety 3: Keyword Router</h3>
<p>Avoids spam entirely — no @mention needed, but the bot only reacts to specific keywords. This is the cleanest production setup for group commands.</p>
<p><strong>How it works:</strong></p>
<ul>
<li><p>Replace the single agent node with a <strong>Switch</strong> node — each branch checks for a different keyword (<code>@order</code>, <code>@help</code>, <code>@status</code>, etc.)</p>
</li>
<li><p>Each branch triggers a different workflow or prompt tailored to that command</p>
</li>
<li><p>Always add a <strong>fallback branch</strong> that replies with the list of valid keywords the group can use</p>
</li>
</ul>
<p><strong>Why this avoids spam:</strong></p>
<ul>
<li><p>No @mention required — clean UX</p>
</li>
<li><p>Bot stays silent unless it sees a valid keyword</p>
</li>
<li><p>Switch node routes to the right agent in seconds</p>
</li>
</ul>
<h3>The Workflow JSON</h3>
<p>Copy the JSON below → In n8n: Workflows → three dots menu → Import from JSON → paste → import. Then re-select credentials in every node with a key icon.</p>
<pre><code class="language-json">{
  "name": "WhatsApp AI Bot: personal chat + group @agent (OpenWA + Ollama)",
  "nodes": [
    {
      "parameters": {
        "content": "## Reply to WhatsApp messages with a local AI agent using OpenWA and Ollama\n\n**Who it's for:** anyone who wants a WhatsApp AI assistant without paying for the Meta Cloud API, and without sending chats to a cloud LLM.\n\n**How it works**\n1. [OpenWA](https://github.com/rmyndharis/OpenWA), a free self-hosted WhatsApp API gateway, posts every incoming message to the webhook.\n2. **Should the bot reply?** passes only two kinds of message: a direct message from the contact you name, or a group message that contains your mention keyword (default `@agent`). Everything else is ignored, so the bot never floods a group.\n3. An AI Agent answers with a local Ollama model and remembers the last 30 messages per chat.\n4. The reply goes back to the same chat through OpenWA's `send-text` endpoint.\n\n**Setup**\n1. Run OpenWA and connect a WhatsApp session (scan the QR code).\n2. Run Ollama and pull a model: `ollama pull llama3.2:3b`.\n3. Fill in **Bot settings**: OpenWA URL, the contact's WhatsApp profile name (`pushName`, not the name saved in your phone), the mention keyword and the system prompt.\n4. Create a **Header Auth** credential: name `X-API-Key`, value = your OpenWA API key. Select it in **Send reply via OpenWA**. Select your Ollama credential in **Ollama chat model**.\n5. In OpenWA \u2192 Webhooks, add this workflow's production webhook URL for the `message.received` event, then activate the workflow.\n\nFull step-by-step guide (Docker, tunnel, webhooks): [abhishekrajpurohit.in](https://abhishekrajpurohit.in/blog/complete-guide-self-hosting-n8n-openwa-whatsapp-api)",
        "height": 820,
        "width": 460,
        "color": 1
      },
      "id": "8b095b9c-9890-4a10-af04-24997d00b4bd",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -480,
        -80
      ]
    },
    {
      "parameters": {
        "content": "## 1. Receive and filter\nWebhook from OpenWA, your settings, and the reply gate.",
        "height": 420,
        "width": 660,
        "color": 7
      },
      "id": "58764b41-3b84-4e3b-a3cf-dc8a1153b2be",
      "name": "Step 1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        160
      ]
    },
    {
      "parameters": {
        "content": "## 2. Generate the reply\nLocal Ollama model with per-chat memory.",
        "height": 560,
        "width": 420,
        "color": 7
      },
      "id": "248f5cd2-78bb-49d1-9d71-f6f5bd03bd42",
      "name": "Step 2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        700,
        160
      ]
    },
    {
      "parameters": {
        "content": "## 3. Send it back\nPOST to OpenWA send-text.",
        "height": 420,
        "width": 300,
        "color": 7
      },
      "id": "54fce74c-c9c1-4fa4-9ea5-9d60eccd77c9",
      "name": "Step 3",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1160,
        160
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "wa-webhook",
        "options": {}
      },
      "id": "e27fd98d-e8f1-4485-9a54-832d531a723f",
      "name": "Receive WhatsApp message",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        60,
        340
      ],
      "webhookId": "191913a8-511e-4cd0-9418-1db18069c22b"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "76da82cf-df57-4552-990b-04e6da77a558",
              "name": "openwaBaseUrl",
              "value": "http://openwa-api:2785",
              "type": "string"
            },
            {
              "id": "6a30d48f-01b3-448b-b675-216446439b81",
              "name": "allowedContactName",
              "value": "YourContactName",
              "type": "string"
            },
            {
              "id": "573736ee-8c63-422c-95c8-862cc30d8bcb",
              "name": "mentionKeyword",
              "value": "@agent",
              "type": "string"
            },
            {
              "id": "33dae449-4f9f-42d1-8470-0b3304af59ba",
              "name": "systemPrompt",
              "value": "You are a helpful assistant replying on WhatsApp. Keep answers short and direct. Use numbered lists for steps. If you are unsure, say so.",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "ad52ad92-858d-4057-a6bc-eff3edc183b4",
      "name": "Bot settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        260,
        340
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "254e0a11-59c1-4855-b343-596d716e7626",
              "leftValue": "={{ $json.body.data.isGroup !== true &amp;&amp; $json.body.data.contact?.pushName === $json.allowedContactName }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            },
            {
              "id": "fc87d657-3596-430c-9a34-09e0b643cf13",
              "leftValue": "={{ $json.body.data.isGroup === true &amp;&amp; String($json.body.data.body || '').includes($json.mentionKeyword) }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "or"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "id": "03b18476-46f0-4245-809b-1c856e1cc7e2",
      "name": "Should the bot reply?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        460,
        340
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.body.data.body }}",
        "options": {
          "systemMessage": "={{ $json.systemPrompt }}"
        }
      },
      "id": "05816bd5-2b36-400a-a3ad-e16abc6f97ab",
      "name": "AI agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        760,
        340
      ]
    },
    {
      "parameters": {
        "model": "llama3.2:3b",
        "options": {}
      },
      "id": "ea821e70-dd40-49c9-8369-b375b74ba86a",
      "name": "Ollama chat model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOllama",
      "typeVersion": 1,
      "position": [
        740,
        580
      ]
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Receive WhatsApp message').item.json.body.data.chatId }}",
        "contextWindowLength": 30
      },
      "id": "b74115f2-29b2-4ac1-b722-4e670b95331d",
      "name": "Chat memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        920,
        580
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Bot settings').item.json.openwaBaseUrl }}/api/sessions/{{ $('Receive WhatsApp message').item.json.body.sessionId }}/messages/send-text",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ chatId: $('Receive WhatsApp message').item.json.body.data.chatId, text: $json.output }) }}",
        "options": {}
      },
      "id": "3e214463-c8c9-4e22-83b9-3d68d9f7b651",
      "name": "Send reply via OpenWA",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1240,
        340
      ]
    }
  ],
  "connections": {
    "Receive WhatsApp message": {
      "main": [
        [
          {
            "node": "Bot settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Bot settings": {
      "main": [
        [
          {
            "node": "Should the bot reply?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Should the bot reply?": {
      "main": [
        [
          {
            "node": "AI agent",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "AI agent": {
      "main": [
        [
          {
            "node": "Send reply via OpenWA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ollama chat model": {
      "ai_languageModel": [
        [
          {
            "node": "AI agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Chat memory": {
      "ai_memory": [
        [
          {
            "node": "AI agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  }
}
</code></pre>
<p>This covers Variety 1 and Variety 2 with one <strong>Should the bot reply?</strong> gate; add a Switch after it for Variety 3. After importing: select a Header Auth credential (<code>X-API-Key</code> = your OpenWA key) in <strong>Send reply via OpenWA</strong> and your Ollama credential in <strong>Ollama chat model</strong>. In <strong>Bot settings</strong>, change <code>YourContactName</code> to the actual <code>pushName</code> from your webhook payload. Activate the workflow.</p>
<hr />
<h2>You're Done</h2>
<p>At this point you have:</p>
<ul>
<li><p>n8n running at <a href="https://n8n.yourdomain.com">https://n8n.yourdomain.com</a> behind a Cloudflare Tunnel with zero open firewall ports</p>
</li>
<li><p>OpenWA running locally, WhatsApp session authenticated and ready</p>
</li>
<li><p>A management script to start/stop/monitor the whole stack</p>
</li>
<li><p>Backups of all OpenWA data on a 7-day rotation</p>
</li>
<li><p>A working WhatsApp AI workflow ready to extend</p>
</li>
</ul>
<p>The stack is not the hard part. The hard part is the system prompt and deciding what to actually automate. Start simple — pick one variety, ship it, let people use it, fix what breaks.</p>
<p><strong>Looking for the OpenWA API reference?</strong> → <a href="https://abhishekrajpurohit.in/blog/openwa-api-reference-n8n-workflows">OpenWA API Reference for n8n Workflows</a> — every endpoint with exact n8n HTTP Request node configuration.</p>
]]></content:encoded></item></channel></rss>