How to Build an AI Document Chatbot with n8n, Google Drive and Supabase

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

You’ve got dozens of PDFs, reports, and spreadsheets scattered across Google Drive. Finding that one specific number or policy buried on page 47? That’s where your afternoon goes.

What if your documents could talk? Imagine asking your chatbot “What was our Q3 revenue projection?” and getting an instant, accurate answer pulled from your actual files, not a hallucination. That’s the power of RAG (Retrieval Augmented Generation).

In this guide, we’re building exactly that: an AI document chatbot that automatically processes everything you upload to Google Drive, makes it searchable, and lets you chat with it using natural language. We’ll use n8n to orchestrate the whole pipeline, Google Drive for storage, Google Gemini to enhance your content, OpenAI for embeddings and chat, and Supabase as our vector database.

By the end, you’ll have a workflow that doesn’t just read documents, it understands them contextually and retrieves the most relevant information on demand.

What You’ll Build

This workflow has two halves that work together seamlessly:

The Document Processing Pipeline watches your Google Drive folder, automatically grabs new files (PDFs, CSVs, Google Docs), extracts the text, enhances it with AI smarts, splits it into searchable chunks, generates vector embeddings, and stores everything in a Supabase database.

The Chat Interface is where users interact. When someone sends a message, an AI agent searches your vector database for the most relevant chunks, reads them in context, and generates an intelligent answer grounded in your actual documents.

The magic here is that the chatbot doesn’t just guess. It retrieves and reasons over your real data. No more “I don’t have that information.” Your documents become your knowledge base.

How It Works: High-Level Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    Document Processing Pipeline                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Google Drive      Extract      AI Enhancement    Vector Store   │
│   Folder          Text               │              (Supabase)   │
│      │               │         Generate:               │         │
│      │               │         • Metadata       • Embeddings     │
│      │               │         • Context        • Chunks         │
│      └──────────────┬┴────────────┬────────────────┤             │
│                     │             │                │             │
│            (Monitor every minute)                  │             │
│                                                    └─────────┐   │
│                                                              │   │
├──────────────────────────────────────────────────────────────┼──┤
│                   Chat Interface                             │  │
├──────────────────────────────────────────────────────────────┼──┤
│                                                              │  │
│  User Message     Search      Retrieve      Generate        │  │
│      │            Vector DB   Context       Answer           │  │
│      │               │            │            │             │  │
│      └───────────────┼────────────┼────────────┤             │  │
│                      │            │            │             │  │
│            (Powered by OpenAI GPT-4o-mini) ←──┘             │  │
│                                               │             │  │
│                            Sourced from Supabase────────────┘  │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

What You’ll Need

Before we start building, gather these:

  • n8n account: cloud or self-hosted, either works
  • Google account: for Google Drive and Google Gemini API access
  • OpenAI API key: for embeddings and the GPT-4o-mini chat model
  • Supabase account: free tier is fine; you’ll create a PostgreSQL database with the pgvector extension
  • A Google Drive folder: dedicated to documents you want indexed
  • Sample documents: PDFs, CSVs, or Google Docs to test with

Estimated setup time: 15 to 20 minutes. The workflow itself is provided as a template you can import directly.

Part 1: Document Processing Pipeline

This is the intake system. Every minute, it checks for new files and prepares them for retrieval.

1 Google Drive Trigger

The entry point. Configure this node to:

  • Connect your Google account via OAuth2
  • Select the Google Drive folder where you’ll upload documents
  • Set the poll frequency to every 1 minute (or adjust to your needs)

This trigger fires whenever n8n detects new or modified files in that folder. You can filter by file type if you want (e.g., only PDFs and CSVs).

2 Loop Over Items

Since Google Drive might return multiple files, we process them one by one. This node iterates through the list so each file gets its own execution path through the pipeline.

3 Set File ID

A simple expression node that extracts and stores the Google Drive file ID. We’ll use this later to track which document a chunk came from. Use an expression like: $json.fileId

4 Download File

Downloads the actual file from Google Drive. Important: Google Docs are automatically converted to PDF during download, so you get consistent text extraction across all document types.

5 Switch Node (MIME Type Router)

Routes the file to the right text extraction method based on its type:

  • If MIME type is application/pdf → send to PDF extractor
  • If MIME type is text/csv → send to CSV extractor

This branching logic ensures each file type is handled with the right tool.

6 Extract from PDF / Extract from CSV

Two parallel nodes (one for each file type) that pull raw text:

  • PDF extraction: Use n8n’s built-in PDF node or a code snippet to extract text, preserving paragraph structure where possible
  • CSV extraction: Parse the CSV, convert rows to readable text (e.g., “Row 1: Name=Jennifer Chen, Department=Marketing, Salary=92000”)

Both outputs feed into the next stage as plain text.

💡

Rough PDFs or image-heavy documents? Consider adding an OCR step here using services like Google Vision API. It adds latency but ensures no text is missed. Check our template marketplace for OCR-enhanced variants.

7 Document Data (JSON Wrapper)

Wraps the extracted text in a structured JSON object:

{
  "text": "[extracted text here]",
  "file_id": "[Google Drive ID]",
  "file_name": "[original filename]",
  "mime_type": "[pdf or csv]"
}

This structure carries metadata through the pipeline, so we always know where each chunk originated.

8 Create Metadata (Google Gemini)

Before we split text into chunks, we generate high-level metadata using Google Gemini. Send the full document text to Gemini with a prompt like:

“Read this document and provide: (1) A concise title (5 to 10 words), (2) A brief description (1 to 2 sentences) explaining what this document contains and who should read it. Output as JSON: {title, description}”

Gemini returns structured metadata that will be attached to every chunk from this document. This is crucial: when the chatbot retrieves chunks, it shows the user which document they came from and why it’s relevant.

9 Split Into Chunks (Code Node)

Raw documents are too large to process as embeddings. This node intelligently splits text into 1000-character chunks with 200-character overlap, respecting sentence and paragraph boundaries:

// Simplified chunking logic (pseudocode)
const chunkSize = 1000;
const overlapSize = 200;
let chunks = [];
let currentChunk = '';

// Split on sentences/paragraphs first
const sentences = text.split(/(?<=[.!?])\s+/);

for (let sentence of sentences) {
  if ((currentChunk + sentence).length > chunkSize) {
    chunks.push(currentChunk.trim());
    currentChunk = currentChunk.slice(-overlapSize) + sentence;
  } else {
    currentChunk += ' ' + sentence;
  }
}
if (currentChunk) chunks.push(currentChunk.trim());

return chunks;

This ensures that no information is lost at chunk boundaries, and related concepts stay together.

10 Split Out

Converts the chunks array into separate items so each chunk flows through its own execution. If a document produces 50 chunks, this creates 50 parallel execution paths.

11 Limit (Optional)

A safety valve: limits processing to the first 20 chunks per document. Set this based on your OpenAI quota and cost tolerance. You can remove it for unlimited processing.

12 Process Context (Google Gemini)

Each chunk is sent to Gemini for contextual enhancement. Prompt:

“Here is an excerpt from a larger document. Enhance this excerpt with additional context that would help someone retrieve and understand it later. Add brief background, define jargon, and clarify pronouns. Keep it to 1 to 2 sentences. Return the enhanced excerpt.”

Example: If a chunk says “Q3 revenue up 15%”, Gemini might enhance it to “Q3 2025 revenue increased 15% compared to Q2 2025, reaching $4.2M.” Now when the chatbot retrieves this chunk for a question like “What was our revenue trend?”, it has full context.

13 Summarize (Concatenate)

Combines the enhanced chunks back into a single string, separated by a delimiter (e.g., \n---\n). This serves as the final text to embed. The structure is:

[Enhanced Chunk 1]
---
[Enhanced Chunk 2]
---
[Enhanced Chunk 3]

14 Add Data to Supabase Vector Store

The final step: store everything in Supabase. This node does three things:

  1. Generates OpenAI embeddings for each chunk (using text-embedding-3-small model)
  2. Prepares metadata (file_id, title, description, chunk index)
  3. Stores in Supabase with the embedding vector, content, and metadata

The node uses Supabase’s built-in vector support. Each row in your documents table looks like:

  • content: The enhanced chunk text
  • embedding: The 1536-dimensional vector from OpenAI
  • metadata: JSON with file_id, title, description, chunk_index

Once this completes, your document is fully indexed and searchable.

Part 2: Chat Interface

Now users can ask questions and get answers grounded in their documents.

1 When Chat Message Received

This is n8n’s chat trigger. Users send messages through the n8n UI (or via webhook if you embed it elsewhere), and each message fires this trigger. The input includes the user’s question and conversation history.

2 AI Agent (OpenAI)

The brain of the system. Configure an n8n AI Agent node with:

Model: GPT-4o-mini (fast, cost-effective, smart enough for RAG retrieval)

System Prompt:

“You are an internal company knowledge assistant. Your job is to answer questions about company documents. Always search the Vector Database first using the provided tool. Retrieve the most relevant document excerpts. Ground your answer in what you found. Never guess or make up information. If you cannot find relevant information, say so honestly.”

Memory: Enable Simple Memory with a 10-message limit so the chatbot remembers recent context within a conversation.

Tools: Attach the Supabase Vector Store as a tool. This gives the agent the ability to search for relevant chunks when answering a question.

When the user asks “What’s our return policy?”, the agent:

  1. Takes the question
  2. Calls the Vector Store tool with an embedding of the question
  3. Retrieves the top 20 most similar chunks
  4. Reads those chunks
  5. Generates a coherent answer citing the relevant sections

This is RAG in action: retrieval + augmented generation.

The Data Structure: Supabase Setup

Your vector database needs the right schema. In Supabase, run this SQL:

-- Enable pgvector extension
CREATE EXTENSION IF NOT EXISTS vector;

-- Create documents table
CREATE TABLE public.documents (
  id bigserial PRIMARY KEY,
  content text NOT NULL,
  metadata jsonb,
  embedding vector(1536),
  created_at timestamp with time zone DEFAULT now()
);

-- Create an index for faster vector searches
CREATE INDEX documents_embedding_idx
  ON documents
  USING ivfflat (embedding vector_cosine_ops)
  WITH (lists = 100);

-- Create an index on metadata for filtering
CREATE INDEX documents_metadata_idx
  ON documents
  USING GIN (metadata);

Explanation:

  • content: The chunk text itself
  • metadata: JSONB storing file_id, title, description, chunk_index
  • embedding: The 1536-dimensional vector from OpenAI’s embedding model
  • The ivfflat index speeds up similarity searches across millions of chunks

Full System Flow Diagram

START
  │
  ├─► Google Drive Trigger (every 1 min)
  │     │
  │     ├─► Loop Over Items
  │     │     │
  │     │     ├─► Extract File ID
  │     │     │
  │     │     ├─► Download File
  │     │     │
  │     │     ├─► Switch by MIME Type
  │     │     │     │
  │     │     │     ├─► PDF Branch
  │     │     │     │     ├─► Extract PDF Text
  │     │     │     │     └─► Wrap in JSON
  │     │     │     │
  │     │     │     └─► CSV Branch
  │     │     │           ├─► Parse CSV
  │     │     │           └─► Wrap in JSON
  │     │     │
  │     │     ├─► Create Metadata (Gemini)
  │     │     │     └─► Store title + description
  │     │     │
  │     │     ├─► Split into 1000-char Chunks
  │     │     │
  │     │     ├─► Split Out (iterate chunks)
  │     │     │
  │     │     ├─► Limit to 20 chunks
  │     │     │
  │     │     ├─► Process Context (Gemini)
  │     │     │     └─► Enhance each chunk
  │     │     │
  │     │     ├─► Summarize (concatenate)
  │     │     │
  │     │     └─► Add to Supabase
  │     │           ├─► Generate Embedding (OpenAI)
  │     │           └─► Store in Vector DB
  │     │
  │     └─► [Document Indexed & Searchable]
  │
  └─► Chat Interface
        │
        ├─► When Chat Message Received
        │     │
        │     └─► AI Agent (GPT-4o-mini)
        │           │
        │           ├─► Embed Question (OpenAI)
        │           │
        │           ├─► Search Supabase Vector Store
        │           │     └─► Retrieve top 20 chunks
        │           │
        │           └─► Generate Answer
        │                 └─► Return to User
        │
        └─► [Chat Interface Ready]

Testing Your Workflow

Once deployed, test it end-to-end:

  1. Upload a test document to your Google Drive folder. A PDF with company policies works great. You should see the Google Drive trigger fire within 1 minute.
  2. Monitor the workflow execution in n8n. Watch the logs as your document flows through extraction, chunking, enhancement, and embedding.
  3. Verify in Supabase. Query your documents table: SELECT COUNT(*) FROM documents; You should see rows for each chunk.
  4. Test the chat interface. Click the chat icon in n8n and ask a question about your document. For example: “What is the vacation policy?” The chatbot should retrieve relevant chunks and answer from your actual document.
  5. Refine as needed. If answers aren’t specific enough, adjust the context enhancement prompt in Step 12. If too many irrelevant chunks appear, experiment with the chunk size or overlap in Step 9.

A successful test: You ask a specific question (like “Who is responsible for budget approvals?”) and the chatbot cites the exact section of the document it found the answer in.

Frequently Asked Questions

How long does it take to index a new document?

Depends on document size. A typical 20-page PDF takes 30 to 90 seconds: extraction (5s), enhancement (20 to 60s), embedding (5 to 30s), and Supabase storage (5s). Large datasets benefit from batching and optimizing your chunk size.

What happens if I ask the chatbot about something not in my documents?

The AI agent is configured to search the Vector Database first. If no relevant chunks are retrieved, it will respond honestly: “I couldn’t find information about that in the available documents.” No hallucinations. That’s the RAG advantage.

Can I use a different LLM instead of OpenAI?

Yes. n8n supports Anthropic Claude, Google Gemini, and others. For embeddings, you’d need a provider with embedding endpoints (OpenAI, Cohere, or HuggingFace). Swap the model and API keys in the configuration. The workflow structure stays the same.

What’s the cost to run this workflow?

Rough estimates per 100 documents: Google Drive API (~$0, free tier is generous), Google Gemini API (metadata + context: ~$2 to $5), OpenAI embeddings (~$0.02 per document), GPT-4o-mini chat (~$0.015 per conversation, highly variable), Supabase hosting ($0 on free tier, $25/mo for production). Your biggest variable is chat usage and document volume.

Can I delete or update documents in the vector store?

Absolutely. Add a separate n8n workflow with a Supabase trigger that listens for deleted files in Google Drive. When a file is deleted, remove all chunks with matching file_id from the vector store. Same for updates: re-process the document and insert fresh chunks, optionally deleting old ones.

How many documents can the system handle?

Supabase’s free tier supports millions of vectors. The bottleneck is API rate limits: Google’s quota (varies), OpenAI embeddings (up to 3,500 requests/min on paid accounts), and Gemini rate limits. For enterprise scale, contact your API providers for higher quotas.

What’s Next?

You now have a functioning RAG chatbot. Here are natural next steps:

  • Deploy the chat interface publicly: Embed the n8n chat widget on a website or expose it via a webhook so external users can query your documents.
  • Add document filtering: Modify the Vector Store search to filter by department, date range, or document type. Useful if different teams have different documents.
  • Implement multi-language support: Use translation APIs to process documents in French, Spanish, German, etc. The embeddings and chat adapt automatically.
  • Create document analytics: Track which documents get queried most, what questions come up repeatedly, and use that to improve your knowledge base organization.
  • Integrate with Slack or Teams: Instead of a web chat, let employees query documents directly from Slack. n8n webhooks make this straightforward.
  • Add feedback loops: Let users rate chatbot answers (“Was this helpful?”). Log feedback to a table and use it to retrain or improve your prompts.

