<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hussain Mirza</title>
    <description>The latest articles on DEV Community by Hussain Mirza (@hussain_mirza_86996572c15).</description>
    <link>https://dev.to/hussain_mirza_86996572c15</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3976062%2F0e4e1610-c629-4357-a75c-ab0a69ed1436.png</url>
      <title>DEV Community: Hussain Mirza</title>
      <link>https://dev.to/hussain_mirza_86996572c15</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hussain_mirza_86996572c15"/>
    <language>en</language>
    <item>
      <title>How I Built a WhatsApp AI Assistant That Answers Questions From a Live Database</title>
      <dc:creator>Hussain Mirza</dc:creator>
      <pubDate>Tue, 09 Jun 2026 13:19:25 +0000</pubDate>
      <link>https://dev.to/hussain_mirza_86996572c15/how-i-built-a-whatsapp-ai-assistant-that-answers-questions-from-a-live-database-34if</link>
      <guid>https://dev.to/hussain_mirza_86996572c15/how-i-built-a-whatsapp-ai-assistant-that-answers-questions-from-a-live-database-34if</guid>
      <description>&lt;p&gt;I recently built a system that lets an entire organization query their live database through WhatsApp — no app, no dashboard, no training required. Just type a question and get an answer instantly.&lt;/p&gt;

&lt;p&gt;Here's exactly how I built it, what tools I used, and what I learned.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Fayz e Husayni is a community organization in Karachi that manages ticketing, finance, properties, and IT systems across multiple departments. Their data was scattered across spreadsheets and manual processes. Team members had to contact specific people just to get basic information like "how many confirmed tickets are there?" or "which properties need maintenance?"&lt;/p&gt;

&lt;p&gt;The goal: let any authorized member ask questions in plain English through WhatsApp and get instant answers from live data.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;A WhatsApp AI bot built on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;n8n&lt;/strong&gt; — workflow automation and middleware&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAHA&lt;/strong&gt; — self-hosted WhatsApp HTTP API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Groq (LLaMA 3.1)&lt;/strong&gt; — free AI inference&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets&lt;/strong&gt; — mock database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; — container orchestration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Member sends message in WhatsApp group
        ↓
WAHA captures the message via webhook
        ↓
n8n receives it and filters by group ID
        ↓
Fetches live data from all 4 department sheets
        ↓
Is it a report request? Yes → format report
                         No → send to Groq AI with data context
        ↓
Response sent back to WhatsApp group
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1 — Setting Up WAHA and n8n with Docker
&lt;/h2&gt;

&lt;p&gt;Both services run as Docker containers. The &lt;code&gt;docker-compose.yml&lt;/code&gt; is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;n8n&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.n8n.io/n8nio/n8n&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5678:5678"&lt;/span&gt;
  &lt;span class="na"&gt;waha&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devlikeapro/waha&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WAHA gives you a dashboard at &lt;code&gt;localhost:3000/dashboard&lt;/code&gt; where you scan a QR code to connect your WhatsApp number. Once connected, it fires a webhook on every incoming message.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Receiving Messages in n8n
&lt;/h2&gt;

&lt;p&gt;In n8n, a Webhook node listens at &lt;code&gt;/webhook/whatsapp-incoming&lt;/code&gt;. WAHA posts every incoming message to this URL.&lt;/p&gt;

&lt;p&gt;The first thing I do is filter by group ID — the bot should only respond to messages from the designated Fayz group, not personal chats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;IF &lt;span class="nv"&gt;$json&lt;/span&gt;.body.payload.from &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"120363428000042923@g.us"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single filter keeps personal conversations completely private.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Fetching Live Data
&lt;/h2&gt;

&lt;p&gt;Instead of multiple Google Sheets nodes (which hit rate limits), I wrote a single Code node that fetches all 4 sheets in one execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ticketing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Finance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Properties&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheetName&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sheets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://sheets.googleapis.com/v4/spreadsheets/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/values/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sheetName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;httpRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sheetName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs once and gives the AI complete context across all departments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — The AI Layer
&lt;/h2&gt;

&lt;p&gt;I used Groq's free tier with LLaMA 3.1 8B. The prompt is the key — it needs strict formatting rules otherwise the AI gives verbose responses that look terrible in WhatsApp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a data assistant for Fayz e Husayni.
Answer ONLY from the data provided.
Max 4 lines. No calculation steps. End with one emoji.
Never volunteer unrequested information.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example-based formatting in the prompt made a huge difference — showing the AI exactly how a correct response looks forced it to follow the format consistently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Report Generation
&lt;/h2&gt;

&lt;p&gt;When someone types "send me finance report", the system detects the word "report" and routes to a different flow that formats the data as a structured text report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📊 Finance Report
Generated: 09/06/2026
─────────────────
1. Batch A Collections — PKR 510,000 — Received
2. Batch B Collections — PKR 270,000 — Partial
...
─────────────────
Total records: 8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No AI needed for this — pure data formatting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 — Error Handling
&lt;/h2&gt;

&lt;p&gt;If Groq is down or the Sheets API fails, the bot sends a friendly message instead of dying silently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠️ Sorry, I'm unable to process your request right now. Please try again in a moment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Small detail, big difference in user experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;WAHA Core is limited.&lt;/strong&gt; File sending, Business WhatsApp, and multiple sessions require WAHA Plus. Plan your budget accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Groq free tier burns fast during development.&lt;/strong&gt; Use it sparingly in testing — each workflow test consumes quota. I switched between accounts twice in one day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt formatting matters more than the model.&lt;/strong&gt; LLaMA 3.1 with good formatting instructions outperformed my initial Gemini 2.5 attempts. Concrete examples in the prompt are more effective than abstract rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;n8n's Code node limitations are real.&lt;/strong&gt; No &lt;code&gt;fetch&lt;/code&gt;, no &lt;code&gt;$http&lt;/code&gt; — use &lt;code&gt;this.helpers.httpRequest&lt;/code&gt; for external calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production Requirements
&lt;/h2&gt;

&lt;p&gt;This prototype runs on a local machine. For production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPS server (~$5-10/month) for 24/7 uptime&lt;/li&gt;
&lt;li&gt;WAHA Plus ($19/month) for Business WhatsApp and file delivery&lt;/li&gt;
&lt;li&gt;Dedicated WhatsApp number&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Full source code, workflow JSON, docker-compose, and setup guide:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/rajjcreationss-hub/unified-data-intelligence-system" rel="noopener noreferrer"&gt;github.com/rajjcreationss-hub/unified-data-intelligence-system&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Built this for a client and planning to productize it as a template for other organizations. If you're building something similar or have questions, drop them in the comments.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>whatsapp</category>
      <category>ai</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
