This workflow follows the Google Calendar → Google Sheets recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Sales Lead Qualifier",
"nodes": [
{
"parameters": {
"updates": [
"message",
"callback_query"
],
"additionalFields": {}
},
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.2,
"position": [
0,
0
],
"id": "bb25e52a-0978-4cc2-ac25-0481cc3c0d1e",
"name": "Telegram Trigger",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "https://docs.google.com/spreadsheets/d/1B2_NFRSRoZYwfn6mU9i2FL_-KborBkg6JWdDmhJYpaw/edit",
"mode": "url"
},
"sheetName": {
"__rl": true,
"value": 1644959373,
"mode": "list",
"cachedResultName": "Data",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1B2_NFRSRoZYwfn6mU9i2FL_-KborBkg6JWdDmhJYpaw/edit#gid=1644959373"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Name": "={{ $json.name }}",
"chat_id": "={{ $json.chat_id }}",
"Company": "={{ $json.company }}",
"Message": "={{ $json.original_text }}",
"Enrichment_source": "={{ $json.enrichment_source }}",
"teleg_username": "=@{{ $json.sender_username }}",
"Generated Response": "={{ $json.generated_response }}",
"Timestamp": "={{ $now.format('yyyy-MM-dd HH:mm:ss') }}",
"\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)": "={{ $json.category }}",
"\u0421\u0440\u043e\u0447\u043d\u043e\u0441\u0442\u044c(Urgency)": "={{ $json.urgency }}",
"\u041d\u0430\u043c\u0435\u0440\u0435\u043d\u0438\u0435(Intend)": "={{ $json.intent }}",
"\u0434\u043e\u043f. \u0414\u0430\u0442\u0430(Enrichment)": "={{ $json.enrichment }}"
},
"matchingColumns": [],
"schema": [
{
"id": "Timestamp",
"displayName": "Timestamp",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Name",
"displayName": "Name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Company",
"displayName": "Company",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)",
"displayName": "\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "\u0421\u0440\u043e\u0447\u043d\u043e\u0441\u0442\u044c(Urgency)",
"displayName": "\u0421\u0440\u043e\u0447\u043d\u043e\u0441\u0442\u044c(Urgency)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "\u041d\u0430\u043c\u0435\u0440\u0435\u043d\u0438\u0435(Intend)",
"displayName": "\u041d\u0430\u043c\u0435\u0440\u0435\u043d\u0438\u0435(Intend)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Message",
"displayName": "Message",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "\u0434\u043e\u043f. \u0414\u0430\u0442\u0430(Enrichment)",
"displayName": "\u0434\u043e\u043f. \u0414\u0430\u0442\u0430(Enrichment)",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Enrichment_source",
"displayName": "Enrichment_source",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "chat_id",
"displayName": "chat_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "teleg_username",
"displayName": "teleg_username",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Generated Response",
"displayName": "Generated Response",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
2720,
96
],
"id": "69e9ddcc-8b2b-4840-a0f0-25317718fc8b",
"name": "Append row in sheet",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "=1619231316",
"text": "=\ud83d\udd14 \u041d\u043e\u0432\u044b\u0439 \u043b\u0438\u0434 (\u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u2705)\n\n\ud83d\udc64 \u041a\u043b\u0438\u0435\u043d\u0442: {{ $('Auto Respond?').item.json.Name }}\n\ud83c\udfe2 \u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f: {{ $('Auto Respond?').item.json['\u0434\u043e\u043f. \u0414\u0430\u0442\u0430(Enrichment)']}}\n\ud83d\udcca \u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430: {{ $('Auto Respond?').item.json['\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)'] }}\n\ud83d\udd25\u0421\u0440\u043e\u0447\u043d\u043e\u0441\u0442\u044c: {{ $json['\u0421\u0440\u043e\u0447\u043d\u043e\u0441\u0442\u044c(Urgency)'] }}\n\ud83c\udfaf\u041d\u0430\u043c\u0435\u0440\u0435\u043d\u0438\u0435: {{ $json[\"\u041d\u0430\u043c\u0435\u0440\u0435\u043d\u0438\u0435(Intend)\"] }}\n\n\ud83d\udcac \u041a\u043b\u0438\u0435\u043d\u0442:{{ $('Auto Respond?').item.json['Message'] }}\n\n\u2709\ufe0f \u041e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e: {{ $('Auto Respond?').item.json['Generated Response'] }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
3408,
96
],
"id": "90636008-e2c8-4927-b20f-8817ec1d21a7",
"name": "Message to Managers",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-flash-lite-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-flash-lite-preview"
},
"messages": {
"values": [
{
"content": "=You are an expert sales lead qualifier for Big Dream Lab, an educational company in Astana, Kazakhstan specializing in: Unity 3D game development, Unreal Engine 5, UI/UX design, 3D modeling and animation, Product Management, and AR/VR development. They also offer corporate training programs and partner with Tech Orda government grant program.\n\nWrite a personalized response to this potential client. RESPOND IN THE SAME LANGUAGE AS THE CLIENT'S MESSAGE.\n\nCLIENT'S MESSAGE: {{ $('Telegram Trigger').first().json.message.text }}\n\nLEAD ANALYSIS: {{ $json.candidates[0].content.parts[0].text }}\n\nRULES:\n- NEVER suggest specific dates or times like \"\u0432 \u0447\u0435\u0442\u0432\u0435\u0440\u0433 \u0432 11:00\" or \"\u0437\u0430\u0432\u0442\u0440\u0430 \u0432 15:00\". Instead ask \"\u041a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u0443\u0434\u043e\u0431\u043d\u043e \u0441\u043e\u0437\u0432\u043e\u043d\u0438\u0442\u044c\u0441\u044f?\" or \"\u041f\u043e\u0434\u0441\u043a\u0430\u0436\u0438\u0442\u0435 \u0443\u0434\u043e\u0431\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0434\u043b\u044f \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0437\u0432\u043e\u043d\u043a\u0430?\"\n- If category is \"hot\": be enthusiastin and mention specific solutions\n- If category is \"warm\": be helpful, answer their likely questions, gently push toward a call\n- If category is \"cold\": be friendly, provide general info, invite to a free webinar\n- Keep it under 100 words\n- Address client by name\n- If company is mentioned, reference it naturally\n- End with a clear call-to-action\n- RESPOND IN THE SAME LANGUAGE AS THE CLIENT'S MESSAGE\n\nRETURN ONLY the message text. No JSON, no formatting, no quotes."
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
2192,
96
],
"id": "e29c53a1-fd63-431b-b960-5dc3430d8963",
"name": "respond for user",
"retryOnFail": false,
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-flash-lite-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-flash-lite-preview"
},
"messages": {
"values": [
{
"content": "=You are an expert sales lead qualifier for Big Dream Lab, an educational company in Astana, Kazakhstan specializing in: Unity 3D game development, Unreal Engine 5, UI/UX design, 3D modeling and animation, Product Management, and AR/VR development. They also offer corporate training programs and partner with Tech Orda government grant program.\n\nYour task: Analyze the incoming message from a potential client and return a structured JSON.\n\nINCOMING MESSAGE:\n\"{{ $json.message_text }}\"\n\nSENDER INFO:\n- Name: {{ $json.first_name }}\n- Username:{{ $json.username }}\n\nCLASSIFY the lead into ONE of these categories:\n- \"hot\" \u2014 clear buying intent, specific product interest, ready to discuss terms, mentions company/budget/timeline\n- \"warm\" \u2014 genuine interest but still exploring, asks general questions, comparing options\n- \"cold\" \u2014 vague curiosity, just browsing, students with no clear goal, off-topic\n\nEXTRACT the following fields:\n- name: person's name (from message or sender info)\n- company: company name if mentioned, otherwise null. IMPORTANT: normalize the company name to its official name (e.g., \"\u043a\u0430\u0441\u043f\u0438\" \u2192 \"Kaspi Bank\", \"\u0445\u0430\u043b\u044b\u043a\" \u2192 \"Halyk Bank\", \"\u043a\u0430\u0437\u043f\u043e\u0447\u0442\u0430\" \u2192 \"Kazpost\")\n- category: \"hot\" | \"warm\" | \"cold\"\n- intent: one short sentence describing what they want (in Russian)\n- urgency: \"high\" | \"medium\" | \"low\"\n- reasoning: 1-2 sentences explaining your classification (in English)\n\nRETURN ONLY valid JSON in this exact format, no markdown, no extra text:\n{\n \"name\": \"...\",\n \"company\": \"...\" or null,\n \"category\": \"hot\" | \"warm\" | \"cold\",\n \"intent\": \"...\",\n \"urgency\": \"high\" | \"medium\" | \"low\",\n \"reasoning\": \"...\"\n}"
}
]
},
"simplify": false,
"jsonOutput": true,
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
1856,
96
],
"id": "7bf432ff-17cf-4f5f-a395-95ae312b2892",
"name": "field extracter",
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $json.chat_id }}",
"text": "={{ $json.response_text }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
656,
-304
],
"id": "faee0abd-5c7c-4ea0-b170-e671b89fe7a3",
"name": "Send a text message",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nif (!staticData.auto_respond) staticData.auto_respond = 'on';\nif (!staticData.notify_filter) staticData.notify_filter = 'all';\n\nconst previousData = $input.all()[0].json;\nconst category = (previousData['\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)'] || previousData.category || '').toLowerCase();\nconst filter = staticData.notify_filter;\n\nlet shouldNotify = true;\nif (filter === 'all') {\n shouldNotify = true;\n} else if (filter.includes(',')) {\n shouldNotify = filter.split(',').includes(category);\n} else {\n shouldNotify = filter === category;\n}\n\nreturn [{\n json: {\n ...previousData,\n auto_respond: staticData.auto_respond,\n should_notify: shouldNotify\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2720,
288
],
"id": "83cf65fe-e112-49ff-bd26-40c359bdaa1d",
"name": "Check Auto Respond"
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nconst text = $json.message.text.trim();\nconst chatId = $json.message.chat.id;\n\nif (!staticData.auto_respond) staticData.auto_respond = 'on';\nif (!staticData.notify_filter) staticData.notify_filter = 'all';\n\nlet responseText = '';\n\nif (text === '/on') {\n staticData.auto_respond = 'on';\n responseText = '\u2699\ufe0f \u0410\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442: \u0412\u041a\u041b\u042e\u0427\u0401\u041d \u2705\\n\\n\u041a\u043b\u0438\u0435\u043d\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.';\n\n} else if (text === '/off') {\n staticData.auto_respond = 'off';\n responseText = '\u2699\ufe0f \u0410\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442: \u0412\u042b\u041a\u041b\u042e\u0427\u0415\u041d \ud83d\udd34\\n\\n\u0412\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0441 \u043a\u043d\u043e\u043f\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u044f.';\n\n} else if (text === '/status') {\n const mode = staticData.auto_respond === 'on' ? '\u2705 \u0412\u041a\u041b\u042e\u0427\u0401\u041d' : '\ud83d\udd34 \u0412\u042b\u041a\u041b\u042e\u0427\u0415\u041d';\n const filter = staticData.notify_filter;\n responseText = '\u2699\ufe0f \u0422\u0435\u043a\u0443\u0449\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438:\\n\\n\ud83d\udce8 \u0410\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442: ' + mode + '\\n\ud83d\udd14 \u0424\u0438\u043b\u044c\u0442\u0440: ' + filter;\n\n} else if (text.startsWith('/filter_')) {\n const filter = text.replace('/filter_', '').toLowerCase().trim();\n if (['hot', 'warm', 'cold', 'hot_warm', 'all'].includes(filter)) {\n const saveFilter = filter.replace('_', ',');\n staticData.notify_filter = saveFilter;\n responseText = '\ud83d\udd14 \u0424\u0438\u043b\u044c\u0442\u0440 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439: ' + saveFilter;\n } else {\n responseText = '\u2753 \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440.\\n/filter_hot\\n/filter_hot_warm\\n/filter_all';\n }\n\n} else {\n responseText = '\u2753 \u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430.\\n\\n/on \u2014 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442\\n/off \u2014 \u0432\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442\\n/status \u2014 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\\n/analytics \u2014 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430\\n/filter_hot \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0433\u043e\u0440\u044f\u0447\u0438\u0435\\n/filter_hot_warm \u2014 \u0433\u043e\u0440\u044f\u0447\u0438\u0435 \u0438 \u0442\u0451\u043f\u043b\u044b\u0435\\n/filter_all \u2014 \u0432\u0441\u0435';\n}\n\nreturn [{\n json: {\n chat_id: chatId,\n response_text: responseText\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
464,
-304
],
"id": "9818f50e-f5d6-43bb-915a-249544c5f704",
"name": "Command Handler",
"notesInFlow": false
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "3475a150-b120-485b-8edd-9b5613cf073a",
"leftValue": "={{ $json.auto_respond }}",
"rightValue": "on",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
2912,
288
],
"id": "bc19aff9-e012-4411-b544-68ccb554c7e3",
"name": "Auto Respond?"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "6ab53650-7240-47fb-af6b-bd81730f20f8",
"leftValue": "={{ $json[\"\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)\"] }}",
"rightValue": "warm",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "ef42119b-dbf9-432e-ae0e-3f30a33d9743",
"leftValue": "={{ $json[\"\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)\"] }}",
"rightValue": "hot",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "or"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
2912,
96
],
"id": "d01db5ac-0c51-4953-8472-6646ba47a59f",
"name": "check for category"
},
{
"parameters": {
"chatId": "={{ $json.chat_id }}",
"text": "={{ $json['Generated Response'] }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
3408,
240
],
"id": "a02664ae-c288-4a54-bb28-f45fd185e2f6",
"name": "Auto Send to Client",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "=1619231316",
"text": "=\ud83d\udd14 \u041d\u043e\u0432\u044b\u0439 \u043b\u0438\u0434 (\u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u23f3)\n\n\ud83d\udc64 \u041a\u043b\u0438\u0435\u043d\u0442: {{ $json.Name }}\n\ud83c\udfe2 \u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f: {{ $json['\u0434\u043e\u043f. \u0414\u0430\u0442\u0430(Enrichment)'] }}\n\ud83d\udcca \u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430: {{ $json['\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)'] }}\n\ud83d\udd25 \u0421\u0440\u043e\u0447\u043d\u043e\u0441\u0442\u044c: {{ $json[\"\u0421\u0440\u043e\u0447\u043d\u043e\u0441\u0442\u044c(Urgency)\"] }}\n\ud83c\udfaf \u041d\u0430\u043c\u0435\u0440\u0435\u043d\u0438\u0435: {{ $json[\"\u041d\u0430\u043c\u0435\u0440\u0435\u043d\u0438\u0435(Intend)\"] }}\n\n\ud83d\udcac \u041a\u043b\u0438\u0435\u043d\u0442: {{ $json['Message'] }}\n\n\ud83d\udcdd {{ $('data enricher').item.json.reasoning }}\n\n---GENERATED_RESPONSE---\n{{ $json['Generated Response'] }}\n---END_RESPONSE---",
"replyMarkup": "inlineKeyboard",
"inlineKeyboard": {
"rows": [
{
"row": {
"buttons": [
{
"text": "\u2705 \u041e\u0434\u043e\u0431\u0440\u0438\u0442\u044c",
"additionalFields": {
"callback_data": "=approve_{{ $json.chat_id }}"
}
},
{
"text": "\u274c \u041e\u0442\u043a\u043b\u043e\u043d\u0438\u0442\u044c",
"additionalFields": {
"callback_data": "=reject_{{ $json.chat_id }}"
}
}
]
}
}
]
},
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
3408,
384
],
"id": "db9d1714-e681-4e39-adc6-fd6ae01bddf0",
"name": "Notify Manager Manual",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{ $json.action }}",
"rightValue": "approve",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "ebad1c43-e9b4-44c9-a4ee-5f1f2f509444"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "approve"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "c2220389-1daf-4902-8e3e-2ecbf059ac93",
"leftValue": "={{ $json.action }}",
"rightValue": "reject",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "reject"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
672,
512
],
"id": "e3c07f28-fb0f-48ef-9d36-76bb9360bb2e",
"name": "Switch"
},
{
"parameters": {
"chatId": "={{ $json.client_chat_id }}",
"text": "={{ $json.generated_response }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
960,
496
],
"id": "2bc5d19d-f65f-4729-8716-3583f34c4be6",
"name": "Send to Client",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $('Switch').item.json.manager_chat_id }}",
"text": "\u2705 \u041e\u0442\u0432\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u043a\u043b\u0438\u0435\u043d\u0442\u0443!",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1584,
496
],
"id": "82cef387-99f5-4e9c-8b49-3031ed8c1332",
"name": "Confirm Approve",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "expression",
"output": "={{ $json.callback_query ? 2 : ($json.message && $json.message.text === '/analytics' ? 3 : ($json.message && $json.message.text && $json.message.text.startsWith('/') ? 0 : 1)) }}",
"looseTypeValidation": true
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
208,
-16
],
"id": "ed835abd-b314-48d5-ad7a-4f0357565f3a",
"name": "Router"
},
{
"parameters": {
"chatId": "={{ $json.manager_chat_id }}",
"text": "\u274c \u041b\u0438\u0434 \u043e\u0442\u043a\u043b\u043e\u043d\u0451\u043d.",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
960,
688
],
"id": "8093e4a1-075d-47e2-be84-9eefed079768",
"name": "Confirm Reject",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const geminiResponse = $input.first().json.content.parts[0].text.trim().toLowerCase();\nconst telegramData = $('Telegram Trigger').first().json;\n\nconst intent = geminiResponse.includes('lead') ? 'lead' : 'question';\n\nreturn [{\n json: {\n intent: intent,\n message_text: telegramData.message.text,\n chat_id: telegramData.message.chat.id,\n first_name: telegramData.message.from.first_name,\n username: telegramData.message.from.username\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1312,
192
],
"id": "08cf55ff-4ff5-4322-9d67-ce41947cd12d",
"name": "Parse Intent"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-2.5-flash",
"mode": "list",
"cachedResultName": "models/gemini-2.5-flash"
},
"messages": {
"values": [
{
"content": "=Classify the following message into exactly ONE category.\n\nMESSAGE: \"{{ $json.message.text }}\"\n\nCategories:\n- \"lead\" \u2014 the person wants to BUY something, hire for training, get corporate education, become a student, enroll in a course. They express INTENT to purchase or interest in becoming a client.\n- \"question\" \u2014 the person asks a QUESTION about courses, pricing, schedule, requirements, grants, company info. They want INFORMATION, not ready to buy yet.\n\nExamples:\n- \"\u0425\u043e\u0447\u0443 \u043a\u0443\u0440\u0441 Python \u0434\u043b\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Kaspi, 30 \u0447\u0435\u043b\u043e\u0432\u0435\u043a\" \u2192 lead\n- \"\u0421\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u043e\u0438\u0442 \u043a\u0443\u0440\u0441 Unity?\" \u2192 question\n- \"\u041a\u043e\u0433\u0434\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043d\u0430\u0431\u043e\u0440?\" \u2192 question\n- \"\u042f HR \u0438\u0437 Halyk Bank, \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u0443\u0447\u0438\u0442\u044c \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\" \u2192 lead\n- \"\u041a\u0430\u043a\u0438\u0435 \u043a\u0443\u0440\u0441\u044b \u0435\u0441\u0442\u044c?\" \u2192 question\n- \"\u0425\u043e\u0447\u0443 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043a\u0443\u0440\u0441 UI/UX\" \u2192 lead\n- \"\u041d\u0443\u0436\u0435\u043d \u043b\u0438 \u043d\u043e\u0443\u0442\u0431\u0443\u043a?\" \u2192 question\n- \"\u041a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0433\u0440\u0430\u043d\u0442 \u0422\u0435\u0445 \u041e\u0440\u0434\u0430?\" \u2192 question\n\nRETURN ONLY ONE WORD: lead or question"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
960,
192
],
"id": "1e1d6126-177c-4641-997e-5c525df3b5d1",
"name": "Intent Classifier",
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"url": "http://rag:5000/ask",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "q",
"value": "={{ $json.message_text }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
1872,
288
],
"id": "186193f2-162e-4c58-9f89-7642b9af8110",
"name": "HTTP Request"
},
{
"parameters": {
"chatId": "={{ $('Parse Intent').first().json.chat_id }}",
"text": "={{ $json.answer }} ",
"additionalFields": {
"parse_mode": "HTML"
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
2080,
288
],
"id": "c85170ed-08d7-4c05-b915-9083db425a53",
"name": "answer to question",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "https://docs.google.com/spreadsheets/d/1B2_NFRSRoZYwfn6mU9i2FL_-KborBkg6JWdDmhJYpaw/edit",
"mode": "url"
},
"sheetName": {
"__rl": true,
"value": 1644959373,
"mode": "list",
"cachedResultName": "Data",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1B2_NFRSRoZYwfn6mU9i2FL_-KborBkg6JWdDmhJYpaw/edit#gid=1644959373"
},
"filtersUI": {
"values": []
},
"combineFilters": "OR",
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
464,
880
],
"id": "58fde7eb-0e28-4aa7-a20f-ab023dbdc59b",
"name": "Get row(s) in sheet",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const rows = $input.all().map(item => item.json);\nconst telegramData = $('Telegram Trigger').first().json;\n\nconst now = new Date();\nconst todayStr = now.toISOString().split('T')[0];\nconst yesterday = new Date(now);\nyesterday.setDate(yesterday.getDate() - 1);\nconst yesterdayStr = yesterday.toISOString().split('T')[0];\n\nconst recentRows = rows.filter(row => {\n const ts = row.Timestamp || '';\n return ts.startsWith(todayStr) || ts.startsWith(yesterdayStr);\n});\n\n// Course detection by keywords\nconst courseKeywords = {\n 'Unity 3D': ['unity', '\u044e\u043d\u0438\u0442\u0438', 'game dev', 'gamedev', '\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0438\u0433\u0440'],\n 'Unreal Engine': ['unreal', '\u0430\u043d\u0440\u0438\u043b', 'ue5', 'ue4'],\n 'UI/UX \u0434\u0438\u0437\u0430\u0439\u043d': ['ui/ux', 'ui ux', 'uiux', '\u0434\u0438\u0437\u0430\u0439\u043d', 'figma', 'ux design'],\n '3D \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435': ['3d', 'blender', 'maya', '\u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435', '\u0430\u043d\u0438\u043c\u0430\u0446\u0438\u044f'],\n 'Product Management': ['product management', '\u043f\u0440\u043e\u0434\u0430\u043a\u0442', '\u043c\u0435\u043d\u0435\u0434\u0436\u043c\u0435\u043d\u0442 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430'],\n 'AR/VR': ['ar/vr', 'ar vr', 'arvr', '\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c', '\u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c'],\n 'Data Science / ML': ['data science', 'ds', 'machine learning', 'ml', 'python', '\u043f\u0438\u0442\u043e\u043d']\n};\n\nfunction detectCourse(message) {\n const lower = (message || '').toLowerCase();\n for (const [course, keywords] of Object.entries(courseKeywords)) {\n for (const kw of keywords) {\n if (lower.includes(kw)) return course;\n }\n }\n return '\u0414\u0440\u0443\u0433\u043e\u0435';\n}\n\nlet totalHot = 0, totalWarm = 0, totalCold = 0;\nfor (const row of rows) {\n const cat = (row['\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)'] || '').toLowerCase();\n if (cat === 'hot') totalHot++;\n else if (cat === 'warm') totalWarm++;\n else if (cat === 'cold') totalCold++;\n}\n\nlet recentHot = 0, recentWarm = 0, recentCold = 0;\nconst courseCounts = {};\nconst companies = {};\n\nfor (const row of recentRows) {\n const cat = (row['\u0422\u0438\u043f \u0437\u0432\u043e\u043d\u043a\u0430(Category)'] || '').toLowerCase();\n if (cat === 'hot') recentHot++;\n else if (cat === 'warm') recentWarm++;\n else if (cat === 'cold') recentCold++;\n\n const course = detectCourse(row.Message);\n courseCounts[course] = (courseCounts[course] || 0) + 1;\n\n const company = row.Company || '';\n if (company && company !== 'null' && company !== '') {\n companies[company] = (companies[company] || 0) + 1;\n }\n}\n\nconst topCourses = Object.entries(courseCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 3)\n .map((c, i) => `${i + 1}. ${c[0]} \u2014 ${c[1]}`)\n .join('\\n');\n\nconst topCompanies = Object.entries(companies)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 3)\n .map((c, i) => `${i + 1}. ${c[0]} \u2014 ${c[1]}`)\n .join('\\n');\n\nconst report = `\ud83d\udcca \u0410\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430\n\n\ud83d\udcc5 \u0417\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 2 \u0434\u043d\u044f:\n\ud83d\udce5 \u041b\u0438\u0434\u043e\u0432: ${recentRows.length}\n\ud83d\udd25 Hot: ${recentHot} | \ud83d\udfe1 Warm: ${recentWarm} | \ud83d\udd35 Cold: ${recentCold}\n\n\ud83d\udcc5 \u0417\u0430 \u0432\u0441\u0451 \u0432\u0440\u0435\u043c\u044f:\n\ud83d\udce5 \u041b\u0438\u0434\u043e\u0432: ${rows.length}\n\ud83d\udd25 Hot: ${totalHot} | \ud83d\udfe1 Warm: ${totalWarm} | \ud83d\udd35 Cold: ${totalCold}\n\n\ud83c\udfc6 \u0422\u043e\u043f \u043a\u0443\u0440\u0441\u044b (2 \u0434\u043d\u044f):\n${topCourses || '\u041d\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445'}\n\n\ud83c\udfe2 \u0422\u043e\u043f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 (2 \u0434\u043d\u044f):\n${topCompanies || '\u041d\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445'}`;\n\nreturn [{\n json: {\n chat_id: telegramData.message.chat.id,\n report: report\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
880
],
"id": "1836062d-e209-4c75-ad03-06158c01b2e6",
"name": "Code in JavaScript1"
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nif (!staticData.waiting_for_schedule) {\n staticData.waiting_for_schedule = {};\n}\n\nconst preparedData = $('data prepare for approve').first().json;\nconst clientChatId = String(preparedData.client_chat_id);\nconst managerChatId = String(preparedData.manager_chat_id);\n\nstaticData.waiting_for_schedule[clientChatId] = {\n waiting: true,\n manager_chat_id: managerChatId,\n timestamp: new Date().toISOString()\n};\n\nreturn [$input.all()[0]];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1168,
496
],
"id": "09e2dc88-06a0-4a70-8594-da4a60a50d27",
"name": "Save Waiting State"
},
{
"parameters": {
"chatId": "={{ $json.result.chat.id }}",
"text": "\u041a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u0443\u0434\u043e\u0431\u043d\u043e \u0441\u043e\u0437\u0432\u043e\u043d\u0438\u0442\u044c\u0441\u044f? \u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0434\u0430\u0442\u0443 \u0438 \u0432\u0440\u0435\u043c\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \"\u0417\u0430\u0432\u0442\u0440\u0430 \u0432 15:00\" \u0438\u043b\u0438 \"\u0412 \u0441\u0440\u0435\u0434\u0443 \u0432 10 \u0443\u0442\u0440\u0430\".",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1376,
496
],
"id": "f1456e89-6467-485d-b00e-76c2c121e53a",
"name": "ask for a time",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nconst chatId = String($json.message.chat.id);\n\n// Debug: \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0442\u043e \u0432 Static Data\nconst debugInfo = JSON.stringify(staticData.waiting_for_schedule || {});\nconst allKeys = Object.keys(staticData.waiting_for_schedule || {});\n\nconst isWaiting = staticData.waiting_for_schedule && \n staticData.waiting_for_schedule[chatId] &&\n staticData.waiting_for_schedule[chatId].waiting;\n\nlet managerChatId = '';\nif (isWaiting) {\n managerChatId = staticData.waiting_for_schedule[chatId].manager_chat_id;\n}\n\nreturn [{\n json: {\n ...$json,\n is_waiting: isWaiting ? true : false,\n manager_chat_id: managerChatId,\n debug_chat_id: chatId,\n debug_keys: allKeys,\n debug_static: debugInfo\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
464,
16
],
"id": "a84d5e3e-4e75-46fb-a333-b93c9e37c4a2",
"name": "check waiting?"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-3.1-flash-lite-preview",
"mode": "list",
"cachedResultName": "models/gemini-3.1-flash-lite-preview"
},
"messages": {
"values": [
{
"content": "=Extract date and time from this message. Today is {{ $now.format('yyyy-MM-dd') }} and today is {{ $now.format('EEEE') }}.\n\nMESSAGE: \"{{ $json.message.text }}\"\n\nRules:\n- \"\u0417\u0430\u0432\u0442\u0440\u0430 \u0432 15:00\" \u2192 tomorrow's date, 15:00\n- \"\u0412 \u0441\u0440\u0435\u0434\u0443 \u0432 10\" \u2192 next Wednesday, 10:00\n- \"\u041f\u043e\u0441\u043b\u0435\u0437\u0430\u0432\u0442\u0440\u0430 \u0443\u0442\u0440\u043e\u043c\" \u2192 day after tomorrow, 09:00\n- If only date mentioned, default time is 10:00\n- If only time mentioned, default date is tomorrow\n- If cannot parse \u2192 set \"valid\": false\n\nRETURN ONLY JSON:\n{\"date\": \"YYYY-MM-DD\", \"time\": \"HH:MM\", \"valid\": true}\nor\n{\"date\": \"\", \"time\": \"\", \"valid\": false}"
}
]
},
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
960,
-128
],
"id": "3b01c066-c192-4414-ba6b-28f39080289a",
"name": "Parse a DateTime",
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const geminiResponse = $input.first().json.content.parts[0].text;\nconst parsed = JSON.parse(geminiResponse);\nconst telegramData = $('Telegram Trigger').first().json;\nconst chatId = String(telegramData.message.chat.id);\nconst staticData = $getWorkflowStaticData('global');\nconst waitingData = staticData.waiting_for_schedule[chatId];\n\nif (!parsed.valid) {\n return [{\n json: {\n valid: false,\n chat_id: chatId,\n manager_chat_id: waitingData.manager_chat_id\n }\n }];\n}\n\n// Clear waiting state\ndelete staticData.waiting_for_schedule[chatId];\n\nconst startDateTime = parsed.date + 'T' + parsed.time + ':00';\nconst endHour = String(parseInt(parsed.time.split(':')[0]) + 1).padStart(2, '0');\nconst endDateTime = parsed.date + 'T' + endHour + ':' + parsed.time.split(':')[1] + ':00';\n\nreturn [{\n json: {\n valid: true,\n date: parsed.date,\n time: parsed.time,\n start: startDateTime,\n end: endDateTime,\n chat_id: chatId,\n manager_chat_id: 1619231316,\n client_name: telegramData.message.from.first_name || '\u041a\u043b\u0438\u0435\u043d\u0442'\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1312,
-128
],
"id": "974b4eff-b927-4c6e-ad3b-192c87cf1401",
"name": "Prepare Calendar Event"
},
{
"parameters": {
"chatId": "={{ $json.chat_id }}",
"text": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0442\u044c \u0434\u0430\u0442\u0443. \u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0435\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \"20 \u0430\u043f\u0440\u0435\u043b\u044f \u0432 15:00\".",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1856,
-96
],
"id": "edd0e05d-c1d7-4e6a-8a98-987a922d7339",
"name": "Ask Again",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"calendar": {
"__rl": true,
"value": "0d2d2c0371d0a0ab40dade60d1f12fa11ae78242ba3c81e2e2709764786f347f@group.calendar.google.com",
"mode": "list",
"cachedResultName": "\u0432\u0441\u0442\u0440\u0435\u0447\u0438"
},
"start": "={{ $json.start }}",
"end": "={{ $json.end }}",
"additionalFields": {
"description": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u043e Sales Lead Qualifier Bot",
"summary": "=\u0417\u0432\u043e\u043d\u043e\u043a \u0441 {{ $json.client_name }}"
}
},
"type": "n8n-nodes-base.googleCalendar",
"typeVersion": 1.3,
"position": [
1856,
-256
],
"id": "32061aa9-85f7-4341-82b1-bc4125769845",
"name": "Create Meeting",
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $('check for validity').item.json.chat_id }}",
"text": "=\u2705 \u0412\u0441\u0442\u0440\u0435\u0447\u0430 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u043d\u0430 {{ $('check for validity').item.json.date }} \u0432 {{ $('check for validity').item.json.time }}. \u0416\u0434\u0451\u043c \u0432\u0430\u0441!",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
2064,
-256
],
"id": "9cffb594-2470-44b1-9de0-551fe9869735",
"name": "Confirm to Client",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $json.result.chat.id }}",
"text": "=\ud83d\udcc5 \u041d\u043e\u0432\u0430\u044f \u0432\u0441\u0442\u0440\u0435\u0447\u0430! \ud83d\udc64 {{ $json.result.chat.first_name }} \ud83d\udd50 {{ $('check for validity').item.json.date }} \u0432 {{ $('check for validity').item.json.time }}\ud83d\udccd \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Google Calendar",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
2256,
-256
],
"id": "c07d8c6f-1e18-4265-b1d3-bfd99aee3143",
"name": "time confirmation to manager",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "d3a03a1b-52b0-4b09-ace2-02a9d5e666de",
"leftValue": "={{ $json.valid }}",
"rightValue": "true",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1520,
-128
],
"id": "bd57af41-d5c5-4928-946d-7304ecae98fd",
"name": "check for validity"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "520351ed-09e1-4071-b333-e1b35b39d18b",
"leftValue": "={{ $json.is_waiting }} ",
"rightValue": "true",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
656,
16
],
"id": "8262dd7f-72d6-4eec-b158-c52884349bf2",
"name": "client waiting?"
},
{
"parameters": {
"chatId": "={{ $json.chat_id }}",
"text": "={{ $json.report }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
880,
880
],
"id": "e4f22270-7973-4dbf-a40a-5e8d5c643aaf",
"name": "send a report",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const callbackData = $input.first().json.callback_query.data;\nconst parts = callbackData.split('_');\nconst action = parts[0];\nconst clientChatId = parts.slice(1).join('_');\nconst managerChatId = $input.first().json.callback_query.message.chat.id;\nconst messageText = $input.first().json.callback_query.message.text;\n\nlet generatedResponse = \"\";\nif (messageText.includes(\"---GENERATEDRESPONSE---\")) {\n generatedResponse = messageText\n .split(\"---GENERATEDRESPONSE---\")[1]\n .split(\"---ENDRESPONSE---\")[0]\n .trim();\n}\n\nreturn [{\n json: {\n action: action,\n client_chat_id: clientChatId,\n manager_chat_id: managerChatId,\n generated_response: generatedResponse\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
464,
512
],
"id": "75ef0a24-2a5b-4e01-a0cf-5211026289b5",
"name": "data prepare for approve"
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nif (!staticData.waiting_for_schedule) {\n staticData.waiting_for_schedule = {};\n}\n\nconst codeData = $('data enricher').first().json;\nconst clientChatId = String(codeData.chat_id);\nconst managerChatId = clientChatId;\n\nstaticData.waiting_for_schedule[clientChatId] = {\n waiting: true,\n manager_chat_id: managerChatId,\n timestamp: new Date().toISOString()\n};\n\nreturn [$input.all()[0]];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
3616,
240
],
"id": "7905ec97-ddf4-45dc-9e44-d02ae06e2324",
"name": "set waiting"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "0e5b163b-d295-44bd-bb18-e1c826bb85a1",
"leftValue": "={{ $json.should_notify }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
3184,
400
],
"id": "23344536-5570-41fb-9f49-98bf3ee383a8",
"name": "should notify?"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "ad08eb74-0bc8-431f-9393-d64f67ff7e2d",
"leftValue": "={{ $json.should_notify }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
3184,
112
],
"id": "6b668c3f-749c-49e1-8f19-1914a6ab0865",
"name": "should notify?1"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "e000d542-e62e-48d2-821a-2b729d23f787",
"leftValue": "={{ $json.intent }}",
"rightValue": "lead",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1520,
192
],
"id": "e9940fe3-6f46-45f1-a3cc-bea662c10577",
"name": "intend identifier"
},
{
"parameters": {
"jsCode": "const gemini1 = $('field extracter').first().json;\nconst gemini2 = $('respond for user').first().json;\nconst telegramData = $('Telegram Trigger').first().json;\n\nconst parsed = JSON.parse($('field extracter').first().json.candidates[0].content.parts[0].text);\nconst generatedResponse = $input.first().json.content.parts[0].text;\n\n// Known companies database - no need to scrape these\nconst knownCompanies = {\n \"Kaspi Bank\": {\n industry: \"Fintech / Digital Banking\",\n size: \"15,000+ \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\",\n description: \"\u041a\u0440\u0443\u043f\u043d\u0435\u0439\u0448\u0430\u044f \u0444\u0438\u043d\u0442\u0435\u0445-\u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430\u043d\u0430: \u0431\u0430\u043d\u043a\u0438\u043d\u0433, \u043c\u0430\u0440\u043a\u0435\u0442\u043f\u043b\u0435\u0439\u0441, \u043f\u043b\u0430\u0442\u0435\u0436\u0438, \u043f\u0443\u0442\u0435\u0448\u0435\u0441\u0442\u0432\u0438\u044f\",\n website: \"kaspi.kz\"\n },\n \"Halyk Bank\": {\n industry: \"Banking\",\n size: \"12,000+ \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\",\n description: \"\u041a\u0440\u0443\u043f\u043d\u0435\u0439\u0448\u0438\u0439 \u0431\u0430\u043d\u043a \u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430\u043d\u0430 \u043f\u043e \u0430\u043a\u0442\u0438\u0432\u0430\u043c, \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u0431\u0430\u043d\u043a\u0438\u043d\u0433 \u0434\u043b\u044f \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0438 \u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043b\u0438\u0446\",\n website: \"halykbank.kz\"\n },\n \"Kolesa Group\": {\n industry: \"Tech / Classifieds\",\n size: \"1,500+ \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\",\n description: \"\u041a\u0440\u0443\u043f\u043d\u0435\u0439\u0448\u0430\u044f IT-\u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430\u043d\u0430: Kolesa.kz, Krisha.kz, Market.kz\",\n website: \"kolesa.group\"\n },\n \"Air Astana\": {\n industry: \"Aviation\",\n size: \"5,000+ \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\",\n description: \"\u041d\u0430\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0430\u0432\u0438\u0430\u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430\u043d\u0430, \u0444\u043b\u0430\u0433\u043c\u0430\u043d\u0441\u043a\u0438\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0437\u0447\u0438\u043a\",\n website: \"airastana.com\"\n },\n \"Freedom Finance\": {\n industry: \"Fintech / Investments\",\n size: \"3,000+ \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\",\n description: \"\u0418\u043d\u0432\u0435\u0441\u0442\u0438\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f, \u0431\u0440\u043e\u043a\u0435\u0440\u0441\u043a\u0438\u0435 \u0443\u0441\u043b\u0443\u0433\u0438, \u0431\u0430\u043d\u043a\u0438\u043d\u0433, \u0441\u0442\u0440\u0430\u0445\u043e\u0432\u0430\u043d\u0438\u0435\",\n website: \"ffin.kz\"\n },\n \"Forte Bank\": {\n industry: \"Banking\",\n size: \"4,000+ \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\",\n description: \"\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u0431\u0430\u043d\u043a \u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430\u043d\u0430, \u0440\u043e\u0437\u043d\u0438\u0447\u043d\u044b\u0439 \u0438 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0431\u0430\u043d\u043a\u0438\u043d\u0433\",\n website: \"forte.kz\"\n },\n \"Technodom\": {\n industry: \"Retail / Electronics\",\n size: \"3,000+ \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\",\n description: \"\u041a\u0440\u0443\u043f\u043d\u0435\u0439\u0448\u0430\u044f \u0441\u0435\u0442\u044c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u0438\u043a\u0438 \u0438 \u0431\u044b\u0442\u043e\u0432\u043e\u0439 \u0442\u0435\u0445\u043d\u0438\u043a\u0438 \u0432 \u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430\u043d\u0435\",\n website: \"technodom.kz\"\n },\n \"Kazakhtelecom\": {\n industry: \"Telecom\",\n size: \"20,000+ \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432\",\n description: \"\u041d\u0430\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0435\u043b\u0435\u043a\u043e\u043c-\u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 \u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430\u043d\u0430\",\n website: \"telecom.kz\"\n }\n};\n\n// Check if company is in our known database\nlet enrichment = null;\nlet enrichmentSource = \"none\";\n\nif (parsed.company && knownCompanies[parsed.company]) {\n const info = knownCompanies[parsed.company];\n enrichment = `${parsed.company} \u2014 ${info.industry}, ${info.size}. ${info.description}`;\n enrichmentSource = \"known_database\";\n} else if (parsed.company) {\n enrichment = `\u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \"${parsed.company}\" \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0432 \u0431\u0430\u0437\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439. \u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0440\u0443\u0447\u043d\u043e\u0439 \u0430\u043d\u0430\u043b\u0438\u0437.`;\n enrichmentSource = \"unknown_needs_research\";\n} else {\n enrichment = \"\u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0430\";\n enrichmentSource = \"not_provided\";\n}\n\nreturn [{\n json: {\n ...parsed,\n generated_response: generatedResponse,\n enrichment: enrichment,\n enrichment_source: enrichmentSource,\n original_text: telegramData.message.text,\n chat_id: telegramData.message.chat.id,\n sender_name: telegramData.message.from.first_name,\n sender_username: telegramData.message.from.username\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2496,
96
],
"id": "6a796eaa-48a8-4d92-9b35-fe02a69a87fb",
"name": "data enricher"
}
],
"connections": {
"Telegram Trigger": {
"main": [
[
{
"node": "Router",
"type": "main",
"index": 0
}
]
]
},
"Append row in sheet": {
"main": [
[
{
"node": "check for category",
"type": "main",
"index": 0
}
]
]
},
"Message to Managers": {
"main": [
[]
]
},
"respond for user": {
"main": [
[
{
"node": "data enricher",
"type": "main",
"index": 0
}
]
]
},
"field extracter": {
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleCalendarOAuth2ApigooglePalmApigoogleSheetsOAuth2ApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Sales Lead Qualifier. Uses telegramTrigger, googleSheets, telegram, googleGemini. Event-driven trigger; 41 nodes.
Source: https://github.com/aibarC/sales-lead-qualifier/blob/2b5743e973d0bc4849474ae64938d8534fafa3e3/workflow.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow is a complete, AI-powered content engine designed to help automation experts build their personal brand on LinkedIn. It transforms a technical n8n workflow (in JSON format) into a polish
Ask questions like “How much did I spend on food last month?” and get instant answers from your financial data — directly in Telegram.
Most expense tracker apps (like Money Lover, Spendee, or Wallet) have a common friction point: Data Entry. You have to unlock your phone, find the app, wait for it to load, navigate menus, and manuall
> ⚠️ Disclaimer: This workflow uses Community Nodes and must be run on a self-hosted instance of n8n.
Viral Tik Tok Clone Finder. Uses httpRequest, telegramTrigger, openAi, googleSheets. Event-driven trigger; 41 nodes.