Ready to Deploy Your RAG Chatbot?

The complete, production-ready n8n workflow template is available right now. Import it directly into your n8n instance, configure your API keys, and start indexing documents in minutes.

Get the Template

One-click import. Full documentation included. Free to modify and extend.

Summary

You now understand the architecture of an enterprise-grade document chatbot. The two-part workflow, document processing and chat interface, works together to create a system that knows your documents intimately and answers questions with precision.

The key innovations:

  • Intelligent chunking preserves context
  • Gemini enhancement adds semantic richness
  • Vector embeddings enable semantic search (not keyword search)
  • RAG prevents hallucinations by grounding answers in actual content
  • Metadata tracking ensures users know where answers come from

Build this workflow, test it with your own documents, and watch it transform how your team accesses information. Stop searching pages manually. Start asking.

ai-chatbot
rag
n8n
google-drive
supabase
openai
vector-database
automation

How to Build an AI Self-Healing Engine for n8n (Auto-Fix Failing Workflows with Azure OpenAI)

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

Every n8n user knows the feeling: you check your automations in the morning and find three workflows sitting in failed state. One timed out, one hit a bad API response, one has a broken parameter. Now you’re spending an hour debugging instead of building. What if your n8n instance could diagnose and fix those failures itself, while you slept?

That’s exactly what this workflow does. It’s a global AI-powered error handler that hooks into n8n’s built-in error trigger, fetches the failing workflow’s full JSON, hands it to Azure OpenAI GPT-4o, and either retries the execution automatically or patches the broken parameter, then posts the result to Slack. No manual debugging, no stale failures, no wasted morning.

💡 Prefer to skip the setup? Grab the ready-made template and have your self-healing engine running in under 15 minutes.

What You’ll Build

  1. A global error listener: n8n’s Error Trigger fires the moment any workflow in your instance fails, passing you the full execution context.
  2. A self-loop guard: A Filter node prevents the engine from accidentally triggering itself if it ever fails.
  3. An AI diagnostics layer: Azure OpenAI GPT-4o reads the error message, the failed node name, and the entire workflow JSON, then decides: is this a temporary network hiccup (RETRY) or a fixable logic error (FIX)?
  4. Automatic repair: For RETRY cases, the engine waits one minute and re-runs the failed execution. For FIX cases, it patches the broken parameter directly in the workflow JSON and pushes the update via the n8n API.
  5. Slack alerts for everything: You get a Slack message for every auto-fix applied, every auto-retry queued, and every error that needs a human to look at it.

How It Works: The Big Picture

+—————————————————————————+
| AI SELF-HEALING ENGINE |
| |
| [On Workflow Error] -> [Filter: Ignore Self] -> [Get Workflow JSON] |
| | |
| [Diagnose Error (GPT-4o)] |
| +- AI Model + + Output Schema -+ |
| | |
| [Determine Action] |
| / | \ |
| RETRY FIX MANUAL |
| | | | |
| [Cool Down] [Generate [Notify Manual |
| | Patch JSON] Fix (Slack)] |
| [Retry [Update |
| Execution] Workflow] |
| [Notify Success (Slack)] |
+—————————————————————————+

What You’ll Need

  • n8n (self-hosted or cloud): access to Settings → API for an API key, and Settings → Variables to store it
  • Azure OpenAI account: with a GPT-4o deployment active (GPT-4 Turbo works too)
  • Slack workspace: with a channel designated for automation alerts
  • Build time from scratch: ~60 minutes | With template: ~15 minutes

Step-by-Step Build

Step 1: On Workflow Error (Error Trigger)

This is n8n’s built-in errorTrigger node, nothing to configure. It fires whenever any workflow encounters an unhandled error and passes the full execution context:

{
  "workflow": {
    "id": "a7b3c9d1e2f4",
    "name": "Daily Shopify Order Sync"
  },
  "execution": {
    "id": "exec_88221",
    "lastNodeExecuted": "Send to Google Sheets",
    "error": {
      "message": "The caller does not have permission to execute the requested operation."
    }
  }
}
Tip: After this workflow is live, go into each of your other workflows’ Settings and set Error Workflow to this engine. That’s how n8n routes failures here.

Step 2: Filter: Ignore Self

Compares $json.workflow.id against $workflow.id. Only passes items where the IDs differ, i.e., the failing workflow is not this engine itself. Without this, a failure in the engine would trigger an infinite loop.

Step 3: Get Workflow JSON (HTTP Request)

Fetches the full workflow definition via the n8n API so GPT-4o can read its structure.

Field Value
Method GET
URL {{ $vars.N8N_BASE_URL }}/api/v1/workflows/{{ $json.workflow.id }}
Header: X-N8N-API-KEY {{ $vars.N8N_API_KEY }}
Tip: Store your n8n base URL and API key as n8n Variables (Settings → Variables). This keeps the workflow portable across environments.

Step 4: Azure OpenAI GPT-4o + Decision Schema

The Azure OpenAI GPT-4o sub-node is the AI brain. Configure it with your Azure endpoint and API key. The Decision Schema (Structured Output Parser) forces the AI to return a predictable structure:

{
  "state": "RETRY" | "FIX",
  "diagnosis": "Human-readable explanation",
  "patch": {
    "parameterName": "broken parameter name",
    "newValue": "corrected value"
  }
}

Step 5: Diagnose Error (AI Agent)

The agent passes this prompt to GPT-4o with full context injected:

You are an n8n Senior Engineer.
Failed Workflow: {{ workflow.name }}
Error: {{ execution.error.message }}
Failed Node: {{ execution.lastNodeExecuted }}
Workflow JSON: {{ full workflow definition }}

Decide: RETRY (transient network error) or FIX (logic/parameter error).
If FIX, identify the broken parameter and provide the corrected value.

Example: if a Google Sheets node fails with “Invalid spreadsheet ID”, GPT-4o reads the workflow JSON, finds the node, and returns a FIX with the corrected documentId.

Step 6: Determine Action (Switch) + Three Paths

Output Condition Path
0: RETRY state === "RETRY" Cool Down (1 min) → Retry Execution
1: FIX state === "FIX" Generate Patch JSON → Update Workflow → Slack success
2: MANUAL Everything else Slack diagnostic alert for human review

For the FIX path, a Code node injects the AI’s corrected value into the workflow JSON, then an HTTP PUT call updates the live workflow via the n8n API. The patched node gets a visible annotation on the canvas so you can see exactly what changed.

Testing Your Workflow

  1. Create a test workflow: Schedule Trigger + HTTP Request to https://httpstat.us/500 (always returns an error).
  2. Set that test workflow’s Error Workflow to this engine.
  3. Execute the test workflow, it will fail immediately.
  4. Check your Slack channel for the diagnosis message within 30 seconds.
Issue Likely Cause Fix
Filter blocks all items Engine is its own Error Workflow Remove self-reference in Settings
401 Unauthorized on API calls API key missing or expired Regenerate key, update N8N_API_KEY variable
AI returns empty patch Error too ambiguous Normal, MANUAL path handles it
No Slack messages Wrong channel ID Right-click Slack channel → Copy Link, use last path segment

Frequently Asked Questions

Does this work on n8n Cloud or only self-hosted?

Both. You just need n8n API access, which is available on all plans. On Cloud, your base URL is something like https://yourname.app.n8n.cloud.

Can I use standard OpenAI instead of Azure OpenAI?

Yes. Swap the Azure OpenAI Chat Model sub-node for a standard OpenAI Chat Model node and connect your OpenAI API key. Everything else stays the same.

What kinds of errors can the AI actually fix automatically?

Common auto-fixable errors: malformed URL parameters, outdated document/spreadsheet IDs, wrong HTTP method, missing required headers, incorrect field names in node parameters. Network timeouts and rate limits go to the RETRY path instead.

Is it safe to let AI update my live workflows automatically?

The engine only patches the single broken parameter in the failed node, and it doesn’t restructure anything. For high-stakes workflows, you can remove the auto-update step and have the AI post the suggested fix to Slack for human approval first.

What happens if the engine itself fails?

The Filter node prevents self-loops. If the engine has its own unhandled error, it stops gracefully without triggering itself. You’ll see the failure in n8n’s execution log like any other workflow.

Can I use Telegram instead of Slack for alerts?

Yes. Replace both Slack nodes with Telegram nodes, set your bot token, and use your Telegram chat ID. The message text is identical, just paste it in.

What’s Next

  • Approval gate: Route FIX suggestions to Slack with approve/reject buttons before auto-applying.
  • Audit log: Add a Google Sheets node at each branch end to log every auto-fix and retry.
  • Frequency escalation: If the same workflow fails more than 3 times in 24 hours, escalate to a high-priority channel or send an email.
  • PagerDuty/OpsGenie integration: For critical production failures that need immediate human response.

Get the AI Self-Healing Engine Template

Stop waking up to broken workflows. The ready-made template includes the complete n8n workflow JSON, a step-by-step Setup Guide PDF, and a Credentials Guide PDF: everything you need to go from zero to running in under 15 minutes.

Buy the template → $14.99

Instant download · Works on n8n Cloud and self-hosted · Lifetime access

How to Build an AI WhatsApp Support Bot with Human Handoff Using n8n, Gemini & Supabase

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

Your support team can’t be online 24/7, but your customers expect answers at midnight just as much as noon. The problem with most WhatsApp bots is the binary choice: either the AI handles everything (and frustrates customers when it fails), or a human handles everything (and burns out your team). This n8n workflow solves it differently. The AI answers routine questions instantly, and the moment a human agent jumps in, the bot steps aside. After two hours of silence from the human side, the AI quietly resumes. You get the best of both worlds.

In this guide you’ll build a production-ready WhatsApp support system in n8n using Twilio, Google Gemini, and Supabase vector search. The bot answers questions by searching your own knowledge base documents, can look up customer records in Airtable, maintains per-conversation memory, and handles the AI/human handoff completely automatically.

Prefer to skip the build? Grab the ready-made template → and be live on WhatsApp in under an hour.

What You’ll Build

  1. A Twilio webhook listens for incoming WhatsApp messages from customers.
  2. Before the AI responds, the workflow checks a human-handoff API. If a human agent is active, the bot stays silent.
  3. After 2 hours with no human response, the AI automatically resumes handling the conversation.
  4. The AI agent (powered by Google Gemini) searches your Supabase knowledge base for accurate answers, can look up customer CRM records, and maintains full conversation context via a memory buffer.
  5. Replies go back to the customer via WhatsApp through Twilio.
  6. A separate RAG pipeline keeps the knowledge base fresh: whenever you add or update a document in a Google Drive folder, n8n re-embeds it and syncs to Supabase automatically.

How It Works: The Big Picture

┌──────────────────────────────────────────────────────────────────────────────┐
│  WORKFLOW 1: WhatsApp Chat Handler                                           │
│                                                                              │
│  [Twilio Trigger]                                                            │
│       ↓                                                                      │
│  [Check Human Handoff API]                                                   │
│       ↓                                                                      │
│  [2-Hour Filter] ──(human active + <2h)──→ STOP (human is handling it)      │
│       ↓ (no human, or 2h+ since last response)                              │
│  [Validate Text Input] ──(empty message)──→ STOP                            │
│       ↓                                                                      │
│  [WhatsApp Support Agent (Gemini)]                                           │
│       ├── tool: Knowledgebase RAG (Supabase)                                │
│       ├── tool: CRM Lookup (Airtable)                                       │
│       ├── tool: Think + Calculator                                           │
│       └── memory: Conversation Buffer (per phone number)                    │
│       ↓                                                                      │
│  [Send WhatsApp Reply via email or SMS]                                            │
└──────────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────────┐
│  WORKFLOW 2: Knowledge Base Sync (RAG Pipeline)                              │
│                                                                              │
│  [Google Drive Trigger: file created/updated]                                │
│       ↓                                                                      │
│  [Map File Fields] → [Delete Old Embeddings from Supabase]                  │
│       ↓                                                                      │
│  [Loop Over Files] → [Download from Google Drive]                           │
│       ↓                                                                      │
│  [Load + Chunk Document] → [Embed with OpenAI] → [Store in Supabase]        │
└──────────────────────────────────────────────────────────────────────────────┘

What You’ll Need

  • n8n: cloud or self-hosted (v1.30+)
  • Twilio account: with a WhatsApp-enabled number (~$20/month); Meta business verification required
  • Google Gemini API key: free tier available at aistudio.google.com
  • Supabase account: free tier works; needs the pgvector extension and a documents table
  • OpenAI API key: for generating embeddings (text-embedding-3-small; very cheap)
  • Google Drive folder: where you’ll store your .docx knowledge base files
  • Airtable base: optional, for CRM lookups; remove that tool node if not needed
  • Human handoff dashboard: a simple API that tracks which conversations are currently handled by a human agent (open-source starter available)

Build time from scratch: 3 to 5 hours. With this template: under 60 minutes.


Part 1: The WhatsApp Chat Handler

1 Twilio WhatsApp Trigger: Receive incoming messages

This webhook node fires every time a customer sends a WhatsApp message to your Twilio number. Twilio formats the payload with a data.body field containing the message text and data.from / data.to containing the WhatsApp phone numbers (prefixed with whatsapp:).

To set it up: in Twilio, go to your WhatsApp Sandbox (or verified number) → Messaging → Configure → set the webhook URL to your n8n webhook URL. In n8n, create the credential using your Twilio Account SID and Auth Token.

{
  "data": {
    "body": "Hi, I need help tracking my order #1847",
    "from": "whatsapp:+15559876543",
    "to": "whatsapp:+15551234567"
  }
}
💡

Twilio’s WhatsApp messaging has a 24-hour session window: you can only reply to customers who’ve messaged you first within the last 24 hours. For proactive messages, you’ll need pre-approved Twilio message templates.

2 Check Human Handoff Status: Query the handoff API

This HTTP Request node calls a simple API with the customer’s phone number and gets back a JSON object indicating whether a human agent has taken over, and when they last responded. The API is a lightweight dashboard you deploy separately. A Node.js/Netlify starter is included in the template package.

{
  "humanActive": true,
  "lastHumanResponseTime": "2026-04-10T14:32:00Z"
}

Set the URL parameter to https://YOUR_DASHBOARD_URL/api/check-human?phone={{ $json.data.from.trim() }}.

📌

If you don’t have a handoff dashboard yet, you can stub this out: create a Google Sheet with phone numbers and a “humanActive” column, and replace this node with a Google Sheets lookup. The filter node in Step 3 will then use that value.

3 2-Hour Handoff Filter: Let humans take over

This Filter node uses an OR condition to decide whether the AI should respond: pass the message to the AI if no human is active, OR if the last human response was more than 2 hours ago. If a human agent is actively handling the conversation and responded recently, the filter blocks the item and the AI stays silent.

The two conditions are:

Condition Value Means
humanActive false No human agent is active, AI should respond
lastHumanResponseTime after $now - 2 hours Human hasn’t responded in 2h, AI resumes

4 Validate Text Message: Screen out non-text events

Twilio also fires the webhook for image, audio, and status events. This IF node checks that $('Twilio WhatsApp Trigger').item.json.data.body is not empty, routing only text messages to the AI agent. Non-text events fall through the false branch (which ends here with no action).

5 WhatsApp Support Agent: The AI brain

