New to n8n? Start with our step-by-step setup guide, then come back to build this workflow. Connecting a store too? See how to connect Shopify to n8n in 2026.
Running customer support around the clock without burning out your team is one of the hardest operational problems a growing business faces. Customers expect instant replies, but hiring an agent for every timezone is expensive and unsustainable. This n8n workflow connects a Telegram bot, Google Gemini, and Supabase to build a support bot that reads conversation history, generates intelligent replies, and responds in under 3 seconds, around the clock, automatically. Telegram is free with no per-message fee, and Gemini’s free tier means the AI costs nothing to run at typical volume.
Want a ready-made agent instead? The Shopify AI Customer Support Agent is a tested, import-ready template built on the same idea.
What You’ll Build
- A customer sends a message to your Telegram bot at any hour.
- n8n receives the message through the Telegram Trigger node.
- The workflow fetches the customer’s last 10 messages from Supabase so the AI has full context.
- Google Gemini reads the conversation history and generates a helpful, on-brand reply.
- 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 the Telegram Trigger node. 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.
┌──────────────────────────────────────────────────────────┐ │ TELEGRAM AI CUSTOMER SUPPORT BOT │ │ │ │ [Telegram Trigger] │ │ | │ │ v │ │ [Is Text Message?] --(No)--> [Stop] │ │ | (Yes) │ │ v │ │ [Extract Message Data] │ │ | │ │ v │ │ [Get History · Supabase] │ │ | │ │ v │ │ [Build Gemini Request] │ │ | │ │ v │ │ [Google Gemini · generateContent] │ │ | │ │ v │ │ [Store Conversation · Supabase] │ │ | │ │ v │ │ [Send Telegram Reply] │ └──────────────────────────────────────────────────────────┘
What You’ll Need
- n8n instance: Cloud or self-hosted (v1.0 or higher)
- Telegram bot token: created in two minutes with @BotFather, completely free, no approval or phone number setup
- Google Gemini API key: from Google AI Studio, free tier, no credit card, no OpenAI subscription needed
- Supabase account: the free tier handles thousands of daily conversations
- One
conversationstable in Supabase (SQL provided below)
Estimated build time: 40 to 50 minutes from scratch.
Part 1: Building the Workflow Step by Step
1 Create your Telegram bot (2 minutes)
Open Telegram and message @BotFather. Send /newbot, give it a name and a username, and BotFather replies with a bot token that looks like 7123456789:AAH.... Copy it. That single token is all the authentication your bot needs, there is no business verification, phone number, or app review to wait on.
2 Telegram Trigger (Telegram Trigger node)
Add the Telegram Trigger node. Create a Telegram credential and paste your bot token, then set Updates to message. n8n registers the webhook with Telegram for you, so there is no URL to copy or verify by hand.
Here is what a typical incoming update looks like:
{
"message": {
"message_id": 4021,
"from": { "id": 15551234567, "first_name": "James" },
"chat": { "id": 15551234567, "type": "private" },
"date": 1775745137,
"text": "Hi, where is my order #1042?"
}
}
Telegram also sends updates for edited messages, joins, and other events. The next step keeps only real text messages so nothing else reaches the AI.
3 Is Text Message? (IF node)
Not every update is a customer message. Edits, stickers, and service events arrive through the same trigger without a plain message.text. This IF node gates those out to avoid errors downstream.
Configure the condition:
- Left Value:
{{ $json.message.text }} - Operation: exists (is not empty)
The True branch continues to message processing. The False branch simply ends, Telegram does not need any acknowledgment from your workflow.
4 Extract Message Data (Set node)
This Set node flattens the fields we need into a clean object so every downstream node can reference data without long expression chains. The chat ID is the key that identifies each customer’s conversation.
| Field Name | n8n Expression |
|---|---|
chatId |
={{ $json.message.chat.id }} |
message |
={{ $json.message.text }} |
messageId |
={{ $json.message.message_id }} |
firstName |
={{ $json.message.from.first_name }} |
timestamp |
={{ $now.toISO() }} |
After this node, the data looks like:
{
"chatId": 15551234567,
"message": "Hi, where is my order #1042?",
"messageId": 4021,
"firstName": "James",
"timestamp": "2026-04-05T14:32:17.000Z"
}
5 Get Conversation History (HTTP Request to Supabase)
Before calling Gemini, we fetch the customer’s history so the AI understands context: was this their first message? Did they already explain the issue? This node queries Supabase’s REST API for the last 10 messages from this chat.
- Method:
GET - URL:
https://YOUR_SUPABASE_PROJECT_REF.supabase.co/rest/v1/conversations - Header
apikey: your Supabase anon/public key - Header
Authorization:Bearer YOUR_SUPABASE_ANON_KEY - Query param
chat_id:eq.{{ $('Extract Message Data').item.json.chatId }} - Query param
order:created_at.desc - Query param
limit:10
Returns up to 10 rows, or an empty array for a brand-new customer, which is handled gracefully in the next step.
6 Build Gemini Request (Code node)
This is the brain of the operation. The Code node takes the Supabase history, reverses it to chronological order, and builds the exact request body Google Gemini’s generateContent API expects: a system_instruction that sets the bot’s personality, and a contents array of the conversation so far.
const history = $input.all();
const current = $('Extract Message Data').item.json.message;
const chatId = $('Extract Message Data').item.json.chatId;
// System instruction defines the bot's personality and knowledge
const systemText = `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.`;
// Gemini uses role "user" and "model"; map stored "assistant" to "model"
const contents = history.map(i => i.json)
.filter(m => m.role && m.content)
.reverse()
.map(m => ({
role: m.role === 'assistant' ? 'model' : 'user',
parts: [{ text: m.content }]
}));
// Add the current incoming message
contents.push({ role: 'user', parts: [{ text: current }] });
return [{ json: {
chatId,
currentMessage: current,
body: {
system_instruction: { parts: [{ text: systemText }] },
contents
}
} }];
The system instruction is where you make this bot yours. Add your return policy, shipping timeframes, or a short FAQ. The more specific it is, the fewer cases the bot escalates to a human.
7 Google Gemini (HTTP Request to generateContent)
Sends the full conversation to Gemini and receives a natural, contextual reply. Using an HTTP Request node gives you full control over the request format, and Gemini’s free tier keeps this step free at typical support volume.
- Method:
POST - URL:
https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent - Header
x-goog-api-key:YOUR_GEMINI_API_KEY - Header
Content-Type:application/json - Body (JSON expression):
={{ $json.body }}
The response object looks like this:
{
"candidates": [{
"content": {
"role": "model",
"parts": [{ "text": "Hi! I'd be happy to look into order #1042 for you. Could you confirm the email on the order so I can pull it up?" }]
},
"finishReason": "STOP"
}]
}
Access the reply downstream with $json.candidates[0].content.parts[0].text.
Gemini’s free tier covers a generous number of requests per day at no cost. If you outgrow it, gemini-2.5-flash is still one of the cheapest capable models available.
8 Store Conversation (HTTP Request to Supabase)
Saves both the customer’s message and the AI’s reply to Supabase in a single bulk insert. This is what gives the bot memory: every message is persisted for future context retrieval.
- Method:
POST - URL:
https://YOUR_SUPABASE_PROJECT_REF.supabase.co/rest/v1/conversations - Headers: same
apikeyandAuthorizationas Step 5, plusContent-Type: application/jsonandPrefer: return=minimal - Body: a JSON array with two objects, the user message and the assistant reply
=[
{
"chat_id": "={{ $('Build Gemini Request').item.json.chatId }}",
"role": "user",
"content": "={{ $('Build Gemini Request').item.json.currentMessage }}"
},
{
"chat_id": "={{ $('Build Gemini Request').item.json.chatId }}",
"role": "assistant",
"content": "={{ $json.candidates[0].content.parts[0].text }}"
}
]
9 Send Telegram Reply (Telegram node)
Delivers the AI-generated response back to the customer. Add the Telegram node (not the trigger), reuse your bot credential, and configure:
- Resource: Message, Operation: Send Message
- Chat ID:
={{ $('Extract Message Data').item.json.chatId }} - Text:
={{ $('Google Gemini').item.json.candidates[0].content.parts[0].text }}
That is the whole delivery step. No phone number formatting, no Graph API version, no message templates to get approved.
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,
chat_id 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_chat_id ON conversations(chat_id);
CREATE INDEX idx_conversations_created_at ON conversations(created_at);
| Column | Type | Example | Description |
|---|---|---|---|
id |
bigint | 42 | Auto-incrementing primary key |
chat_id |
text | 15551234567 | Telegram chat ID (the conversation key) |
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 |
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.
Testing Your Workflow
- Open Telegram, find your bot by its username, and send it a message like “Hi, where is my order #1042?”
- Open n8n and watch the Executions panel, a new execution should appear within a second.
- Verify the IF node routed to the True branch and every node shows a green checkmark.
- Check your Supabase
conversationstable, two new rows should appear (role=user and role=assistant). - Check Telegram, the AI reply should have arrived.
| Problem | Likely Cause | Fix |
|---|---|---|
| Trigger never fires | Wrong bot token, or the workflow is not active | Re-check the token from @BotFather, save the credential, and turn the workflow Active |
| 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 with a policy allowing insert/select |
| 400 from Gemini | Malformed contents array or empty message | Confirm each item has a role and parts[].text, and that the current message is not empty |
| Gemini 403 or 429 | Missing key or free-tier rate limit hit | Check the x-goog-api-key header, and if you are testing rapidly, wait a moment between messages |
| Bot has no memory between sessions | Supabase table or column name mismatch | Confirm the table is conversations with columns chat_id, role, content, created_at, exact case |
Frequently Asked Questions
Do I need a paid account to run a Telegram support bot?
No. Telegram bots are completely free, with no per-message fee and no business verification. You create a bot with @BotFather in about two minutes, and the only other cost is your n8n hosting, which is free when self hosted.
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 5 to any number you prefer. Higher limits give the AI more context, and on Gemini’s free tier the extra tokens still cost nothing at typical volume.
Can I customize the AI’s tone and knowledge?
Yes, that is the whole point of the system instruction in Step 6. Edit the systemText in the Build Gemini Request node to reflect your brand voice, product catalog, return policies, or specific FAQ answers. The more specific it is, the fewer cases the bot escalates to a human.
What happens if Gemini is temporarily unavailable?
n8n marks the execution as an error and the customer will not receive a reply. To handle this gracefully, add an Error Trigger workflow that catches failures and sends a fallback Telegram message such as “We are having a brief technical issue, a team member will respond within the hour.” You can also enable retry on the Gemini HTTP Request node.
Can I hand off to a human agent when needed?
Yes. In the Build Gemini Request node, check whether the incoming message contains phrases like “speak to a human” or “agent.” If detected, skip the Gemini step and send a Slack or email notification to your support team instead. The conversation history in Supabase gives them full context immediately.
Can the bot look up real order data?
Yes. Add a Shopify node before the Gemini step that fetches the order by number, then include that data in the system instruction so the AI answers with real status. See Shopify order status lookups with n8n for that pattern.
Get the ready-made support agent
You now have everything to deploy a 24/7 Telegram AI support bot with full conversation memory. Prefer to import than build? The Shopify AI Customer Support Agent is a tested, ready-to-import template that runs free on Gemini, so you skip the setup and go straight to testing.
Instant download, works on n8n Cloud and self-hosted
What’s Next?
- Add order lookup: connect your Shopify store so the bot can answer with real-time order status by number.
- Detect frustrated customers: add a sentiment check, and if the AI detects frustration, route to a Slack alert so a human can jump in.
- Go multilingual: Gemini already replies in the customer’s language when you ask it to in the system instruction, no extra step needed.
- Build a weekly digest: use a Schedule trigger to query Supabase for weekly conversation summaries and email them to your team every Monday.
Browse more ready-to-run options in the best n8n AI agent templates for Shopify, or get order and stock alerts on Telegram.