This workflow follows the HTTP Request → Telegram 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": "Local AI Agent (HTTP-based)",
"nodes": [
{
"parameters": {
"path": "telegram-ai-agent",
"httpMethod": "POST",
"responseMode": "onReceived",
"options": {}
},
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
-520,
300
]
},
{
"parameters": {
"functionCode": "const body = $json.body || {};\nconst message = body.message || {};\nconst chat = message.chat || {};\nconst text = message.text || '';\nconst document = message.document;\n\nconst isWebSearch = text.startsWith('/search ');\nconst isDocument = !!document;\n\nreturn [{\n chatId: chat.id,\n originalText: text,\n isWebSearch,\n isDocument,\n document,\n query: isWebSearch ? text.replace('/search ', '') : text\n}];"
},
"name": "Parse Telegram",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
-300,
300
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "Thinking...",
"additionalFields": {}
},
"name": "Telegram Typing",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
-90,
140
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.isDocument }}",
"value2": true
}
],
"string": []
}
},
"name": "If Document",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
-90,
560
]
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.isWebSearch }}",
"value2": true
}
],
"string": []
}
},
"name": "If Web Search",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
90,
420
]
},
{
"parameters": {
"requestMethod": "GET",
"url": "={{ 'https://api.telegram.org/bot' + $env.TELEGRAM_TOKEN + '/getFile' }}",
"jsonParameters": true,
"options": {},
"sendQuery": true,
"queryParametersJson": "={{ JSON.stringify({ \"file_id\": $json.document.file_id }) }}"
},
"name": "Get File Path",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
200,
700
]
},
{
"parameters": {
"requestMethod": "GET",
"url": "={{ 'https://api.telegram.org/file/bot' + $env.TELEGRAM_TOKEN + '/' + $json.result.file_path }}",
"jsonParameters": true,
"options": {},
"responseFormat": "string"
},
"name": "Download File",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
400,
700
]
},
{
"parameters": {
"functionCode": "const text = $json.data || '';\n// Simple chunking by paragraphs or length\nconst chunkSize = 1000;\nconst chunks = [];\n\n// Basic split by newlines first\nconst paragraphs = text.split(/\\n\\s*\\n/);\n\nlet currentChunk = '';\nfor (const p of paragraphs) {\n if ((currentChunk + p).length > chunkSize) {\n if (currentChunk) chunks.push(currentChunk.trim());\n currentChunk = p;\n } else {\n currentChunk += (currentChunk ? '\\n\\n' : '') + p;\n }\n}\nif (currentChunk) chunks.push(currentChunk.trim());\n\n// If no paragraphs (single line), force split\nif (chunks.length === 0 && text.length > 0) {\n for (let i = 0; i < text.length; i += chunkSize) {\n chunks.push(text.slice(i, i + chunkSize));\n }\n}\n\nreturn chunks.map((chunk, index) => ({\n chunk,\n chunkIndex: index,\n totalChunks: chunks.length,\n fileName: $node[\"Parse Telegram\"].json.document.file_name,\n chatId: $node[\"Parse Telegram\"].json.chatId\n}));"
},
"name": "Split Text",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
600,
700
]
},
{
"parameters": {
"requestMethod": "POST",
"url": "={{ ($env.OLLAMA_BASE_URL || 'http://ollama:11434') + '/api/embeddings' }}",
"jsonParameters": true,
"options": {},
"headerParameters": [
{
"name": "Authorization",
"value": "={{ $env.OLLAMA_API_KEY ? ('Bearer ' + $env.OLLAMA_API_KEY) : '' }}"
}
],
"bodyParametersJson": "={{ JSON.stringify({\n \"model\": $env.OLLAMA_MODEL || \"llama3.2:3b\",\n \"prompt\": $json.chunk\n}) }}"
},
"name": "Embed Chunk",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
800,
700
]
},
{
"parameters": {
"requestMethod": "POST",
"url": "={{ 'http://chroma:8000/api/v1/collections/' + $('Merge Collection ID').first().json.collectionId + '/upsert' }}",
"jsonParameters": true,
"options": {},
"bodyParametersJson": "={{ JSON.stringify({\n \"ids\": [ $('Parse Telegram').first().json.document.file_id + '_' + $json.chunkIndex ],\n \"embeddings\": [ $json.embedding ],\n \"documents\": [ $json.chunk ],\n \"metadatas\": [ { \"source\": \"file\", \"filename\": $json.fileName } ]\n}) }}"
},
"name": "Upsert Chunk",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
1000,
700
]
},
{
"parameters": {
"chatId": "={{ $('Parse Telegram').first().json.chatId }}",
"text": "={{ 'I have read \"' + $('Parse Telegram').first().json.document.file_name + '\". Processed ' + ($('Split Text').item.json.chunkIndex + 1) + ' of ' + $('Split Text').item.json.totalChunks + ' chunks.' }}",
"additionalFields": {}
},
"name": "Notify Progress",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
1200,
700
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"requestMethod": "POST",
"url": "http://chroma:8000/api/v1/collections",
"jsonParameters": true,
"options": {},
"bodyParametersJson": "{\"name\": \"ai_memory\", \"get_or_create\": true}"
},
"name": "Get Collection",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
-200,
420
]
},
{
"parameters": {
"functionCode": "const original = $node[\"Parse Telegram\"].json;\nconst collection = $json;\nreturn [{ ...original, collectionId: collection.id }];"
},
"name": "Merge Collection ID",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
200,
420
]
},
{
"parameters": {
"requestMethod": "POST",
"url": "={{ ($env.OLLAMA_BASE_URL || 'http://ollama:11434') + '/api/embeddings' }}",
"jsonParameters": true,
"options": {},
"headerParameters": [
{
"name": "Authorization",
"value": "={{ $env.OLLAMA_API_KEY ? ('Bearer ' + $env.OLLAMA_API_KEY) : '' }}"
}
],
"bodyParametersJson": "={{ JSON.stringify({\n \"model\": $env.OLLAMA_MODEL || \"llama3.2:3b\",\n \"prompt\": $json.query\n}) }}"
},
"name": "Generate Query Embedding",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
140,
220
]
},
{
"parameters": {
"requestMethod": "POST",
"url": "={{ 'http://chroma:8000/api/v1/collections/' + $node[\"Merge Collection ID\"].json.collectionId + '/query' }}",
"jsonParameters": true,
"options": {},
"bodyParametersJson": "={{ JSON.stringify({ \"query_embeddings\": [ $json.embedding ], \"n_results\": 3 }) }}"
},
"name": "Chroma Query",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
340,
220
]
},
{
"parameters": {
"functionCode": "const results = $json.documents || [];\nlet context = '';\nif (Array.isArray(results) && results.length > 0) {\n const docs = results[0];\n if (Array.isArray(docs)) {\n context = docs.join('\\n');\n }\n}\n\n// Access original data from Parse Telegram node\nconst originalData = $('Parse Telegram').first().json;\n\nreturn [{\n chatId: originalData.chatId,\n originalText: originalData.originalText,\n query: originalData.query,\n isWebSearch: originalData.isWebSearch,\n memoryContext: context\n}];"
},
"name": "Build RAG Context",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
540,
220
]
},
{
"parameters": {
"requestMethod": "POST",
"url": "https://api.tavily.com/search",
"jsonParameters": true,
"options": {},
"bodyParametersJson": "={{ JSON.stringify({\n \"api_key\": $env.TAVILY_API_KEY,\n \"query\": $json.query,\n \"search_depth\": \"advanced\",\n \"max_results\": 7,\n \"include_answer\": true,\n \"include_raw_content\": true\n}) }}"
},
"name": "Tavily Search",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
140,
520
]
},
{
"parameters": {
"functionCode": "let webContext = '';\n\nif ($json.answer) {\n webContext += `Tavily Direct Answer:\\n${$json.answer}\\n\\n`;\n}\n\nif (Array.isArray($json.results)) {\n webContext += \"Search Results:\\n\";\n webContext += $json.results.map((r, i) => `[${i+1}] Title: ${r.title}\\nURL: ${r.url}\\nContent: ${r.content}`).join('\\n\\n');\n}\n\n// Access original data from Parse Telegram node\nconst originalData = $('Parse Telegram').first().json;\n\nreturn [{\n chatId: originalData.chatId,\n originalText: originalData.originalText,\n query: originalData.query,\n memoryContext: '',\n webContext\n}];"
},
"name": "Build Web Context",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
360,
520
]
},
{
"parameters": {
"functionCode": "const system = '\u0422\u044b \u0440\u0443\u0441\u0441\u043a\u043e\u044f\u0437\u044b\u0447\u043d\u044b\u0439 \u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442 \u0432 Telegram. \u041e\u0442\u0432\u0435\u0447\u0430\u0439 \u0441\u0442\u0440\u043e\u0433\u043e \u043f\u043e \\\"Web search context\\\". \u041d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0437\u043d\u0430\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u043d\u0435\u0442 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435, \u043f\u0440\u044f\u043c\u043e \u0441\u043a\u0430\u0436\u0438 \u043e\u0431 \u044d\u0442\u043e\u043c. \u041f\u0440\u0438\u0432\u043e\u0434\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 [1], [2]. \u0411\u0443\u0434\u044c \u043a\u0440\u0430\u0442\u043e\u043a.';\n\nconst parts = [];\nif ($json.memoryContext) {\n parts.push('Memory context:\\n' + $json.memoryContext);\n}\nif ($json.webContext) {\n parts.push('Web search context:\\n' + $json.webContext);\n}\nconst context = parts.join('\\n\\n');\n\nlet content = '';\nif (context) {\n content = `<<CONTEXT_START>>\\n${context}\\n<<CONTEXT_END>>\\n\\n\u0412\u043e\u043f\u0440\u043e\u0441: ${$json.query}\\n\u041e\u0442\u0432\u0435\u0442:`;\n} else {\n content = `\u0412\u043e\u043f\u0440\u043e\u0441: ${$json.query}\\n\u041e\u0442\u0432\u0435\u0442:`;\n}\n\nreturn [{\n chatId: $json.chatId,\n prompt: content,\n system,\n query: $json.query\n}];"
},
"name": "Build Prompt",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
580,
340
]
},
{
"parameters": {
"requestMethod": "POST",
"url": "={{ ($env.OLLAMA_BASE_URL || 'http://ollama:11434') + '/api/chat' }}",
"jsonParameters": true,
"options": {},
"headerParameters": [
{
"name": "Authorization",
"value": "={{ $env.OLLAMA_API_KEY ? ('Bearer ' + $env.OLLAMA_API_KEY) : '' }}"
}
],
"bodyParametersJson": "={{ JSON.stringify({\n \"model\": ($env.OLLAMA_MODEL || \"llama3.2:3b\"),\n \"keep_alive\": \"30m\",\n \"stream\": false,\n \"options\": { \"temperature\": 0 },\n \"messages\": [\n {\"role\": \"system\", \"content\": $json.system},\n {\"role\": \"user\", \"content\": $json.prompt}\n ]\n}) }}"
},
"name": "Ollama Chat",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
800,
340
]
},
{
"parameters": {
"functionCode": "const resp = $json;\nlet llmAnswer = '';\nif (resp.message && resp.message.content) {\n llmAnswer = resp.message.content;\n} else if (resp.error) {\n llmAnswer = \"\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0442 AI: \" + JSON.stringify(resp.error);\n} else {\n llmAnswer = \"\u041d\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430 \u043e\u0442 AI. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442: \" + JSON.stringify(resp);\n}\n\nlet tavilyAnswer = '';\ntry {\n const t = $('Tavily Search').first().json;\n if (t && t.answer) tavilyAnswer = t.answer;\n} catch (e) {}\n\nconst finalAnswer = tavilyAnswer || llmAnswer;\n\nconst originalData = $('Parse Telegram').first().json;\nconst promptData = $('Build Prompt').first().json;\n\nreturn [{\n chatId: originalData.chatId,\n answer: finalAnswer,\n question: promptData.query\n}];"
},
"name": "Extract Answer",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
1020,
340
]
},
{
"parameters": {
"requestMethod": "POST",
"url": "={{ ($env.OLLAMA_BASE_URL || 'http://ollama:11434') + '/api/embeddings' }}",
"jsonParameters": true,
"options": {},
"headerParameters": [
{
"name": "Authorization",
"value": "={{ $env.OLLAMA_API_KEY ? ('Bearer ' + $env.OLLAMA_API_KEY) : '' }}"
}
],
"bodyParametersJson": "={{ JSON.stringify({\n \"model\": $env.OLLAMA_MODEL || \"llama3.2:3b\",\n \"prompt\": ($json.question + \"\\n\\n\" + $json.answer).substring(0, 4000)\n}) }}"
},
"name": "Generate Answer Embedding",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
1240,
220
]
},
{
"parameters": {
"requestMethod": "POST",
"url": "={{ 'http://chroma:8000/api/v1/collections/' + $('Merge Collection ID').first().json.collectionId + '/upsert' }}",
"jsonParameters": true,
"options": {},
"bodyParametersJson": "={{ JSON.stringify({\n \"ids\": [ Date.now().toString() ],\n \"embeddings\": [ $json.embedding ],\n \"documents\": [ $('Extract Answer').first().json.question + \"\\n\\n\" + $('Extract Answer').first().json.answer ],\n \"metadatas\": [ { \"source\": \"telegram\" } ]\n}) }}"
},
"name": "Chroma Upsert",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [
1440,
220
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.answer }}",
"additionalFields": {}
},
"name": "Telegram Send Answer",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
1640,
460
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Parse Telegram",
"type": "main",
"index": 0
}
]
]
},
"Parse Telegram": {
"main": [
[
{
"node": "Telegram Typing",
"type": "main",
"index": 0
},
{
"node": "Get Collection",
"type": "main",
"index": 0
}
]
]
},
"Get Collection": {
"main": [
[
{
"node": "Merge Collection ID",
"type": "main",
"index": 0
}
]
]
},
"Merge Collection ID": {
"main": [
[
{
"node": "If Document",
"type": "main",
"index": 0
}
]
]
},
"If Document": {
"main": [
[
{
"node": "Get File Path",
"type": "main",
"index": 0
}
],
[
{
"node": "If Web Search",
"type": "main",
"index": 0
}
]
]
},
"If Web Search": {
"main": [
[
{
"node": "Tavily Search",
"type": "main",
"index": 0
}
],
[
{
"node": "Generate Query Embedding",
"type": "main",
"index": 0
}
]
]
},
"Generate Query Embedding": {
"main": [
[
{
"node": "Chroma Query",
"type": "main",
"index": 0
}
]
]
},
"Chroma Query": {
"main": [
[
{
"node": "Build RAG Context",
"type": "main",
"index": 0
}
]
]
},
"Tavily Search": {
"main": [
[
{
"node": "Build Web Context",
"type": "main",
"index": 0
}
]
]
},
"Build RAG Context": {
"main": [
[
{
"node": "Build Prompt",
"type": "main",
"index": 0
}
]
]
},
"Build Web Context": {
"main": [
[
{
"node": "Build Prompt",
"type": "main",
"index": 0
}
]
]
},
"Build Prompt": {
"main": [
[
{
"node": "Ollama Chat",
"type": "main",
"index": 0
}
]
]
},
"Ollama Chat": {
"main": [
[
{
"node": "Extract Answer",
"type": "main",
"index": 0
}
]
]
},
"Extract Answer": {
"main": [
[
{
"node": "Generate Answer Embedding",
"type": "main",
"index": 0
},
{
"node": "Telegram Send Answer",
"type": "main",
"index": 0
}
]
]
},
"Generate Answer Embedding": {
"main": [
[
{
"node": "Chroma Upsert",
"type": "main",
"index": 0
}
]
]
},
"Get File Path": {
"main": [
[
{
"node": "Download File",
"type": "main",
"index": 0
}
]
]
},
"Download File": {
"main": [
[
{
"node": "Split Text",
"type": "main",
"index": 0
}
]
]
},
"Split Text": {
"main": [
[
{
"node": "Embed Chunk",
"type": "main",
"index": 0
}
]
]
},
"Embed Chunk": {
"main": [
[
{
"node": "Upsert Chunk",
"type": "main",
"index": 0
}
]
]
},
"Upsert Chunk": {
"main": [
[
{
"node": "Notify Progress",
"type": "main",
"index": 0
}
]
]
}
}
}
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.
telegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Local AI Agent (HTTP-based). Uses telegram, httpRequest. Webhook trigger; 24 nodes.
Source: https://github.com/Katapios/N8N-telegram-manager/blob/42a0f215059060fe5a22eeb0d76383a9859567b6/n8n/workflows/ai-agent.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.
🤖 Telegram Messaging Agent for Text/Audio/Images. Uses telegram, httpRequest. Webhook trigger; 39 nodes.
AI Phone Agent with RetellAI. Uses telegram, googleCalendar, httpRequest, googleDrive. Webhook trigger; 36 nodes.
Trato Hecho - Chat Agent. Uses httpRequest, telegram, gmail. Webhook trigger; 26 nodes.
Trato Hecho - Chat Agent. Uses httpRequest, telegram, gmail. Webhook trigger; 26 nodes.
good2AI Agent in n8n (Multimodal). Uses telegram, httpRequest. Webhook trigger; 17 nodes.