This is the LangChain Agent node that orchestrates the response. It uses Google Gemini as its language model and has four tools at its disposal: the knowledge base RAG search, a CRM lookup in Airtable, a Think tool for multi-step reasoning, and a Calculator. The Conversation Memory buffer maintains the last 20 messages per phone number, giving the agent full context of the ongoing support conversation.

Customize the system message with your company name, website, support email, and the topics the bot should and shouldn’t discuss. The key constraints to keep:

  • Keep responses under 1,600 characters (WhatsApp best practice)
  • Instruct the agent to say “Let me connect you with a human agent” when it can’t resolve something, your dashboard UI lets agents pick up those flagged conversations
💡

The memory key is set to $json.phoneNumber, which means each phone number gets its own isolated conversation history. If a customer texts from two different numbers, those will be treated as separate conversations.

6 Send WhatsApp Reply: Close the loop

The Twilio node sends the AI’s output back to the customer’s WhatsApp. The from and to fields strip the whatsapp: prefix that Twilio adds to phone numbers, and toWhatsapp: true tells Twilio to route it over WhatsApp instead of SMS.


Part 2: The Knowledge Base RAG Pipeline

The RAG (Retrieval-Augmented Generation) pipeline is what makes the AI actually useful: instead of hallucinating answers, it searches your real company documents. You maintain the knowledge base by uploading .docx files to a Google Drive folder; n8n handles embedding and indexing automatically.

7 Google Drive Triggers: Detect new and updated documents

Two Google Drive Trigger nodes poll a specific folder every minute: one fires on fileCreated, the other on fileUpdated. Both feed into the same processing chain. Set the folder ID by pointing the node to the Drive folder where you’ll store your knowledge base documents (.docx format).

8 Map File Fields + Delete Old Embeddings

The Set node extracts file_id and file_name from the Drive event. Then the Supabase Delete node removes any existing embeddings for that file from the documents table. This is critical for the updated trigger path so you don’t end up with stale duplicate chunks. The delete uses a metadata filter: metadata->>file_id=eq.{{ $json.file_id }}.

9 Loop → Download → Chunk → Embed → Store

The SplitInBatches node processes files one at a time. For each file, n8n downloads the binary from Google Drive, loads it through the Document Loader (configured for .docx files), splits it into chunks using the Recursive Character Text Splitter, generates OpenAI embeddings (1536-dimension, matching the Supabase table setup), and stores the resulting vectors in Supabase.

💡

The Document Loader node embeds the file_id as metadata on each chunk. That’s how the delete step in Step 8 knows which vectors to remove when a file is updated. Don’t remove that metadata configuration.

The Data Structure

Your Supabase documents table needs to be set up with pgvector support. Here’s the required schema:

Column Type Description
id bigserial (primary key) Auto-incrementing row ID
content text The text chunk from your document
metadata jsonb Stores file_id and other chunk metadata
embedding vector(1536) The OpenAI embedding for semantic search

You also need to create a Supabase RPC function called match_documents that performs the vector similarity search. Supabase’s official n8n integration guide provides the exact SQL for this function.

📌

The embedding dimension must match exactly. This workflow uses OpenAI text-embedding-3-small with 1536 dimensions. If you switch to a different embedding model, update both the dimensions parameter in the Embeddings node AND recreate the Supabase column with the matching size.

Full System Flow

Customer sends WhatsApp message
         ↓
  Twilio Webhook (n8n)
         ↓
  Check human handoff API ─────────────── Human dashboard tracks
         ↓                                agent activity per phone
  2-hour filter
         │
         ├─── Human active & recent → SKIP (human handles it)
         │
         └─── No human / expired → continue
                   ↓
          Validate text input
                   ↓
          Gemini Agent
                   ├── Search Supabase ← (vector embeddings from your docs)
                   ├── Lookup Airtable CRM
                   └── Per-phone memory buffer
                   ↓
          Twilio: Send WhatsApp reply
                   ↓
         Customer receives answer

─── Separately (knowledge base sync) ──────────────────────────────
Upload .docx to Google Drive folder
         ↓
  n8n detects new/updated file
         ↓
  Delete old embeddings → Re-embed → Store in Supabase

Testing Your Workflow

Test the chat handler by texting your Twilio WhatsApp sandbox number from your personal phone. Ask a question that’s covered in one of your knowledge base documents and verify the answer is accurate and sourced from your content. Then test the handoff: set humanActive: true in your dashboard for your phone number and send another message, confirm the AI doesn’t respond.

Problem Likely Cause Fix
No reply received Webhook URL not set in Twilio Copy the Twilio Trigger webhook URL into Twilio console → WhatsApp → Configure
AI gives wrong answers Knowledge base not indexed Upload a .docx to the Drive folder and wait for the RAG pipeline to run
Supabase insert fails Dimension mismatch or missing pgvector Enable pgvector in Supabase dashboard; verify column is vector(1536)
Human filter not working Dashboard API returns wrong format Confirm API returns {"humanActive": true/false, "lastHumanResponseTime": "ISO date"}
Memory not persisting Session key wrong The memory node session key must be ={{ $json.phoneNumber }}, check the incoming phone field name

Frequently Asked Questions

Can I use a different AI model instead of Gemini?

Yes. The Google Gemini (AI Brain) node is just the language model sub-node connected to the agent. You can swap it for OpenAI GPT-4o, Anthropic Claude, or any LangChain-compatible model by deleting that node and connecting a different LLM sub-node. The rest of the workflow stays the same.

Do I need the Airtable CRM tool if I don’t use Airtable?

No, it’s optional. If you don’t need CRM lookups, simply delete the CRM Lookup Tool node. The agent will continue working with the remaining tools: Knowledgebase RAG, Think, and Calculator. You could replace it with a Google Sheets lookup, HubSpot, or any other CRM that has an n8n node.

What’s the human handoff dashboard and do I have to build it?

The human handoff dashboard is a small web app (or API endpoint) that lets your support agents see incoming conversations, flag themselves as “active” on a thread, and send manual replies. The original creator of this workflow has an open-source starter on GitHub. Alternatively, if you just want to test the logic first, you can hardcode humanActive: false in the HTTP Request node response and bypass the handoff check entirely.

What document formats does the knowledge base support?

The Document Loader is set to docxLoader by default, which handles .docx files. To support PDFs, switch the loader to pdfLoader. To support plain text or markdown, use textLoader. You can also run multiple RAG pipelines in parallel, each monitoring a different folder and using a different loader.

How does the 2-hour window work exactly?

When a human agent sends a message through the handoff dashboard, the dashboard records the timestamp as lastHumanResponseTime. The n8n Filter node checks if this timestamp is more than 2 hours ago. If yes, the AI is allowed to respond again. You can adjust this window by changing the hours: 2 value in the filter condition. For example, use hours: 4 for a longer human-priority window.

Can this handle images and voice messages?

Not by default. The Validate Text Message node currently passes only non-empty text bodies. To handle images, you’d add an IF branch that checks for data.mediaContentType and routes to an additional OpenAI vision node. Voice messages would require a speech-to-text step (e.g., OpenAI Whisper) before passing to the agent.

🚀 Get the AI WhatsApp Handoff Template

The template includes the complete n8n workflow JSON (clean, importable), a step-by-step Setup Guide PDF, a Credentials Guide PDF walking you through every API key, and the Supabase SQL to set up your vector store.

Buy the template → $14.99

Instant download, works on n8n Cloud and self-hosted

What’s Next

  • Add voice support: Route audio messages through OpenAI Whisper for transcription before passing to the agent.
  • Multilingual detection: Add a language detection step and switch system prompt language to match the customer’s language automatically.
  • Analytics: Log every conversation turn to a Google Sheet or Supabase table to track AI vs. human resolution rates, response times, and common questions.
  • Proactive alerts: Use n8n’s Schedule Trigger to send Twilio template messages (appointment reminders, order updates) proactively to opted-in customers.

How to Automate Zendesk Support with AI Using n8n and RAG

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

Your support team is drowning in repetitive tickets while customers wait hours for answers that already exist in your knowledge base. Password resets, shipping questions, “how do I cancel”: the same 20 questions eating up 80% of your agents’ time. What if every new Zendesk ticket got an accurate, knowledge-base-backed AI reply within seconds, and only the genuinely tricky ones reached a human?

That’s exactly what you’ll build in this guide. Using n8n, OpenAI, Supabase, and a RAG (Retrieval-Augmented Generation) pipeline, you’ll create a workflow that reads every incoming Zendesk ticket, searches your knowledge base for relevant answers, generates a professional reply, and posts it directly to the ticket, automatically. When the AI doesn’t have a confident answer, it escalates to your human team with a clear tag so nothing falls through the cracks.

Prefer to skip the setup? Grab the ready-made template and be up and running in under 10 minutes.

What You’ll Build

  1. A new ticket arrives in Zendesk, and the workflow fires instantly via webhook.
  2. The AI agent searches your Supabase-hosted knowledge base using vector similarity (RAG) and drafts a response grounded in your actual documentation.
  3. If the knowledge base has a solid answer, the AI posts the reply directly to the Zendesk ticket and tags it ai_reply.
  4. If the AI can’t find relevant information, it tags the ticket human_requested so your team picks it up, with no guessing, no hallucinations.
  5. Every conversation is stored in Postgres memory, so follow-up tickets from the same customer retain context.

How It Works: The Big Picture

The entire workflow is a single n8n pipeline with a smart routing branch at the end. Here’s the flow from ticket to resolution:

┌──────────────────────────────────────────────────────────────────────────────┐
│  AI-POWERED ZENDESK SUPPORT WITH RAG                                        │
│                                                                              │
│  [Zendesk Webhook] → [Extract Ticket Data] → [RAG AI Agent]                │
│                                                     │                        │
│                                        ┌────────────┴────────────┐          │
│                                         │  Check If Escalation    │         │
│                                        │      Needed             │          │
│                                        └────┬──────────┬─────────┘          │
│                                     YES ↓              ↓ NO                  │
│                               [Tag: human_      [Post AI Reply]             │
│                                requested]              ↓                     │
│                                                  [Tag: ai_reply]            │
│                                                                              │
│  Sub-components of the RAG AI Agent:                                        │
│  ┌──────────────────────────────────────────────┐                            │
│  │ [OpenAI GPT-4o-mini]  [Postgres Memory]      │                           │
│  │ [Knowledge Base Tool → Supabase Vectors]     │                           │
│  │ [OpenAI Embeddings]                         │                           │
│  └──────────────────────────────────────────────┘                           │
└──────────────────────────────────────────────────────────────────────────────┘
  

What You’ll Need

  • n8n: self-hosted or n8n Cloud (any plan)
  • Zendesk: any plan with API access and trigger/webhook support
  • OpenAI API key: for GPT-4o-mini (chat) and text-embedding-3-small (embeddings)
  • Supabase account: free tier works; you’ll need a project with pgvector enabled for the vector store
  • PostgreSQL database: Supabase’s built-in Postgres works, or any external Postgres instance for conversation memory
  • Your knowledge base content: FAQ articles, product docs, troubleshooting guides already written and ready to embed

Estimated build time: 45 to 60 minutes from scratch, or under 10 minutes with the template.

Part 1: Receiving and Parsing Zendesk Tickets

1 Receive New Ticket (Webhook)

This node listens for incoming HTTP POST requests from Zendesk. Every time a new ticket is created, Zendesk’s trigger sends the ticket data to this webhook URL.

  1. Add a Webhook node to your canvas.
  2. Set the HTTP Method to POST.
  3. Set the Path to zendesk-new-ticket (or any slug you prefer).
  4. Copy the generated webhook URL, which you’ll paste this into Zendesk’s trigger configuration.

When a ticket arrives, the webhook receives a JSON payload like this:

{
  "body": {
    "ticket_id": "48291",
    "ticket_status": "new",
    "requester_name": "Sarah Thompson",
    "requester_email": "sarah.thompson@gmail.com",
    "subject": "Can't reset my password",
    "description": "Hi, I've been trying to reset my password for the last hour but the reset email never arrives. I've checked spam. Can you help?"
  }
}
💡

Tip: In Zendesk, go to Admin Center → Objects and rules → Triggers. Create a trigger that fires when “Ticket is Created” and set the action to “Notify active webhook” with your n8n webhook URL. Include ticket_id, subject, description, requester_name, requester_email, and ticket_status as JSON fields in the webhook body.

2 Extract Ticket Data (Set Node)

The raw webhook payload is nested inside body. This Set node extracts the fields you need into clean, top-level variables that the rest of the workflow can reference easily.

  1. Add a Set node after the Webhook.
  2. Create assignments for: ticket_id, ticket_status, requester_name, requester_email, subject, description, and timestamp.
  3. For description, use the expression {{ ($json.body.description || '').split('\n\n').slice(-1)[0].trim() }}, which strips quoted reply chains and keeps only the customer’s latest message.
  4. For timestamp, use {{ $now.format('yyyy-MM-dd HH:mm') }} so you have a record of when the workflow processed the ticket.

After this node, the data flowing forward looks like:

{
  "ticket_id": "48291",
  "ticket_status": "new",
  "requester_name": "Sarah Thompson",
  "requester_email": "sarah.thompson@gmail.com",
  "subject": "Can't reset my password",
  "description": "I've been trying to reset my password for the last hour but the reset email never arrives. I've checked spam. Can you help?",
  "timestamp": "2026-04-10 14:23"
}

Part 2: The RAG AI Agent

This is the brain of the workflow. The AI Agent node connects to three sub-components: a language model (GPT-4o-mini), a conversation memory (Postgres), and a knowledge base retrieval tool (Supabase vector store). Together, they form a RAG pipeline that grounds every response in your actual documentation.

3 Generate AI Response (AI Agent)

The Agent node receives the customer’s ticket and orchestrates the entire reasoning process. It decides whether to search the knowledge base, reads the results, and composes a professional reply.

  1. Add an AI Agent node.
  2. Set the Prompt Type to Define.
  3. Set the input text to:
    Customer: {{ $json.requester_name }}
    Subject: {{ $json.subject }}
    Message: {{ $json.description }}
  4. In the System Message, paste the following instructions that tell the agent how to behave:
You are a professional, empathetic customer support agent. Your job is to help
customers by answering their questions accurately using ONLY the information
from the retrieved knowledge base documents.

Rules:
1. Always be polite, professional, and concise.
2. If the knowledge base contains a clear answer, provide it with step-by-step
   instructions when appropriate.
3. If the knowledge base does NOT contain relevant information, respond with
   EXACTLY: "I will escalate this to our support team who will get back to
   you shortly."
4. Never make up information. Never guess. Only use verified KB content.
5. Sign off with: "Best regards, Support Team"
📌

The exact escalation phrase, “will escalate this” / “get back to you shortly”, is critical. The routing node downstream checks for these phrases to decide whether to post the reply or tag for human review. Don’t change this wording unless you also update the IF condition.

4 OpenAI Chat Model (Sub-node)

This is the language model that powers the agent’s reasoning. It connects to the Agent node’s ai_languageModel input.

  1. Add an OpenAI Chat Model node below the Agent.
  2. Select gpt-4o-mini as the model: it’s fast, cheap, and accurate enough for support responses.
  3. Set temperature to 0.3: low enough to keep answers factual, high enough to sound natural.
  4. Set max tokens to 1024, more than enough for a support reply.
  5. Connect your OpenAI API credential.
💡

Tip: If you need higher-quality reasoning for complex product questions, swap to gpt-4o. The cost goes up roughly 10x per token, but accuracy improves noticeably for technical troubleshooting.

5 Ticket Conversation Memory (Postgres)

This memory node stores the conversation history for each ticket ID in a Postgres table called zendesk_ticket_histories. If a customer sends a follow-up message on the same ticket, the AI remembers what was discussed before.

  1. Add a Postgres Chat Memory node.
  2. Set the table name to zendesk_ticket_histories.
  3. Set the Session Key to {{ $('Extract Ticket Data').item.json.ticket_id }}: this groups messages by ticket.
  4. Connect your Postgres credential (Supabase’s built-in Postgres works perfectly here).

The table is created automatically on first run. It stores each message exchange so the agent has full context for multi-message tickets.

6 Knowledge Base Retrieval Tool (Vector Store)

This is the RAG component. The tool searches your Supabase vector store for the most relevant knowledge base articles and feeds them to the AI agent as context.

  1. Add a Vector Store Tool node and name it “Retrieve Knowledge Base”.
  2. Set the tool name to knowledge_base.
  3. Set Top K to 5: the agent will receive the 5 most relevant document chunks.
  4. Write a clear description: “Contains all company knowledge base articles, FAQs, product documentation, and troubleshooting guides.”

7 Supabase Vector Store + OpenAI Embeddings

The Vector Store Tool needs two sub-components: a vector store (where your documents live) and an embedding model (to convert the search query into a vector).

  1. Add a Supabase Vector Store node. Set the table to documents and the query function to match_documents.
  2. Add an OpenAI Embeddings node. Set the model to text-embedding-3-small with 1536 dimensions.
  3. Wire the Embeddings node into the Vector Store’s ai_embedding input.
  4. Wire the Vector Store into the Retrieve Knowledge Base tool’s ai_vectorStore input.
💡

Tip: To populate your Supabase vector store, create a separate n8n workflow that reads your KB articles (from Google Docs, Notion, or markdown files), splits them into chunks of ~500 tokens, generates embeddings with the same model (text-embedding-3-small), and inserts them into the documents table. This only needs to run once (or whenever you update your docs).

Part 3: Smart Routing and Zendesk Actions

After the AI generates a response, the workflow needs to decide: was the response a confident answer, or did the AI punt because it couldn’t find relevant information?

8 Check If Escalation Needed (IF Node)

This IF node inspects the AI’s output text. If it contains the escalation phrases (“will escalate this” or “get back to you shortly”), the ticket goes to the human escalation path. Otherwise, it proceeds to auto-reply.

  1. Add an IF node after the AI Agent.
  2. Set the condition combinator to OR.
  3. Condition 1: {{ $json.output }} contains will escalate this
  4. Condition 2: {{ $json.output }} contains get back to you shortly
  5. Set case sensitivity to false.

The True branch (escalation needed) goes to the human tagging node. The False branch (AI answered) goes to the reply-posting node.

9 Tag as Human Escalation (True Branch)

When the AI can’t find an answer, this HTTP Request node calls the Zendesk API to tag the ticket with human_requested and ai_escalated. Your support team can create a Zendesk view filtered by these tags to see exactly which tickets need human attention.

  1. Add an HTTP Request node on the True output.
  2. Set Method to PUT.
  3. Set URL to: https://YOUR_ZENDESK_DOMAIN.zendesk.com/api/v2/tickets/{{ $('Extract Ticket Data').first().json.ticket_id }}/tags.json
  4. Set the JSON body to: { "tags": ["human_requested", "ai_escalated"] }
  5. Set authentication to Predefined Credential Type → Zendesk API.

10 Post AI Reply to Ticket (False Branch)

When the AI has a confident answer, this node posts it as a public comment on the Zendesk ticket and sets the ticket status to “pending” (awaiting customer confirmation).

  1. Add an HTTP Request node on the False output.
  2. Set Method to PUT.
  3. Set URL to: https://YOUR_ZENDESK_DOMAIN.zendesk.com/api/v2/tickets/{{ $('Extract Ticket Data').first().json.ticket_id }}.json
  4. Set the JSON body to:
{
  "ticket": {
    "comment": {
      "body": "{{ AI agent's output text }}",
      "public": true
    },
    "status": "pending"
  }
}

The "public": true flag means the customer sees this reply in their email and in the Zendesk portal. Setting status to "pending" tells Zendesk to wait for the customer’s next response.

11 Tag as AI Reply

After posting the reply, this final node tags the ticket with ai_reply and auto_resolved. This lets you track how many tickets the AI handles versus humans, and gives you data for measuring automation ROI.

  1. Add one more HTTP Request node after the reply node.
  2. Same pattern: PUT to the /tags.json endpoint.
  3. Body: { "tags": ["ai_reply", "auto_resolved"] }

The Data Structure

The workflow relies on two persistent data stores: the Supabase vector store for knowledge base content, and the Postgres table for conversation memory.

Supabase documents Table

Column Type Example Description
id bigint 1 Auto-incrementing primary key
content text To reset your password, go to Settings → Security → Change Password… The raw text chunk from your KB article
metadata jsonb {“source”: “password-reset-guide”, “section”: “steps”} Source tracking: which article and section this chunk came from
embedding vector(1536) [0.0023, -0.0119, …] Vector embedding generated by text-embedding-3-small

Postgres zendesk_ticket_histories Table

Column Type Example Description
session_id text 48291 The Zendesk ticket ID, which groups all messages for one ticket
message text {“type”:”human”,”content”:”I can’t reset my password…”} Individual message in the conversation
created_at timestamp 2026-04-10 14:23:00 When the message was stored
📌

The zendesk_ticket_histories table is created automatically by n8n’s Postgres Chat Memory node on first execution. You don’t need to create it manually. The documents table and its match_documents function must be set up in Supabase before the workflow will work. See the Credentials Guide for full setup steps.

Full System Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│  END-TO-END: TICKET ARRIVES → RESOLUTION                                   │
│                                                                             │
│  Customer creates ticket in Zendesk                                        │
│       ↓                                                                     │
│  Zendesk Trigger fires webhook POST                                        │
│       ↓                                                                     │
│  [Receive New Ticket] — n8n webhook catches the request                    │
│       ↓                                                                     │
│  [Extract Ticket Data] — pull ticket_id, subject, description, etc.        │
│       ↓                                                                     │
│  [Generate AI Response] ← GPT-4o-mini + Postgres Memory                   │
│       │                  ← Knowledge Base Tool                              │
│       │                     ↑                                               │
│       │              [Supabase Vectors] ← [OpenAI Embeddings]              │
│       ↓                                                                     │
│  [Check If Escalation Needed]                                              │
│       │                                                                     │
│   YES ├──→ [Tag: human_requested] → Human agent picks up ticket            │
│       │                                                                     │
│    NO └──→ [Post AI Reply to Ticket] → Customer gets instant answer        │
│                    ↓                                                        │
│            [Tag: ai_reply] → Track AI resolution rate                      │
└─────────────────────────────────────────────────────────────────────────────┘
  

Testing Your Workflow

  1. Activate the workflow in n8n (toggle the Active switch).
  2. Create a test ticket in Zendesk with a question your knowledge base can answer, for example, “How do I reset my password?”
  3. Check the n8n execution log: you should see the webhook trigger, data extraction, AI agent processing, and the reply being posted.
  4. Open the Zendesk ticket: verify the AI’s reply appears as a public comment and the ticket has the ai_reply tag.
  5. Create a second test ticket with a question your KB does NOT cover, like “Can I get a refund for my order #12345?”
  6. Verify escalation: the ticket should have the human_requested tag and no AI reply posted.
Problem Likely Cause Fix
Webhook never fires Zendesk trigger not configured or wrong URL Double-check the webhook URL in Zendesk Admin → Triggers. Make sure the trigger condition is “Ticket is Created.”
AI returns generic/wrong answers Knowledge base not populated or embeddings mismatch Verify your documents table has content. Ensure you used text-embedding-3-small with 1536 dimensions when inserting docs.
AI always escalates Vector search returns no results Check that the match_documents Postgres function exists in Supabase and the table name matches exactly.
Reply not appearing on ticket Zendesk API auth error or wrong domain Verify your Zendesk API credential has write access and the domain in the URL matches your subdomain (e.g., mycompany.zendesk.com).
“401 Unauthorized” on tag update Zendesk credential lacks admin scope The API token needs to be created by an admin user. Go to Admin → Channels → API and generate a new token.

Frequently Asked Questions

How much does it cost to run this per ticket?

With GPT-4o-mini and text-embedding-3-small, the average cost is about $0.002 to $0.005 per ticket (input + output tokens combined). If you process 1,000 tickets per month, that’s roughly $2 to $5/month in OpenAI costs. Supabase’s free tier handles up to 500MB of vector data, which is plenty for most knowledge bases.

Can I use this with Zendesk’s free plan?

No, you need API access and the ability to create triggers/webhooks, which requires Zendesk Suite Team or higher. The trigger feature that sends webhook notifications on ticket creation is not available on the free/lite plans.

What happens if the AI gives a wrong answer?

Since the agent is configured with strict RAG-only instructions and a low temperature (0.3), hallucinations are rare. But they can happen. We recommend reviewing AI-tagged tickets weekly during the first month and adjusting your KB content where gaps appear. You can also lower the temperature to 0.1 for even more conservative responses.

Can I add more languages to the knowledge base?

Yes. OpenAI’s embedding model supports 100+ languages natively. If your KB articles are in Spanish or French, the vector search will still work, since the embedding model handles cross-lingual similarity. Just make sure your system prompt tells the agent to reply in the customer’s language.

How do I update the knowledge base when we add new articles?

Create a second n8n workflow that watches your documentation source (Google Drive, Notion, or a webhook from your CMS). When a document changes, the workflow re-chunks it, generates new embeddings, and upserts them into the Supabase documents table. This keeps your AI current without manual intervention.

Does this work with Freshdesk, Intercom, or other helpdesks?

The core RAG pipeline (AI Agent + Supabase + Embeddings) works with any helpdesk that has a webhook or API. You’d swap the Zendesk-specific webhook trigger and HTTP Request nodes for the equivalent in your helpdesk. The logic is identical, only the API endpoints change.

Get the AI Zendesk Support Template

Skip the 60-minute build. Get the complete workflow JSON, Supabase setup instructions, and a step-by-step credentials guide. Paste your API keys and go live in under 10 minutes.

Get the Template

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Add a satisfaction survey: after the AI replies, send a follow-up asking the customer to rate the response. Use the rating to fine-tune your KB.
  • Build a KB ingestion pipeline: automate the process of embedding new documentation into Supabase whenever you publish a new help article.
  • Add Slack notifications for escalations: when a ticket is tagged human_requested, send a Slack message to your support channel so agents can pick it up immediately.
  • Track AI resolution rate: build a simple dashboard (Google Sheets or Metabase) that counts ai_reply vs human_requested tags over time to measure your automation ROI.
n8n
Zendesk
OpenAI
Supabase
RAG
Customer Support
automation

How to Build an AI Chatbot That Queries Your Baserow Database with n8n

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

Your team keeps pinging you with the same questions: “What’s the status on the Carter account?” or “How many orders came in from Texas last week?” The answers already live in your database. The problem is that nobody wants to learn how to query it. What if they could just ask, in plain English, and get the answer back in seconds? That’s exactly what you’ll build in this guide: an AI chatbot powered by n8n, OpenAI GPT-4o Mini, and Baserow that lets anyone on your team talk to your data without writing a single query.

Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.

What You’ll Build

  1. A user opens the n8n chat widget and types a question like “Show me all pending orders from California.”
  2. An AI agent interprets the question, queries your Baserow database for matching records, and formats the results.
  3. The chatbot replies with a clear, readable answer: no SQL, no filters, no training needed.
  4. Follow-up questions work naturally because the chatbot remembers the conversation context.

How It Works: The Big Picture

The whole system is a single n8n workflow with four connected components. The Chat Trigger receives the user’s message, passes it to an AI Agent that decides what to look up, queries Baserow through a built-in tool, and streams the answer back, all while keeping a memory buffer so the conversation flows naturally.

┌──────────────────────────────────────────────────────────────────────┐
│  AI CHATBOT — QUERY BASEROW DATABASE                                │
│                                                                      │
│  [Chat Trigger] ──→ [Database Query Agent] ──→ Chat Response         │
│                           │       │                                  │
│                    ┌──────┘       └──────┐                           │
│                    ↓                     ↓                           │
│           [OpenAI GPT-4o Mini]   [Conversation Memory]               │
│                                         │                            │
│                                  [Query Baserow]                     │
│                                    (AI Tool)                         │
└──────────────────────────────────────────────────────────────────────┘
  

What You’ll Need

  • A self-hosted or cloud n8n instance (version 1.0 or later)
  • An OpenAI account with an API key. GPT-4o Mini costs roughly $0.15 per million input tokens, so this is very affordable
  • A Baserow account (free tier works) with at least one database table containing the data you want to query
  • About 15 minutes to set everything up from scratch

Estimated build time: 15 to 20 minutes from scratch, or under 5 minutes with the template.

Building the Chatbot: Step by Step

1 Chat Trigger (Chat Trigger Node)

This is the entry point. The Chat Trigger node creates a built-in chat widget inside n8n where users can type their questions. When someone sends a message, the node captures the text and passes it downstream.

  1. In your n8n canvas, click Add node and search for Chat Trigger.
  2. Drop it on the canvas. The default settings work, no configuration needed.
  3. This node will automatically generate a chat URL you can share, or you can embed the widget on any page.

After this node fires, the data looks like this:

{
  "chatInput": "Show me all pending orders from California",
  "sessionId": "a7f3e2b1-9c4d-4e8a-b5f6-1234567890ab"
}
💡

Tip: Each conversation gets a unique sessionId. The memory node uses this to keep separate conversation threads, so multiple people can chat at the same time without mixing up context.

2 Database Query Agent (AI Agent Node)

The AI Agent is the brain of this workflow. It receives the user’s question, decides whether it needs to query the database, calls the Baserow tool if needed, and composes a human-readable response. Think of it as a smart middleman between your user and your data.

  1. Add an AI Agent node and connect it to the Chat Trigger’s output.
  2. Open the node settings and find the System Message field under Options.
  3. Paste this system prompt (customize it to describe your specific data):
You are a helpful data assistant. Your job is to answer user questions by querying the connected Baserow database.

Rules:
1. Always use the Baserow tool to look up real data before answering.
2. If the user asks a question you cannot answer from the database, say so clearly.
3. Format numbers, dates, and lists in a readable way.
4. Keep answers concise: summarize large result sets instead of dumping raw rows.
5. If the query returns no results, suggest the user rephrase or check their filters.
💡

Tip: The more specific your system prompt is about your data schema, the better the agent performs. If your table has columns like order_status, customer_state, and total_amount, mention them in the prompt so the agent knows exactly what fields to filter on.

3 OpenAI GPT-4o Mini (Chat Model Node)

This node provides the language model that powers the agent’s reasoning. GPT-4o Mini is the sweet spot here: it’s fast, cheap (fractions of a cent per query), and accurate enough for database lookups and conversational responses.

  1. Add an OpenAI Chat Model node (@n8n/n8n-nodes-langchain.lmChatOpenAi).
  2. Connect it to the AI Language Model input on the Agent node (the bottom-left connector).
  3. Set the Model to gpt-4o-mini.
  4. Under Options, set Temperature to 0.3, you want factual, consistent answers, not creative ones.
  5. Set Max Tokens to 2048, enough for detailed responses without runaway costs.
  6. Select your OpenAI credential (or create a new one by pasting your API key).
📌

You can swap in gpt-4o for more complex reasoning, but for most database Q&A tasks, GPT-4o Mini handles the job well and costs about 10x less.

4 Conversation Memory (Buffer Window Memory Node)

Without memory, every message would be a brand-new conversation. The Buffer Window Memory node stores the last 12 exchanges so the agent understands context. When a user asks “What about New York?” right after asking about California orders, the agent knows they mean orders from New York.

  1. Add a Window Buffer Memory node.
  2. Connect it to the AI Memory input on the Agent node (the bottom-center connector).
  3. Set Session ID Type to From Input, this uses the Chat Trigger’s session ID automatically.
  4. Set Context Window Length to 12. This means the agent remembers the last 12 messages (6 pairs of user + assistant).
💡

Tip: If your users tend to ask long, multi-part questions, increase the window to 20. If you want to save on token costs, drop it to 6. The trade-off is context awareness vs. cost per conversation.

5 Query Baserow (Baserow Tool Node)

This is where the magic happens. The Baserow Tool node gives the AI Agent the ability to search, filter, and read rows from your Baserow table. The agent decides when to use it and what to search for. You just need to point it at the right table.

  1. Add a Baserow node (specifically the Baserow Tool variant for AI agents).
  2. Connect it to the AI Tool input on the Agent node (the bottom-right connector).
  3. Select your Baserow credential (API token, you’ll find this in your Baserow account settings).
  4. Set the Database ID: you can find this in your Baserow URL: https://baserow.io/database/YOUR_ID.
  5. Set the Table ID: click into any table and check the URL for the table number.

When the agent calls this tool, it gets back rows like this:

[
  {
    "id": 142,
    "order_id": "ORD-2026-0891",
    "customer_name": "James Carter",
    "customer_email": "james.carter@gmail.com",
    "customer_state": "CA",
    "order_status": "Pending",
    "total_amount": 249.99,
    "created_at": "2026-04-07"
  },
  {
    "id": 143,
    "order_id": "ORD-2026-0892",
    "customer_name": "Emily Rodriguez",
    "customer_email": "emily.rodriguez@outlook.com",
    "customer_state": "CA",
    "order_status": "Pending",
    "total_amount": 89.50,
    "created_at": "2026-04-08"
  }
]

The agent then reads this data, formats it, and responds to the user with something like: “There are 2 pending orders from California: one for James Carter ($249.99) and one for Emily Rodriguez ($89.50), totaling $339.49.”

The Data Structure

Your Baserow table is the foundation of this whole system. The chatbot can only answer questions about data that exists in your table, so the schema matters. Here’s an example for an order tracking table:

Column Type Example Description
order_id Text ORD-2026-0891 Unique order identifier
customer_name Text James Carter Full name of the customer
customer_email Email james.carter@gmail.com Customer contact email
customer_state Text CA Two-letter US state code
order_status Single Select Pending Current status: Pending, Shipped, Delivered, Cancelled
total_amount Number 249.99 Order total in USD
created_at Date 2026-04-07 Date the order was placed
📌

Column names should be descriptive and snake_case. The AI agent uses column names to understand what each field contains, so customer_state works much better than col_7. If your existing table has cryptic column names, consider renaming them. It makes a real difference in answer quality.

Here’s what the data lifecycle looks like with a few sample rows:

order_id customer_name customer_state order_status total_amount
ORD-2026-0891 James Carter CA Pending $249.99
ORD-2026-0887 Sarah Thompson TX Shipped $175.00
ORD-2026-0879 Michael Chen NY Delivered $312.50

Full System Flow

┌─────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│  USER types question          n8n CHAT WIDGET                           │
│  "Show pending CA orders"  →  [Chat Trigger]                            │
│                                     │                                   │
│                                     ▼                                   │
│                            [Database Query Agent]                       │
│                             GPT-4o Mini brain                           │
│                                │         │                              │
│                      ┌─────────┘         └──────────┐                   │
│                      ▼                              ▼                   │
│             [Conversation Memory]          [Query Baserow]              │
│              12-message window              Reads table rows            │
│                                                    │                    │
│                                                    ▼                    │
│                                          ┌─────────────────┐           │
│                                          │  BASEROW TABLE   │           │
│                                          │  Orders, Leads,  │           │
│                                          │  Inventory, etc. │           │
│                                          └─────────────────┘           │
│                                                    │                    │
│                      ▼                             │                    │
│           Agent formats answer  ◄──────────────────┘                    │
│                      │                                                  │
│                      ▼                                                  │
│  USER receives      "There are 2 pending orders                         │
│  formatted answer    from California totaling $339.49"                  │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
  

Testing Your Workflow

  1. Save your workflow and make sure all four nodes are connected (Chat Trigger → Agent, with the model, memory, and tool feeding into the Agent’s sub-connectors).
  2. Click Chat in the bottom-right of the n8n canvas to open the built-in chat panel.
  3. Type a simple question: How many rows are in the table? This tests the basic connection.
  4. Ask a filtered question: Show me all orders from California This tests the agent’s ability to apply filters.
  5. Ask a follow-up: Which of those are still pending? This tests conversation memory.
  6. Ask something the database can’t answer: What's the weather in Chicago? The agent should politely decline.
Problem Likely Cause Fix
Agent says “I don’t have access to any tools” Baserow Tool node not connected to the Agent’s AI Tool input Check the connection, it should link to the wrench/tool connector on the Agent node, not the main input
Agent returns empty results for valid queries Wrong Database ID or Table ID in the Baserow node Double-check both IDs against your Baserow URL, they’re numeric IDs, not names
“Authentication failed” error Invalid or expired Baserow API token Generate a new token in Baserow → Settings → API tokens, and update the credential in n8n
Follow-up questions don’t work Memory node not connected or Session ID not set Ensure the Buffer Window Memory connects to the AI Memory port, and Session ID Type is set to “From Input”
Responses are slow (10+ seconds) Large table with thousands of rows Add a view filter in Baserow to limit the rows the tool can access, or switch to a more targeted query setup

Frequently Asked Questions

Can I use this with a free Baserow account?

Yes. Baserow’s free tier includes API access, which is all this workflow needs. The free plan supports up to 3,000 rows per table. If your dataset is larger, you’ll need a paid Baserow plan or a self-hosted instance.

How much does it cost to run per month?

Very little. GPT-4o Mini charges roughly $0.15 per million input tokens and $0.60 per million output tokens. A typical database query conversation uses about 2,000 to 4,000 tokens total. If your team runs 100 queries per day, you’re looking at maybe $2 to $5 per month in OpenAI costs. The n8n and Baserow free tiers cover everything else.

Can I connect multiple tables to the same chatbot?

Yes, add additional Baserow Tool nodes for each table and connect them all to the Agent’s AI Tool input. The agent will decide which table to query based on the user’s question. Just make sure your system prompt describes what each table contains so the agent picks the right one.

Can I embed the chat widget on my website?

n8n’s Chat Trigger generates a URL that you can embed as an iframe or integrate with n8n’s chat widget library. For production use, you might want to swap the Chat Trigger for a Webhook node and build a custom frontend, but for internal team use, the built-in widget works great.

What if I want to use Google Sheets instead of Baserow?

Swap the Baserow Tool node for a Google Sheets Tool node (or use an HTTP Request tool with the Sheets API). The rest of the workflow stays the same. Baserow has an edge for structured queries because it supports filters natively, but Google Sheets works fine for smaller datasets.

Is my data sent to OpenAI?

Yes, the rows retrieved from Baserow are sent to OpenAI as part of the prompt so the model can formulate an answer. If you’re working with sensitive data, review OpenAI’s data usage policy or consider using a self-hosted LLM (like Ollama) instead of GPT-4o Mini.

Get the Baserow AI Chatbot Template

Skip the setup and get the pre-built workflow JSON, a step-by-step setup guide, and a credentials walkthrough so you’re live in under 5 minutes.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Add a Slack or Telegram interface: replace the Chat Trigger with a Slack or Telegram trigger so your team can query the database directly from their messaging app.
  • Connect multiple data sources: add Airtable, Google Sheets, or a Postgres database alongside Baserow to build a unified data assistant.
  • Add write capabilities: extend the agent with a Baserow “create row” tool so users can add or update records through the chat (e.g., “Mark order ORD-2026-0891 as shipped”).
  • Schedule automated reports: add a Schedule Trigger that runs a predefined query every morning and sends the results to your team channel.
n8n
Baserow
OpenAI
GPT-4o Mini
AI Agent
chatbot
database automation
no-code

How to Build an AI Chatbot with Long-Term Memory Using n8n and Google Docs

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

Ever built a chatbot that forgets everything the moment the conversation ends? You ask your AI assistant to remember your coffee order preference, and five minutes later it has no idea you’re a oat-milk double-shot latte person. It’s frustrating, and it makes the experience feel less intelligent than it should be.

Here’s the good news: you can fix this with n8n. This workflow gives your AI chatbot true persistent memory using Google Docs as a beautifully simple, completely free knowledge store. No database setup. No complex infrastructure. Just your chatbot learning about users, remembering details, and getting smarter with every conversation.

Ready to build this? You can grab the complete ready-to-import workflow with all the setup instructions at EasyWorkflows. Import in seconds and start building immediately.

What You’ll Build

This workflow creates an intelligent chatbot experience where:

  1. Users send messages via chat trigger (or Telegram)
  2. The workflow instantly retrieves all previous memories and notes about that user from Google Docs
  3. An AI agent (GPT-4o-mini) reads the incoming message alongside historical context
  4. The AI thinks about what to remember, what to save, and how to respond
  5. Important facts get saved to the “Long-Term Memories” doc for persistence
  6. Temporary notes get saved to the “Notes” doc for session context
  7. The response goes back to the user via Telegram, chat interface, or both

How It Works: The Big Picture

Let me show you the architecture before we dive into the build:

┌─────────────────────────────────────────────────────────────────┐
│                         USER MESSAGE                             │
│                  (Chat Trigger / Telegram)                       │
└────────────────────────────┬────────────────────────────────────┘
                             │
                ┌────────────┴────────────┐
                │                         │
      ┌─────────▼──────────┐   ┌─────────▼──────────┐
      │   Load Memories    │   │    Load Notes      │
      │  from Google Docs  │   │  from Google Docs  │
      │   (Memory Doc)     │   │   (Notes Doc)      │
      └─────────┬──────────┘   └─────────┬──────────┘
                │                         │
                └────────────┬────────────┘
                             │
                      ┌──────▼──────┐
                      │    Merge    │
                      │ Memories &  │
                      │    Notes    │
                      └──────┬──────┘
                             │
                      ┌──────▼──────┐
                      │  Combine    │
                      │  Context    │
                      └──────┬──────┘
                             │
        ┌────────────────────┼────────────────────┐
        │                    │                    │
   ┌────▼────┐      ┌────────▼────────┐     ┌────▼────┐
   │ Session │      │  AI Memory      │     │  GPT-   │
   │ Memory  │      │  Agent (with    │     │ 4o Mini │
   │ Buffer  │      │  System Prompt) │     │         │
   └────┬────┘      └────────┬────────┘     └────┬────┘
        │                    │                    │
        └────────────────────┼────────────────────┘
                             │
                ┌────────────┴────────────┐
                │                         │
      ┌─────────▼────────┐    ┌──────────▼──────────┐
      │  Save Memory to  │    │  Save Note to      │
      │   Google Docs    │    │  Google Docs       │
      │  (Triggered by   │    │  (Triggered by AI) │
      │   AI decision)   │    │                    │
      └─────────┬────────┘    └──────────┬──────────┘
                │                        │
      ┌─────────▼──────────────────────────▼────────┐
      │              Response Output                │
      │      (Format Chat + Send Telegram)          │
      └──────────────────────────────────────────────┘

What You’ll Need

Before you start, gather these prerequisites:

  • n8n account (cloud.n8n.io or self-hosted), free tier works fine
  • OpenAI API key with GPT-4o-mini access (~$0.15 per conversation)
  • Google account with Google Docs access (free)
  • Telegram account (optional, but included in this workflow)
  • Telegram Bot Token from BotFather (optional, only if using Telegram output)
  • Basic n8n knowledge: familiarity with nodes, connections, and expressions is helpful but not required

Time estimate: 25 to 35 minutes for setup and first conversation.

Setting Up Your Google Docs

Google Docs is the memory backbone of this workflow. Think of it like a simple, searchable database that the AI can read and write to. Here’s why it works so well:

  • Free: You don’t pay per request or storage
  • Human-readable: You can open it anytime and read what the AI has learned
  • Searchable: Built-in Find function means you can track memory growth
  • OAuth2-compatible: n8n integrates seamlessly
  • Shareable: If you want to audit or share user data with a team member, it’s just a Google Doc link

Create Two Google Docs

Doc 1: “Long-Term Memories”

This stores facts that persist across conversations. Examples:

[04/08/2026] - Name: Sarah
[04/08/2026] - Preference: Oat milk lattes, no sugar
[04/08/2026] - Job: Product Manager at TechCorp
[04/08/2026] - Timezone: America/Chicago
[04/08/2026] - Goal: Learn n8n automation

Doc 2: “Notes”

This stores temporary reminders, action items, and session notes:

[04/08/2026 14:32:15] - User asked about workflow pricing
[04/08/2026 14:33:42] - Follow up: send template examples
[04/08/2026 14:35:08] - User mentioned bandwidth limits in n8n Cloud

You’ll get the document IDs after you create them. In Google Docs, the URL looks like:

https://docs.google.com/document/d/YOUR_GOOGLE_DOC_ID/edit

Copy that ID. You’ll paste it into the workflow nodes.

Building the Workflow: Step by Step

Now let’s build this. I’ll walk through each node with configurations and tips.

1 Chat Trigger

What it does: Listens for incoming messages from the n8n chat interface or external webhooks. This is your entry point for user input.

How to configure:

  • In n8n, add a “Chat Trigger” node (n8n-nodes-base.chatTrigger)
  • Set chatTriggerType to "webhook"
  • Save and deploy the workflow
  • The node will generate a webhook URL automatically

Data that flows out:

{
  "chatId": "user-session-123",
  "message": "Hey, can you remember I prefer oat milk lattes?",
  "sessionId": "session-456"
}

Tip: The Chat Trigger can receive messages from n8n’s web chat UI, Telegram, Slack, or any HTTP POST request. In this workflow, it fans out to two parallel Google Docs fetch operations.

2 Load Memories from Google Docs

What it does: Retrieves the Long-Term Memories document. This gives the AI context about the user’s past.

How to configure:

  • Add a “Google Docs” node
  • Authenticate with your Google account (OAuth2)
  • Set resource to "document"
  • Set documentId to YOUR_GOOGLE_DOC_ID_MEMORIES
  • Set option to "getText"

Data that flows out:

{
  "data": "[04/08/2026] - Name: Sarah\n[04/08/2026] - Preference: Oat milk lattes, no sugar\n[04/08/2026] - Job: Product Manager at TechCorp"
}

Tip: This node runs in parallel with the Notes fetch, so both operations happen simultaneously. The latency is usually under 500ms per fetch.

3 Load Notes from Google Docs

What it does: Retrieves the Notes document. This provides immediate session context and recent reminders.

How to configure:

  • Add another “Google Docs” node (separate from the Memories node)
  • Authenticate with the same Google account
  • Set resource to "document"
  • Set documentId to YOUR_GOOGLE_DOC_ID_NOTES
  • Set option to "getText"

Data that flows out:

{
  "data": "[04/08/2026 14:32:15] - Asked about workflow pricing\n[04/08/2026 14:33:42] - Follow up: send template examples"
}

Tip: If the Notes doc doesn’t exist yet or is empty, the node will return an empty string. That’s fine. The workflow handles it gracefully.

4 Merge Memories & Notes

What it does: Combines the two separate outputs into a single data structure. This merge prepares the data for aggregation.

How to configure:

  • Add a “Merge” node (@n8n-nodes-base.merge)
  • Set mode to "combine"
  • Set combineBy to "id"
  • Connect Memories output to input 0
  • Connect Notes output to input 1

Data that flows out:

[
  { "json": { "data": "[memories...]", "type": "memories" } },
  { "json": { "data": "[notes...]", "type": "notes" } }
]

Tip: The Merge node in “combine” mode takes multiple inputs and creates an array. This makes it easy for the next node to iterate over both data sources.

5 Combine Context

What it does: Aggregates the memories and notes into a single context object that the AI agent will receive.

How to configure:

  • Add an “Aggregate” node (@n8n-nodes-base.aggregate)
  • Set mode to "combine"
  • Leave other options as default

Data that flows out:

{
  "memory_context": "[all memories]",
  "notes_context": "[all notes]",
  "combined": true
}

Tip: Think of Aggregate as the “summary” step. It pools all items from the previous step so the AI Agent receives one clean input.

6 AI Memory Agent

What it does: This is the brain. The AI Agent reads the user’s message, your system prompt, and all the context from memories and notes. It decides what to respond, what to save, and which tools to call.

How to configure:

  • Add an “AI Tools Agent” node (@n8n/n8n-nodes-langchain.agent)
  • Set agentOptions.systemPrompt to the following:
You are an AI assistant with persistent memory capabilities. Your role is to:

1. MEMORY MANAGEMENT:
   - Extract and save important facts about the user for long-term recall
   - Format memories as: [DATE] - [TOPIC]: [DETAIL]
   - Save to 'Save Memory to Google Docs' when you learn something important

2. NOTE TAKING:
   - Capture actionable items, reminders, or temporary notes
   - Format notes as: [DATE] [TIME] - [NOTE]
   - Save to 'Save Note to Google Docs' when user mentions something to remember

3. CONTEXT AWARENESS:
   - Review loaded memories and notes before responding
   - Reference past conversations naturally
   - Acknowledge when you remember something about the user

4. RESPONSE GUIDELINES:
   - Be helpful, personable, and concise
   - If unsure, ask clarifying questions
   - Never make up information not in your context

Always use the provided tools to save important information.

Tool attachments: Connect the Session Memory Buffer, GPT-4o Mini LLM, and the two Save tools (Save Memory, Save Note). The agent will automatically call these tools based on its logic.

Data that flows out:

{
  "output": "Thanks Sarah! I've noted that you prefer oat milk lattes. I'll remember that for next time we chat. Is there anything else I can help with?",
  "tool_calls": [
    { "tool": "Save Memory to Google Docs", "input": { "topic": "Preference", "detail": "Oat milk lattes, no sugar" } }
  ]
}

Tip: The system prompt is where you define the AI’s personality and its rules for memory management. You can customize this heavily, make it more casual, more formal, more technical, whatever fits your use case.

7 GPT-4o Mini

What it does: The language model that powers the agent. GPT-4o-mini is fast, cheap (~$0.15 per 1K tokens), and smart enough for most chatbot tasks.

How to configure:

  • Add an “OpenAI Chat Model” node (@n8n/n8n-nodes-langchain.lmChatOpenAi)
  • Authenticate with your OpenAI API key
  • Set model to "gpt-4o-mini"
  • Set temperature to 0.7 (keeps responses natural but consistent)

Data flow: The Agent sends prompts to this node, which returns generated text. The node is “read-only” from the Agent’s perspective, it doesn’t make decisions, just generates language.

Tip: If you want more creative responses, increase temperature to 0.9. If you want more deterministic responses, lower it to 0.3. For memory-critical tasks, I’d recommend 0.5 to 0.7.

8 Session Memory Buffer

What it does: Stores the last 10 messages in the conversation (configurable). This gives the AI short-term context within a single chat session, complementing the long-term Google Docs memory.

How to configure:

  • Add a “Memory Buffer Window” node (@n8n/n8n-nodes-langchain.memoryBufferWindow)
  • Set bufferSize to 10 (keeps the last 10 messages)

Data that flows out:

{
  "history": [
    { "role": "user", "content": "Hey, remember I prefer oat milk?" },
    { "role": "assistant", "content": "I'll remember that!" },
    ...
  ]
}

Tip: The Session Memory Buffer is separate from the long-term Google Docs memory. Use bufferSize 10 to 20 for natural conversations. If you set it higher (50+), you might hit token limits on OpenAI, increasing costs.

9 Save Memory to Google Docs

What it does: A Google Docs node that appends new long-term memories. When the AI Agent decides to save something important, it calls this tool.

How to configure:

  • Add a “Google Docs” node (different from the retrieval nodes)
  • Authenticate with your Google account
  • Set resource to "document"
  • Set documentId to YOUR_GOOGLE_DOC_ID_MEMORIES
  • Set option to "appendText"
  • Set text to: ={{ "[" + new Date().toLocaleDateString() + "] - " + $json.input.topic + ": " + $json.input.detail }}

Example append:

[04/08/2026] - Preference: Oat milk lattes, no sugar

Tip: The expression creates a timestamp automatically. Each memory is on a new line, making the doc easy to scan.

10 Save Note to Google Docs

What it does: A Google Docs node for temporary notes. When the AI wants to save a reminder or action item, it calls this.

How to configure:

  • Add another “Google Docs” node (separate from Save Memory)
  • Authenticate with your Google account
  • Set resource to "document"
  • Set documentId to YOUR_GOOGLE_DOC_ID_NOTES
  • Set option to "appendText"
  • Set text to: ={{ "[" + new Date().toLocaleDateString() + " " + new Date().toLocaleTimeString() + "] - " + $json.input }}

Example append:

[04/08/2026 14:32:15] - User asked about Telegram integration

Tip: This node includes both date and time, since notes are usually time-sensitive. Notes doc grows fast, you might clean it out monthly or weekly depending on volume.

11 Send Telegram Reply

What it does: Sends the AI’s response to Telegram. Optional, but great for testing or if you want users to chat via Telegram instead of the n8n web UI.

How to configure:

  • Add an “HTTP Request” node (@n8n-nodes-base.httpRequest)
  • Set method to POST
  • Set url to: https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/sendMessage
  • Add body parameters:
  • chat_id: YOUR_TELEGRAM_CHAT_ID
  • text: ={{ $json.output }}

Tip: Get your Telegram Bot Token from BotFather on Telegram. Get your Chat ID by messaging the bot and calling /getchatid (if your bot has that command) or using a debug service. If you’re not using Telegram, you can delete this node entirely. The workflow still works.

12 Format Chat Response

What it does: Formats the AI’s output for the n8n chat interface. This is optional if you’re only using Telegram, but it’s useful for testing.

How to configure:

  • Add a “Set” node (@n8n-nodes-base.set)
  • Set mode to "map"
  • Add assignment: response = ={{ $json.output }}

Data that flows out:

{
  "response": "Thanks Sarah! I've noted that you prefer oat milk lattes. I'll remember that for next time."
}

Tip: This node is simple, but it’s important for keeping the chat interface happy. It ensures the response is in the expected format.

The Data Structure

Let’s look at how data flows through the system, using a real example with Sarah:

Initial state (empty memories and notes):

Google Docs - Long-Term Memories:
(empty)

Google Docs - Notes:
(empty)

Sarah sends her first message:

User: "Hi! I'm Sarah from Chicago. I work in product management and I love oat milk lattes."

The workflow retrieves (empty context):

memories_context: ""
notes_context: ""

AI Agent decides to save key facts:

Tool call: Save Memory to Google Docs
Input: { topic: "Name", detail: "Sarah" }
Input: { topic: "Timezone", detail: "America/Chicago" }
Input: { topic: "Job", detail: "Product Manager" }
Input: { topic: "Preference", detail: "Oat milk lattes" }

Google Docs after first conversation:

Google Docs - Long-Term Memories:
[04/08/2026] - Name: Sarah
[04/08/2026] - Timezone: America/Chicago
[04/08/2026] - Job: Product Manager
[04/08/2026] - Preference: Oat milk lattes

Google Docs - Notes:
[04/08/2026 14:35:22] - User wants to learn n8n automation

Sarah’s next message (tomorrow):

User: "Hey, can you help me with n8n workflows?"

The workflow now retrieves:

memories_context: "[04/08/2026] - Name: Sarah\n[04/08/2026] - Timezone: America/Chicago\n[04/08/2026] - Job: Product Manager\n[04/08/2026] - Preference: Oat milk lattes"

notes_context: "[04/08/2026 14:35:22] - User wants to learn n8n automation"

AI response:

Output: "Hey Sarah! Of course, I'd love to help with your n8n workflows. Given that you're a PM at TechCorp in Chicago, I'm guessing you're looking to automate some processes for your team. What kind of workflows are you thinking about?"

Notice how the AI naturally references Sarah by name, acknowledges her role, and connects her earlier interest in n8n to the current question. That’s persistent memory in action.

Full System Flow

Here’s the complete end-to-end journey:

┌─ Start: User sends message via Chat Trigger or Telegram ──┐
│                                                             │
├─ Parallel: Load Memories from Google Docs                 │
│ └─ Retrieve all historical facts about user                │
│                                                             │
├─ Parallel: Load Notes from Google Docs                    │
│ └─ Retrieve session context and recent reminders           │
│                                                             │
├─ Merge: Combine both memory sources                       │
│ └─ Creates array: [memories_obj, notes_obj]               │
│                                                             │
├─ Aggregate: Consolidate into single context               │
│ └─ Pools all items into one data structure                │
│                                                             │
├─ AI Memory Agent (receives):                              │
│ ├─ User message                                            │
│ ├─ All long-term memories                                 │
│ ├─ All notes from this session                            │
│ ├─ Last 10 messages from Session Memory Buffer            │
│ ├─ System prompt (memory management rules)                │
│ └─ Connection to GPT-4o-mini and tools                    │
│                                                             │
├─ AI Decision Making:                                      │
│ ├─ Read context and user message                          │
│ ├─ Generate response                                       │
│ ├─ Decide what facts to save (→ Save Memory tool)         │
│ └─ Decide what notes to capture (→ Save Note tool)        │
│                                                             │
├─ Parallel: Save Memory to Google Docs                     │
│ └─ Append new facts (if AI decided to save)               │
│                                                             │
├─ Parallel: Save Note to Google Docs                       │
│ └─ Append new notes (if AI decided to save)               │
│                                                             │
├─ Output to Chat:                                          │
│ ├─ Format Chat Response → n8n Web UI                     │
│ └─ Send Telegram Reply → User's Telegram                 │
│                                                             │
└─ Complete: User receives response & AI remembers ──────────┘

Testing Your Workflow

Now that your workflow is built, let’s test it thoroughly:

Test Plan

  1. Deploy the workflow and activate it in n8n
  2. Open the Chat Trigger and test with a simple message: "Hi, my name is Sarah and I like coffee."
  3. Check the Google Docs: The Memories doc should now have a new entry with Sarah’s name
  4. Send another message: "What do I like to drink?" The AI should reference the coffee preference from the previous message
  5. Test Telegram output (if enabled) by checking your Telegram chat, the response should arrive there too
  6. Clear the Notes doc and send a message with a reminder: "Remind me to follow up with the marketing team tomorrow." Check that the note appears in the Notes doc
  7. Simulate a delay: Wait 10 minutes, come back, and test that the AI still remembers Sarah. This confirms persistence

Troubleshooting Table

Issue Likely Cause Fix
Chat Trigger shows “Webhook not working” Workflow not saved or deployed Click Save, then Deploy. Check that the webhook URL is active in the node.
Google Docs nodes fail with auth error OAuth2 credential not connected or expired Re-authenticate the Google Docs credential. Grant permission for Docs read/write.
AI response is generic, not remembering user Memories not being retrieved or system prompt unclear Check that Load Memories node is pulling data. Review system prompt in AI Agent node for clarity.
Memory saves fail silently (no error, but doc not updated) Google Doc ID is incorrect or doc doesn’t exist Verify Document ID in Save Memory and Save Note nodes. Double-check the URL in Google Docs.
Telegram message doesn’t send Bot token invalid or chat ID missing Confirm bot token with BotFather. Test chat ID manually using curl or Postman.
Workflow is slow (>5 second response) Large memory doc or network latency Archive old memories to a separate doc. Consider pagination in future versions.

Frequently Asked Questions

How much data can Google Docs store?

Google Docs has a 50MB file size limit, which translates to roughly 10 to 20 million words. For a personal chatbot, you’d need to chat 100+ times daily for years to hit that limit. For multi-user systems, consider archiving old memories after 6 months.

Why Google Docs instead of a real database?

Simplicity. No backend servers, no authentication headaches, no costs, and you can read/edit memories by hand. For production systems with thousands of users, you’d want PostgreSQL or MongoDB. For personal projects and small teams, Google Docs is perfect.

Can multiple users use the same workflow?

Yes, but you’d want to create separate memory docs per user (or per user-group). Otherwise, all memories get mixed together. A simple fix is to add a User ID prefix in each memory entry: [04/08/2026] [user-sarah] - Preference: Oat milk.

What if I want to use a different LLM instead of GPT-4o-mini?

Easy. Replace the “GPT-4o Mini” node with any other LLM node that n8n supports: Claude (Anthropic), Gemini (Google), Llama (Meta), or even local models. The system prompt stays the same. Just swap the node and re-authenticate.

How do I ensure memory data is private?

The memories are stored in your Google Docs, which means Google has access (per their privacy policy). If you need end-to-end encryption, deploy n8n self-hosted and use your own database with encryption at rest. For most use cases, treating Google Docs like email (encrypted in transit, encrypted at rest on Google’s servers) is sufficient.

What if the AI forgets something it should remember?

This usually means the system prompt isn’t clear enough about what to save. Revisit the “MEMORY MANAGEMENT” section of the system prompt and be more explicit. Example: add “Save names, preferences, job titles, and timezone. Save reminders and follow-ups as notes.” You can also manually add memories to the Google Doc yourself.

Ready to Deploy This Workflow?

Get the complete, ready-to-import JSON file with all the configurations, plus detailed setup guides for OpenAI, Google Docs, and Telegram. Import in seconds and start building.

Get the Workflow & Setup Guide

Includes step-by-step credential setup, testing checklist, and common customizations.

What’s Next?

You’ve built a powerful memory-driven chatbot. Here are four directions you can take it:

  1. Add Slack Integration: Instead of (or in addition to) Telegram, receive and respond to Slack DMs. Users stay in Slack, and memories persist across platforms.
  2. Multi-User Memory Isolation: Spin up separate memory docs per user, or implement a shared “team knowledge base” doc that all users contribute to. Great for support teams.
  3. Export Memory Analytics: Create a separate workflow that reads the memory doc weekly and generates a summary email. “Sarah asked 12 questions about n8n, preferred 5 templates, and wants to learn automation.”
  4. Archive Old Memories: Set up a scheduled n8n workflow that runs monthly, archives memories older than 6 months to a separate “Archive” doc, and clears the active memory doc. Keeps performance snappy.
n8n
AI Agent
Memory Management
Google Docs
GPT-4o
Chatbot
Telegram
LLM Integration
Automation

How to A/B Test AI Prompts with n8n, Supabase, and OpenAI

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

Most AI builders pick their chatbot’s system prompt based on gut feeling. They write something that sounds good, deploy it, and hope for the best. But what if you could actually test two prompt variants on real users and measure which one performs better? This n8n workflow does exactly that: it randomly assigns users to either a baseline or alternative system prompt, remembers their assignment, and lets you collect data on which version gets better results. No guessing. Just data-driven prompt optimization.

Prefer to skip the setup? Grab the ready-made template and import it into your n8n instance in minutes: get the A/B testing template here.


What You’ll Build

By the end of this guide, you’ll have a fully functional A/B testing system that:

  1. Accepts incoming chat messages with user session IDs
  2. Stores two distinct system prompt variants (baseline and alternative)
  3. Checks whether the session has been assigned to a test group before
  4. Automatically assigns new users to one of the two variants using a 50/50 random split
  5. Ensures returning users always see the same prompt variant they were originally assigned
  6. Passes the correct prompt to your AI agent (OpenAI GPT-4o-mini)
  7. Maintains full conversation history in PostgreSQL so the AI remembers previous messages
  8. Records session and test assignment data for later analysis

How It Works: The Big Picture

Here’s the flow from incoming message to AI response:

┌────────────────────────────────────────────────────────────────┐
│  A/B TEST AI PROMPTS                                           │
│                                                                │
│  [Chat Trigger] → [Define Test Prompts] → [Check Session]     │
│                                              ↓                 │
│                                    [Session Assigned?]         │
│                                     ↙ Yes        ↘ No         │
│                              [Select Prompt]  [Assign Random]  │
│                                     ↘             ↙            │
│                                  [Select Prompt]               │
│                                       ↓                        │
│                                  [AI Agent]                    │
│                              (OpenAI + Memory)                 │
└────────────────────────────────────────────────────────────────┘

The workflow listens for incoming chat messages, queries Supabase to see if the user’s session already exists, and branches based on the result. New users get randomly assigned to one of two prompt variants; returning users get their original variant. Both paths converge at a single AI Agent node that uses the correct system prompt and maintains conversation memory through PostgreSQL.


What You’ll Need

  • Supabase account (free tier is fine). You’ll need a PostgreSQL database and the ability to run SQL queries
  • OpenAI API key with access to GPT-4o-mini (cost: typically less than $1 per 1M tokens)
  • n8n instance, either n8n Cloud (free or paid plans) or self-hosted
  • Basic familiarity with n8n: understanding nodes, inputs, and outputs will help

Build time: 25-35 minutes from scratch, under 10 minutes if you import the ready-made template.


Step-by-Step Setup

1 Set Up Your Chat Trigger

Start with a Chat Trigger node (or HTTP Request if you’re building a custom endpoint). This node receives incoming user messages along with a session ID. The session ID is crucial. It’s how you identify repeat users.

Your incoming payload should look like this:

{
  "sessionId": "sess_7f3a2b91",
  "userId": "user_4c2e9k10",
  "message": "Hello, what's your recommendation for a CRM?"
}
📝

Session ID strategy: If you’re embedding this in a web app, generate a unique session ID and store it in localStorage or a cookie. For API-driven usage, your backend can generate UUIDs or slugs.

2 Define Your Test Prompts

Add a Set node after the Chat Trigger. This node stores both system prompt variants. Here’s an example with a customer support chatbot:

Baseline prompt (friendly):

"You are a helpful customer support agent for an e-commerce platform. Be warm, approachable, and conversational. Always put the customer's needs first. If you don't know something, admit it and offer to escalate."

Alternative prompt (professional):

"You are a professional customer support specialist. Provide concise, accurate answers. Use technical terminology where appropriate. Focus on efficiency and quick resolution. Maintain professional boundaries while remaining courteous."

Store these as variables in your Set node. For example:

{
  "baseline_prompt": "You are a helpful customer support agent...",
  "alternative_prompt": "You are a professional customer support specialist..."
}

You can customize these prompts however you want: adjust tone, instructions, constraints, anything. The point is to test meaningful variations.

3 Query Supabase for Existing Sessions

Add a Supabase node (Query Rows) to check if this session has been assigned before. Set up the query like this:

Table: split_test_sessions

Filter: session_id = (incoming session_id)

This will return an empty array if the session is new, or one row if the session already exists. Save the result to a variable like session_lookup.

4 Add a Conditional: Is the Session Already Assigned?

Use an IF node to check whether the session exists:

session_lookup.length > 0

If true (session exists), branch to “Select Active Prompt”. If false (new session), branch to “Assign Random Variant”.

5 Assign a Random Variant to New Users

In the “false” branch, add a Function node that generates a random coin flip and inserts a new row into Supabase:

// Generate 50/50 random boolean
const show_alternative = Math.random() < 0.5;

// Return the assignment for the next node
return {
  show_alternative: show_alternative,
  session_id: $input.all()[0].json.sessionId,
  timestamp: new Date().toISOString()
};

Follow this with a Supabase Insert Rows node that saves the assignment to the database:

Table: split_test_sessions

Columns:

{
  "session_id": $input.all()[0].json.sessionId,
  "show_alternative": show_alternative,
  "created_at": new Date().toISOString()
}
💡

Tip: Use Supabase's connection pooler for faster queries, especially if you're running high volume. It's in your project settings under "Database" → "Connection Pooling".

6 Select the Active Prompt

Both paths (existing and new sessions) converge at a Set node that picks the correct system prompt. This node needs to check whether show_alternative is true or false and return the matching prompt:

{
  "system_prompt": $input.all()[0].json.show_alternative
    ? $input.all()[0].json.alternative_prompt
    : $input.all()[0].json.baseline_prompt
}

Make sure this node receives the show_alternative boolean from either the database query (existing session) or the assignment function (new session).

7 Configure the AI Agent with Memory

Add an OpenAI node configured as an AI Agent. Set it up like this:

Model: gpt-4o-mini

System prompt: Use the system_prompt variable from the previous Set node

Chat memory: Enable PostgreSQL memory using your Supabase connection. Configure it with:

  • Connection: Your Supabase PostgreSQL connection
  • Session ID: The incoming sessionId
  • Memory type: Buffer memory or summarization (your choice based on conversation length)

This ensures the AI remembers all previous messages in the session, maintaining context across turns.


The Data Structure

You need a PostgreSQL table in Supabase to track session assignments. Here's the schema:

Column Name Type Description
id BIGINT (auto-increment) Primary key, auto-generated
session_id TEXT (unique) Unique identifier for the user session, e.g. "sess_7f3a2b91"
show_alternative BOOLEAN true = user sees alternative prompt, false = user sees baseline prompt
created_at TIMESTAMP When the assignment was created, useful for sorting and analysis

To create this table in Supabase, go to the SQL Editor and run:

CREATE TABLE split_test_sessions (
  id BIGSERIAL PRIMARY KEY,
  session_id TEXT NOT NULL UNIQUE,
  show_alternative BOOLEAN NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_session_id ON split_test_sessions(session_id);

The index on session_id speeds up lookups when users return.


Full System Flow with Data

Let's trace a complete example with realistic data:

1. User message arrives:

{
  "sessionId": "sess_7f3a2b91",
  "userId": "user_4c2e9k10",
  "message": "Hello, what CRM do you recommend for a 50-person startup?"
}

2. Query Supabase:

SELECT * FROM split_test_sessions WHERE session_id = 'sess_7f3a2b91';
// Result: [] (empty, this is a new user)

3. Assign random variant:

Math.random() < 0.5  // Returns true, assign alternative
// Insert into Supabase:
{
  "session_id": "sess_7f3a2b91",
  "show_alternative": true,
  "created_at": "2026-04-08T14:32:05.000Z"
}

4. Select the correct prompt:

// show_alternative is true, so use:
system_prompt = "You are a professional customer support specialist..."

5. AI Agent responds:

The OpenAI node receives the alternative system prompt and the user's message. With PostgreSQL memory enabled, it also pulls any previous messages from this session (none on first message). It generates a response:

"For a 50-person startup, I'd recommend HubSpot or Pipedrive. Both scale efficiently and offer the customization you'll need. What's your primary use case, sales pipeline or customer support?"

6. On the next turn (same session):

{
  "sessionId": "sess_7f3a2b91",
  "userId": "user_4c2e9k10",
  "message": "Mainly sales pipeline. What about pricing?"
}

Query returns the existing row with show_alternative: true. The same professional prompt is used. Memory context includes the entire conversation.


Testing Your Workflow

Before running it live, test each step:

  1. Test the Chat Trigger: Send a sample message with a session ID through the webhook or chat interface. Check that the payload arrives correctly.
  2. Test Supabase connectivity: Run a simple query (e.g., SELECT * FROM split_test_sessions LIMIT 1;) to verify the connection works.
  3. Test the random assignment: Run the workflow 10 times with different session IDs and verify that Supabase records are created with roughly 50% true/false split.
  4. Test the conditional logic: Create a session, run the workflow, then re-run with the same session ID. Verify that the second run retrieves the existing assignment instead of creating a new one.
  5. Test the AI Agent: Verify the AI receives the correct system prompt by checking the API logs or n8n execution history.
  6. Test memory persistence: Send multiple messages in the same session and confirm the AI remembers previous context.

Common Issues and Troubleshooting

Issue Likely Cause Solution
Supabase query returns empty even for existing sessions Session ID mismatch (case sensitivity, extra whitespace) Normalize session IDs: trim whitespace, convert to lowercase
AI Agent fails with authentication error Invalid OpenAI API key or quota exceeded Check your API key in n8n credentials; verify you have billing enabled in OpenAI account
Duplicate session assignments (multiple true/false values) Missing UNIQUE constraint on session_id Add UNIQUE constraint to split_test_sessions.session_id
Conversation memory not working PostgreSQL connection not configured or memory table missing Verify Supabase PostgreSQL credentials in n8n; ensure memory table exists
Workflow executes but AI returns generic responses System prompt not being passed correctly Debug: log the system_prompt variable before the AI Agent node, verify it's not empty
🔍

Debugging tip: Use n8n's Execute Workflow button and inspect the input/output of each node. The execution history shows exactly what data is flowing through your workflow.


Measuring Results

Now that your A/B test is running, how do you measure which prompt is better? A few approaches:

  • User feedback: Add a thumbs-up/thumbs-down button after each AI response and record votes in a feedback table, tagged with session_id and show_alternative.
  • Conversation length: Query Supabase to see average message count per session for each variant. Longer conversations might indicate more engaging prompts.
  • Resolution time: If this is customer support, track how many turns it takes to resolve issues per variant.
  • Manual review: Export a sample of responses from each variant and have a human evaluate quality, tone, and accuracy.
  • Custom metrics: Log additional data (response time, token usage, user satisfaction score) to your Supabase table for analysis.

Run each variant for at least 100-200 sessions before drawing conclusions. Statistical significance matters.


Frequently Asked Questions

Can I test more than two prompts?

Yes, absolutely. Instead of a boolean show_alternative column, use an integer or enum to represent three or more variants. Adjust the random assignment logic to distribute evenly (e.g., if 3 variants: Math.floor(Math.random() * 3)). Update the "Select Active Prompt" node to use a switch statement or nested ternary.

How do I measure which prompt performs better?

Add a feedback mechanism (thumbs-up/down buttons or a satisfaction rating) tied to each session. Store results in Supabase with the variant ID. Then query Supabase to calculate average scores per variant. You can also measure conversation length, resolution time, or cost per variant.

Does this work with models other than GPT-4o-mini?

Yes. The workflow is model-agnostic. You can use GPT-4, GPT-3.5 Turbo, Claude (via Anthropic API), or any LLM with an n8n integration. Just swap the model in the AI Agent node and ensure you have valid API credentials.

What happens if Supabase goes down?

If Supabase is unavailable, the workflow will fail at the session lookup step. To add resilience, wrap Supabase queries in try-catch blocks or add error handling nodes that fall back to a default prompt (e.g., always use baseline if the database is unreachable).

Can I use this for testing different temperatures or models?

Absolutely. Extend the workflow to test different model parameters. For example, add a temperature and model_name column to split_test_sessions. In the AI Agent node, dynamically set the temperature and model based on the session's assigned variant. This lets you A/B test creativity (high temperature) vs. consistency (low temperature).

Can I run multiple A/B tests simultaneously?

Yes. Use separate columns in split_test_sessions for each test (e.g., prompt_test, temperature_test, model_test). Each column holds the variant assignment for that specific test. The workflow then reads all relevant columns and applies them simultaneously to the AI Agent. This is called multivariate testing.


Get the A/B Prompt Testing Template

Stop guessing which prompt works best. Import this ready-made n8n workflow, connect your Supabase and OpenAI accounts, and start testing in minutes.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What's Next: Extending the Workflow

Once you have the basic A/B test working, consider these enhancements:

  • Automatic winner selection: Set up a scheduled workflow that analyzes results every week and automatically switches all new users to the winning variant.
  • Progressive rollout: Instead of 50/50, shift traffic gradually (90/10, 80/20, etc.) as one variant proves better.
  • Segmented testing: Run different tests for different user segments (new vs. returning, by industry, by region).
  • Prompt versioning: Store all prompt versions in Supabase with timestamps so you can track which exact variant each user saw.
  • Multivariate testing: Test system prompt, temperature, and model all at once to find the optimal combination.
  • Cost tracking: Log token usage per variant to see if one prompt is more efficient.

n8n
Supabase
OpenAI
A/B Testing
AI Prompts
Chatbot
Automation
PostgreSQL
Data-Driven

How to Build a WhatsApp AI Customer Support Bot with n8n

New to n8n? Start with our step-by-step setup guide, then come back to build this workflow.

Running customer support 24/7 without burning out your team is one of the hardest operational problems a growing business faces. Customers expect instant replies on WhatsApp, but hiring a support agent for every timezone is expensive and unsustainable. This n8n workflow connects WhatsApp Business API, OpenAI’s GPT-4o mini, and Supabase to build a support bot that reads conversation history, generates intelligent replies, and responds in under 3 seconds, around the clock, automatically.

Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.

What You’ll Build

  1. A customer sends a WhatsApp message to your business number at any hour.
  2. n8n receives the message via webhook and filters out non-text events like delivery receipts.
  3. The workflow fetches the customer’s last 10 messages from Supabase so the AI has full context.
  4. OpenAI GPT-4o mini reads the conversation history and generates a helpful, on-brand reply.
  5. The exchange is saved to Supabase and the reply is sent back to the customer, typically within 2 to 3 seconds.

How It Works: The Big Picture

The workflow is a single pipeline triggered by Meta’s WhatsApp Business webhook. Every incoming message flows through a filter, a context-retrieval step, an AI generation step, and two write operations, one to store the conversation and one to deliver the reply.

┌──────────────────────────────────────────────────────────┐
│  WHATSAPP AI CUSTOMER SUPPORT BOT                        │
│                                                          │
│  [WhatsApp Webhook]                                      │
│         |                                                │
│         v                                                │
│  [Is Text Message?] --(No)--> [Return 200 OK]            │
│         | (Yes)                                          │
│         v                                                │
│  [Extract Message Data]                                  │
│         |                                                │
│         v                                                │
│  [Get History · Supabase]                                │
│         |                                                │
│         v                                                │
│  [Build AI Messages]                                     │
│         |                                                │
│         v                                                │
│  [OpenAI Chat Completion]                                │
│         |                                                │
│         v                                                │
│  [Store Conversation · Supabase]                         │
│         |                                                │
│         v                                                │
│  [Send WhatsApp Reply]                                   │
│         |                                                │
│         v                                                │
│  [Return 200 OK]                                         │
└──────────────────────────────────────────────────────────┘
  

What You’ll Need

  • n8n instance: Cloud or self-hosted (v1.0 or higher)
  • Meta Developer Account: with a WhatsApp Business App and a phone number configured (test numbers are free)
  • OpenAI API key: GPT-4o mini costs roughly $0.0002 per typical support message
  • Supabase account: the free tier handles thousands of daily conversations
  • One conversations table in Supabase (SQL provided below)

Estimated build time: 45 to 60 minutes from scratch, or under 10 minutes with the template.

Part 1: Building the Workflow Step by Step

1 WhatsApp Webhook (Webhook node)

Meta’s WhatsApp Business API delivers every incoming message to a URL you specify, and this node is that URL. When a customer texts your business, Meta makes a POST request here with the full message payload.

Configure the node:

  1. Set HTTP Method to POST
  2. Set Path to whatsapp-support
  3. Set Response Mode to “Using ‘Respond to Webhook’ Node”, which lets the pipeline finish before sending the 200 OK
  4. Copy the production webhook URL (e.g., https://your-n8n.com/webhook/whatsapp-support) and paste it into your Meta App’s WhatsApp → Configuration → Webhook URL field

Here’s what a typical incoming payload looks like:

{
  "entry": [{
    "changes": [{
      "value": {
        "metadata": { "phone_number_id": "123456789012345" },
        "messages": [{
          "id": "wamid.HBgNMTU1NTEyMzQ1Njc4",
          "from": "15551234567",
          "type": "text",
          "text": { "body": "Hi, where is my order #1042?" }
        }]
      }
    }]
  }]
}
💡

Tip: Meta also sends webhook events for status updates (delivered, read). These won’t have a messages array, which is exactly why the next step filters them out.

2 Is Text Message? (IF node)

Not every webhook event is a customer message. Delivery confirmations and read receipts arrive through the same endpoint without a messages[0] object. This IF node gates those out to avoid errors downstream.

Configure the condition:

  1. Left Value: {{ $json.entry[0].changes[0].value.messages[0].type }}
  2. Operation: equals
  3. Right Value: text

The True branch continues to message processing. The False branch connects to a simple Respond to Webhook node that returns 200 OK immediately, keeping Meta happy without doing unnecessary work.

💡

Tip: Want to handle voice notes or images too? Add additional OR conditions for audio or image message types and route them to separate processing branches.

3 Extract Message Data (Set node)

Meta’s webhook payload is deeply nested. This Set node flattens the fields we need into a clean, easy-to-reference object so every downstream node can access data without long expression chains.

Configure these field assignments:

Field Name n8n Expression
phone ={{ $json.entry[0].changes[0].value.messages[0].from }}
message ={{ $json.entry[0].changes[0].value.messages[0].text.body }}
messageId ={{ $json.entry[0].changes[0].value.messages[0].id }}
timestamp ={{ $now.toISO() }}
businessPhoneId ={{ $json.entry[0].changes[0].value.metadata.phone_number_id }}

After this node, the data looks like:

{
  "phone": "15551234567",
  "message": "Hi, where is my order #1042?",
  "messageId": "wamid.HBgNMTU1NTEyMzQ1Njc4",
  "timestamp": "2026-04-05T14:32:17.000Z",
  "businessPhoneId": "123456789012345"
}

4 Get Conversation History (HTTP Request → Supabase)

Before calling OpenAI, we need the customer’s conversation history so the AI can understand context: was this their first message? Did they already explain their issue? This node queries Supabase’s REST API for the last 10 messages from this phone number.

Configure the HTTP Request node:

  1. Method: GET
  2. URL: https://YOUR_SUPABASE_PROJECT_REF.supabase.co/rest/v1/conversations
  3. Add header apikey: your Supabase anon/public key
  4. Add header Authorization: Bearer YOUR_SUPABASE_ANON_KEY
  5. Add query param phone: eq.{{ $('Extract Message Data').item.json.phone }}
  6. Add query param order: created_at.desc
  7. Add query param limit: 10

Returns an array of up to 10 rows, or an empty array for a brand-new customer, which is handled gracefully in the next step.

💡

Tip: The limit=10 means the AI sees the last 5 exchanges (10 messages: 5 user, 5 assistant). Increase this for more context, but be aware that a larger history window increases your OpenAI token cost slightly.

5 Build AI Messages (Code node)

This is the brain of the operation. The Code node takes the Supabase history array, reverses it to chronological order, prepends a system prompt, and appends the current customer message, producing the exact messages array the OpenAI Chat Completions API expects.

const history = $input.all();
const currentMessage = $('Extract Message Data').item.json.message;
const phone = $('Extract Message Data').item.json.phone;
const businessPhoneId = $('Extract Message Data').item.json.businessPhoneId;

// System prompt defines the bot's personality and knowledge
const messages = [
  {
    role: 'system',
    content: `You are a friendly and professional customer support assistant
for our online store. Be concise, empathetic, and helpful.
Answer questions about orders, shipping, returns, and products.
If you cannot resolve an issue, ask the customer to email
support@yourstore.com with their order number.
Keep replies under 150 words.`
  }
];

// Add history in chronological order (we fetched desc, so reverse)
const sorted = history.map(i => i.json)
  .filter(m => m.role && m.content)
  .reverse();
for (const msg of sorted) {
  messages.push({ role: msg.role, content: msg.content });
}

// Add the current incoming message
messages.push({ role: 'user', content: currentMessage });

return [{ json: { messages, currentMessage, phone, businessPhoneId } }];
💡

Tip: The system prompt is where you make this bot yours. Add specific product return policies, shipping timeframes, or even a list of FAQ answers. The more specific the prompt, the more accurate the responses, and the fewer handoffs to human agents.

6 OpenAI Chat Completion (HTTP Request → OpenAI)

Sends the complete conversation context to GPT-4o mini and receives a natural, contextual reply. Using an HTTP Request node (rather than the OpenAI node) gives you full control over the messages array format.

Configure the node:

  1. Method: POST
  2. URL: https://api.openai.com/v1/chat/completions
  3. Header Authorization: Bearer YOUR_OPENAI_API_KEY
  4. Header Content-Type: application/json
  5. Body (JSON expression): ={{ { "model": "gpt-4o-mini", "messages": $json.messages, "max_tokens": 500, "temperature": 0.7 } }}

The response object looks like this:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Hi! I'd be happy to look into order #1042 for you. Could you confirm the email address on the order so I can pull it up?"
    },
    "finish_reason": "stop"
  }],
  "usage": { "total_tokens": 312 }
}

Access the reply downstream with $json.choices[0].message.content.

💡

Tip: GPT-4o mini costs roughly $0.15 per million input tokens. A typical 10-message support conversation uses around 800 tokens total, under $0.0002. Even at 10,000 conversations per month, your OpenAI bill stays under $2.

7 Store Conversation (HTTP Request → Supabase)

Saves both the customer’s message and the AI’s reply to Supabase in a single bulk insert. This is what makes the bot remember past conversations: every message is persisted for future context retrieval.

Configure the node:

  1. Method: POST
  2. URL: https://YOUR_SUPABASE_PROJECT_REF.supabase.co/rest/v1/conversations
  3. Headers: same apikey and Authorization as Step 4, plus Content-Type: application/json and Prefer: return=minimal
  4. Body: a JSON array with two objects: the user message and the assistant reply
=[
  {
    "phone": "={{ $('Build AI Messages').item.json.phone }}",
    "role": "user",
    "content": "={{ $('Build AI Messages').item.json.currentMessage }}"
  },
  {
    "phone": "={{ $('Build AI Messages').item.json.phone }}",
    "role": "assistant",
    "content": "={{ $json.choices[0].message.content }}"
  }
]
💡

Tip: Supabase supports array inserts in a single POST, both rows land in one API call, keeping your workflow fast.

8 Send WhatsApp Reply (HTTP Request → Meta Graph API)

Delivers the AI-generated response to the customer’s WhatsApp number using Meta’s Cloud API. The businessPhoneId you extracted in Step 3 identifies which business phone number to send from.

Configure the node:

  1. Method: POST
  2. URL: =https://graph.facebook.com/v19.0/{{ $('Build AI Messages').item.json.businessPhoneId }}/messages
  3. Header Authorization: Bearer YOUR_WHATSAPP_ACCESS_TOKEN
  4. Body (JSON expression):
={
  "messaging_product": "whatsapp",
  "to": "={{ $('Build AI Messages').item.json.phone }}",
  "type": "text",
  "text": {
    "body": "={{ $('OpenAI Chat Completion').item.json.choices[0].message.content }}"
  }
}
💡

Tip: Phone numbers must be in international format without the + prefix, e.g., 15551234567 not +1-555-123-4567. The value you extracted from the webhook payload is already in the correct format.

9 Return 200 OK (Respond to Webhook node)

Meta requires your webhook endpoint to respond with HTTP 200 within 20 seconds. This final node sends that acknowledgment after the full pipeline completes successfully.

Configure: Respond With = JSON, Response Body = {"status": "ok"}. Also wire the False branch from Step 2 (“Ignore Non-Text”) to a separate instance of this node so non-text webhook events also get their 200 OK immediately.

The Data Structure

The entire conversation memory lives in a single Supabase table. Create it by running this SQL in the Supabase dashboard under SQL Editor:

CREATE TABLE conversations (
  id          bigint primary key generated always as identity,
  phone       text        not null,
  role        text        not null check (role in ('user', 'assistant')),
  content     text        not null,
  created_at  timestamptz not null default now()
);

CREATE INDEX idx_conversations_phone      ON conversations(phone);
CREATE INDEX idx_conversations_created_at ON conversations(created_at);
Column Type Example Description
id bigint 42 Auto-incrementing primary key
phone text 15551234567 Customer WhatsApp number (no + prefix)
role text user Either user or assistant
content text Hi, where is my order #1042? Full message text
created_at timestamptz 2026-04-05T14:32:17Z Auto-set on insert

Sample rows showing a two-turn conversation:

id phone role content created_at
1 15551234567 user Hi, where is my order #1042? 2026-04-05T14:32:17Z
2 15551234567 assistant Hi! I’d be happy to help. Could you confirm the email on the order? 2026-04-05T14:32:19Z
3 15551234567 user Sure, it’s james.carter@gmail.com 2026-04-05T14:35:01Z
📌

Important: The table name must be exactly conversations and column names must match the workflow expressions exactly. Column names are case-sensitive in Supabase’s REST API.

Full System Flow

Customer's WhatsApp
       | sends "Hi, where is order #1042?"
       v
[Meta WhatsApp Business API]
       | POST /webhook/whatsapp-support
       v
[n8n: WhatsApp Webhook]
       |
       v
[Is Text Message?] --(No: status update)--> [Return 200 OK]
       | (Yes)
       v
[Extract Message Data]
  phone=15551234567 | message="Hi, where is order #1042?" | businessPhoneId=12345
       |
       v
[Supabase: GET /conversations?phone=eq.15551234567&order=created_at.desc&limit=10]
  Returns: [] (first-time customer)
       |
       v
[Build AI Messages]
  [ {role:system, content:"You are a helpful support agent..."} ,
    {role:user, content:"Hi, where is order #1042?"} ]
       |
       v
[OpenAI GPT-4o mini: /v1/chat/completions]
  Returns: "Hi! I'd be happy to look into order #1042 for you..."
       |
       v
[Supabase: POST /conversations (bulk insert 2 rows)]
  Stores user msg + assistant reply
       |
       v
[Meta Graph API: POST /messages]
  Sends reply to +15551234567 via WhatsApp
       |
       v
Customer receives reply in <3 seconds
       |
       v
[Return 200 OK to Meta]
  

Testing Your Workflow

  1. In your Meta Developer portal, go to WhatsApp → API Setup and use the test “From” number to send a message to your personal WhatsApp number (which you’ve added as a test recipient).
  2. Open n8n and watch the Executions panel, a new execution should appear within a second.
  3. Verify the IF node routed to the True branch and all nodes show green checkmarks.
  4. Check your Supabase conversations table, two new rows should appear (role=user and role=assistant).
  5. Check your WhatsApp, the AI reply should have arrived.
Problem Likely Cause Fix
Webhook never triggers URL not saved in Meta portal Go to Meta App → WhatsApp → Configuration → paste the n8n production webhook URL and click Verify & Save
403 from Supabase Wrong API key or missing RLS policy Use the anon/public key (not service role), and in Supabase enable Row Level Security but add a policy allowing insert/select
400 from WhatsApp API Phone number format wrong Numbers must be international digits only, no + or dashes, e.g., 15551234567
OpenAI 401 Unauthorized Expired or mistyped API key Regenerate your key at platform.openai.com and update the Authorization header value
Bot has no memory between sessions Supabase table name or column name mismatch Check that the table is named conversations and columns are phone, role, content, created_at, exact case

Frequently Asked Questions

Do I need a paid WhatsApp Business account to use this?

You need a Meta Business Account, but not a paid subscription. WhatsApp’s Cloud API (the free tier) includes 1,000 free service conversations per month. After that, pricing is per-conversation, typically $0.01 to $0.05 depending on your country. You can start testing for free using Meta’s developer test credentials before connecting a real business number.

How many messages back does the bot remember?

By default, the workflow fetches the last 10 messages (5 exchanges). Change the limit=10 query parameter in Step 4 to any number you prefer. Higher limits give the AI more context but slightly increase your OpenAI token usage, still very inexpensive at GPT-4o mini rates.

Can I customize the AI’s tone and knowledge?

Absolutely, that’s the whole point of the system prompt in Step 5. Edit the system message content in the Build AI Messages Code node to reflect your brand voice, product catalog details, return policies, or even specific FAQ answers. The more specific the prompt, the fewer cases the bot will escalate to a human.

What happens if OpenAI is temporarily unavailable?

n8n will mark the execution as an error and the customer won’t receive a reply. To handle this gracefully, add an n8n Error Trigger workflow that catches failures and sends a fallback WhatsApp message: “We’re experiencing a brief technical issue, a team member will respond within 1 hour.” You can also enable n8n’s built-in retry logic on the OpenAI HTTP Request node.

Can I hand off to a human agent when needed?

Yes. In the Build AI Messages Code node, add logic to check whether the incoming message contains phrases like “speak to a human” or “escalate.” If detected, replace the OpenAI step with a Slack message or email notification to your support team, who then reply manually. The conversation history in Supabase gives them full context immediately.

Does this work with multiple WhatsApp phone numbers?

Yes, because the businessPhoneId is pulled from the incoming webhook payload, the workflow dynamically uses the correct sending number for each incoming message. Link multiple WhatsApp numbers to your Meta app and a single n8n workflow handles all of them without any changes.

🚀 Get the WhatsApp AI Customer Support Bot Template

You now have everything you need to deploy a 24/7 WhatsApp AI support bot with full conversation memory. The template includes the ready-to-import workflow JSON, a step-by-step Setup Guide, and a Credentials Guide for WhatsApp Business API, OpenAI, and Supabase, so you skip the hours of API docs and go straight to testing.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Add order lookup: Connect to your Shopify or WooCommerce store so the bot can query real-time order statuses by order number.
  • Detect frustrated customers: Add a sentiment analysis step: if the AI detects frustration or repeated complaints, automatically route to a Slack alert for a human to jump in.
  • Go multilingual: Add a language detection step before the OpenAI call and include the detected language in the system prompt so the bot replies in the customer’s language automatically.
  • Build a weekly digest: Use an n8n Schedule trigger to query Supabase for weekly conversation summaries and email them to your team every Monday morning.
n8n
WhatsApp
OpenAI
Supabase
customer support
chatbot
automation
GPT-4o