This workflow follows the Agent → Chainllm 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": "19-telegram-research-agent-02-research-processor",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 15
}
]
}
},
"id": "68ffd3bd-0007-464b-8488-419614799530",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-2352,
3344
]
},
{
"parameters": {},
"id": "d50501cb-2b71-4bf1-b0ad-724f917e4e4c",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-2352,
3568
]
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "={{ $json.SHEET_ID }}",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "={{ $json.SHEET_NAME }}",
"mode": "name"
},
"options": {}
},
"id": "d3c4693c-178b-4de8-ac4b-3953d035a801",
"name": "Sheets: Get All Rows",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
-1792,
3472
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// L\u1ecdc c\u00e1c y\u00eau c\u1ea7u c\u00f3 status=queue v\u00e0 l\u1ea5y y\u00eau c\u1ea7u \u0111\u1ea7u ti\u00ean (oldest first)\nconst config = $('Set: Config Vars1').first().json;\nconst allItems = $input.all();\n\nconst queueItems = allItems\n .filter(item => item.json.Status === 'queue')\n .sort((a, b) => {\n const dateA = new Date(a.json.Created_At || 0);\n const dateB = new Date(b.json.Created_At || 0);\n return dateA - dateB; // oldest first\n });\n\nif (queueItems.length === 0) {\n return [{ json: { has_pending: false, config } }];\n}\n\nconst first = queueItems[0].json;\nreturn [{\n json: {\n has_pending: true,\n request_id: first.Request_ID,\n title: first.Title,\n raw_request: first.Raw_Request,\n research_type: first.Research_Type || 'other',\n chat_id: first.Chat_ID,\n user_id: first.User_ID,\n created_at: first.Created_At,\n // Pass config\n SHEET_ID: config.SHEET_ID,\n SHEET_NAME: config.SHEET_NAME,\n DRIVE_ROOT_FOLDER_ID: config.DRIVE_ROOT_FOLDER_ID,\n RESEARCH_SOURCES: config.RESEARCH_SOURCES,\n MAX_SEARCH_RESULTS: config.MAX_SEARCH_RESULTS,\n TOP_URLS_TO_SCRAPE: config.TOP_URLS_TO_SCRAPE,\n REPORT_LANGUAGE: config.REPORT_LANGUAGE\n }\n}];"
},
"id": "4d4d3824-76d1-4b4a-ae02-bb5dbb4f7a03",
"name": "Code: Filter Queue",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1504,
3472
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "check-pending",
"leftValue": "={{ $json.has_pending }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "2d026c1d-745e-4abd-aa24-43f4e67cefce",
"name": "IF: Has Pending?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-1232,
3472
]
},
{
"parameters": {
"operation": "update",
"documentId": {
"__rl": true,
"value": "={{ $json.SHEET_ID }}",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "={{ $json.SHEET_NAME }}",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Request_ID": "={{ $json.request_id }}",
"Status": "processing",
"Updated_At": "={{ new Date().toISOString() }}"
},
"matchingColumns": [
"Request_ID"
],
"schema": [
{
"id": "Request_ID",
"displayName": "Request_ID",
"required": false,
"defaultMatch": true,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Status",
"displayName": "Status",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Updated_At",
"displayName": "Updated_At",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
}
]
},
"options": {}
},
"id": "fecf9d07-31e8-4f56-b0e8-580b3cb233de",
"name": "Sheets: Update \u2192 Processing",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
-944,
3344
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.raw_request }}",
"options": {
"systemMessage": "=B\u1ea1n l\u00e0 chuy\u00ean gia research th\u1ecb tr\u01b0\u1eddng v\u00e0 ph\u00e2n t\u00edch d\u1eef li\u1ec7u h\u00e0ng \u0111\u1ea7u.\n\nNHI\u1ec6M V\u1ee4: Nghi\u00ean c\u1ee9u to\u00e0n di\u1ec7n v\u1ec1 ch\u1ee7 \u0111\u1ec1 sau:\nTITLE: {{ $json.title }}\nTYPE: {{ $json.research_type }}\n\nNGU\u1ed2N \u01afU TI\u00caN: {{ $json.RESEARCH_SOURCES }}\n\nQUY TR\u00ccNH B\u1eaeT BU\u1ed8C:\n1. T\u1ea1o 3-4 keyword t\u00ecm ki\u1ebfm ph\u00f9 h\u1ee3p v\u1edbi ch\u1ee7 \u0111\u1ec1 (ti\u1ebfng Vi\u1ec7t v\u00e0 ti\u1ebfng Anh n\u1ebfu c\u1ea7n), \u01b0u ti\u00ean domain t\u1eeb NGU\u1ed2N \u01afU TI\u00caN\n2. D\u00f9ng c\u00f4ng c\u1ee5 \"/search in Firecrawl\" v\u1edbi t\u1eebng keyword \u0111\u1ec3 l\u1ea5y danh s\u00e1ch URLs li\u00ean quan\n3. T\u1eeb k\u1ebft qu\u1ea3 search, ch\u1ecdn 4-5 URLs c\u00f3 n\u1ed9i dung c\u1ee5 th\u1ec3 v\u00e0 uy t\u00edn nh\u1ea5t v\u1ec1 ch\u1ee7 \u0111\u1ec1\n4. D\u00f9ng c\u00f4ng c\u1ee5 \"/scrape in Firecrawl2\" \u0111\u1ec3 \u0111\u1ecdc n\u1ed9i dung \u0111\u1ea7y \u0111\u1ee7 t\u1eebng URL \u0111\u00f3\n5. Thu th\u1eadp s\u1ed1 li\u1ec7u c\u1ee5 th\u1ec3, th\u1ed1ng k\u00ea, d\u1eef li\u1ec7u \u0111\u1ecbnh l\u01b0\u1ee3ng t\u1eeb c\u00e1c b\u00e0i vi\u1ebft\n\nY\u00caU C\u1ea6U:\n- \u01afu ti\u00ean t\u00ecm: s\u1ed1 li\u1ec7u th\u1ecb tr\u01b0\u1eddng, xu h\u01b0\u1edbng, \u0111\u1ed1i th\u1ee7, c\u01a1 h\u1ed9i, r\u1ee7i ro\n- M\u1ed7i URL scrape, gi\u1eef l\u1ea1i ph\u1ea7n n\u1ed9i dung quan tr\u1ecdng nh\u1ea5t\n- T\u1ed5ng h\u1ee3p v\u00e0 tr\u1ea3 v\u1ec1 k\u1ebft qu\u1ea3 research ho\u00e0n ch\u1ec9nh b\u1eb1ng ti\u1ebfng Vi\u1ec7t bao g\u1ed3m:\n * C\u00e1c ph\u00e1t hi\u1ec7n ch\u00ednh v\u1edbi d\u1eabn ch\u1ee9ng t\u1eeb ngu\u1ed3n\n * S\u1ed1 li\u1ec7u, th\u1ed1ng k\u00ea quan tr\u1ecdng\n * Danh s\u00e1ch URLs \u0111\u00e3 s\u1eed d\u1ee5ng",
"maxIterations": 15
}
},
"id": "4d74712d-50e7-45e1-bc45-f08c53d0455b",
"name": "AI Agent: Research",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.7,
"position": [
-368,
3344
],
"onError": "continueErrorOutput"
},
{
"parameters": {
"modelName": "models/gemini-3-flash-preview",
"options": {
"maxOutputTokens": 8192,
"temperature": 0.3
}
},
"id": "34b3d9a3-072d-4ee5-99d5-5746769fc388",
"name": "Gemini Flash (Agent)",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-496,
3648
],
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"promptType": "define",
"text": "=D\u1ef1a tr\u00ean k\u1ebft qu\u1ea3 research sau \u0111\u00e2y, vi\u1ebft m\u1ed9t B\u00c1O C\u00c1O CHUY\u00caN S\u00c2U HO\u00c0N CH\u1ec8NH b\u1eb1ng ti\u1ebfng Vi\u1ec7t.\n\n--- D\u1eee LI\u1ec6U RESEARCH ---\n{{ $json.output }}\n--- K\u1ebeT TH\u00daC D\u1eee LI\u1ec6U ---\n\nTH\u00d4NG TIN B\u00c1O C\u00c1O:\n- Ti\u00eau \u0111\u1ec1: {{ $node['Code: Filter Queue'].json.title }}\n- Lo\u1ea1i nghi\u00ean c\u1ee9u: {{ $node['Code: Filter Queue'].json.research_type }}\n- Ng\u00e0y: {{ new Date().toLocaleDateString('vi-VN') }}\n\nVi\u1ebft b\u00e1o c\u00e1o theo \u0111\u00fang format Markdown sau:\n\n# {{ $node['Code: Filter Queue'].json.title }}\n\n**Ng\u00e0y b\u00e1o c\u00e1o:** [ng\u00e0y h\u00f4m nay] | **Lo\u1ea1i:** [research_type] | **Tr\u1ea1ng th\u00e1i:** Ho\u00e0n th\u00e0nh\n\n---\n\n## T\u00f3m T\u1eaft \u0110i\u1ec1u H\u00e0nh\n[3-5 bullet points v\u1edbi c\u00e1c ph\u00e1t hi\u1ec7n quan tr\u1ecdng nh\u1ea5t]\n\n## B\u1ed1i C\u1ea3nh & T\u1ed5ng Quan\n[Gi\u1edbi thi\u1ec7u v\u1ec1 ch\u1ee7 \u0111\u1ec1, t\u1ea1i sao quan tr\u1ecdng]\n\n## Ph\u00e2n T\u00edch Chi Ti\u1ebft\n[N\u1ed9i dung ph\u00e2n t\u00edch s\u00e2u v\u1edbi d\u1eabn ch\u1ee9ng v\u00e0 s\u1ed1 li\u1ec7u]\n\n## D\u1eef Li\u1ec7u & S\u1ed1 Li\u1ec7u Quan Tr\u1ecdng\n[B\u1ea3ng ho\u1eb7c danh s\u00e1ch c\u00e1c s\u1ed1 li\u1ec7u, th\u1ed1ng k\u00ea c\u1ee5 th\u1ec3]\n\n## Xu H\u01b0\u1edbng & C\u01a1 H\u1ed9i\n[C\u00e1c xu h\u01b0\u1edbng \u0111ang n\u1ed5i l\u00ean v\u00e0 c\u01a1 h\u1ed9i ti\u1ec1m n\u0103ng]\n\n## R\u1ee7i Ro & Th\u00e1ch Th\u1ee9c\n[C\u00e1c r\u1ee7i ro c\u1ea7n l\u01b0u \u00fd]\n\n## K\u1ebft Lu\u1eadn & Khuy\u1ebfn Ngh\u1ecb\n[3-5 khuy\u1ebfn ngh\u1ecb h\u00e0nh \u0111\u1ed9ng c\u1ee5 th\u1ec3]\n\n## Ngu\u1ed3n Tham Kh\u1ea3o\n[Danh s\u00e1ch c\u00e1c URLs \u0111\u00e3 s\u1eed d\u1ee5ng]\n\nY\u00caU C\u1ea6U:\n- Ng\u00f4n ng\u1eef ti\u1ebfng Vi\u1ec7t chu\u1ea9n, chuy\u00ean nghi\u1ec7p\n- T\u1ed5ng \u0111\u1ed9 d\u00e0i 2000-3500 t\u1eeb\n- Tr\u00edch d\u1eabn s\u1ed1 li\u1ec7u v\u1edbi ngu\u1ed3n r\u00f5 r\u00e0ng\n- S\u1eed d\u1ee5ng format Markdown chu\u1ea9n (heading ##, bold **text**, bullet -, b\u1ea3ng |---|)"
},
"id": "9461c57c-e0f8-4648-8041-e110d707be69",
"name": "Gemini: Write Report",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"typeVersion": 1.5,
"position": [
-16,
3088
]
},
{
"parameters": {
"modelName": "models/gemini-3-flash-preview",
"options": {
"maxOutputTokens": 8192,
"temperature": 0.4
}
},
"id": "fcec3fc3-5936-4856-8987-4fe36238a0e9",
"name": "Gemini Flash (Report)",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-16,
3264
],
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Chu\u1ea9n b\u1ecb n\u1ed9i dung file b\u00e1o c\u00e1o v\u00e0 t\u00ean folder\nconst reportMarkdown = $input.first().json.text || '';\nconst req = $('Code: Rebuild Context').first().json;\n\n// T\u1ea1o t\u00ean folder: RequestID - Title - YYYY-MM-DD\nconst dateStr = new Date().toISOString().split('T')[0]; // YYYY-MM-DD\nconst safeTitle = req.title\n .replace(/[/\\\\:*?\"<>|]/g, '-') // k\u00fd t\u1ef1 kh\u00f4ng h\u1ee3p l\u1ec7 cho t\u00ean folder\n .substring(0, 60);\nconst folderName = `${req.request_id} - ${safeTitle} - ${dateStr}`;\n\n// Encode report sang base64 \u0111\u1ec3 upload Drive\nconst reportBuffer = Buffer.from(reportMarkdown, 'utf-8');\nconst reportBase64 = reportBuffer.toString('base64');\n\n// Tr\u00edch xu\u1ea5t overview (200 k\u00fd t\u1ef1 \u0111\u1ea7u c\u1ee7a n\u1ed9i dung sau header)\nconst lines = reportMarkdown.split('\\n').filter(l => l.trim());\nlet overview = '';\nfor (const line of lines) {\n if (!line.startsWith('#') && !line.startsWith('---') && !line.startsWith('**')) {\n overview += line.replace(/[*_#]/g, '').trim() + ' ';\n if (overview.length > 200) break;\n }\n}\noverview = overview.trim().substring(0, 250);\n\nreturn [{\n json: {\n folder_name: folderName,\n report_filename: `report-${req.request_id}.md`,\n report_markdown: reportMarkdown,\n overview: overview || req.title,\n request_id: req.request_id,\n chat_id: req.chat_id,\n title: req.title,\n DRIVE_ROOT_FOLDER_ID: req.DRIVE_ROOT_FOLDER_ID,\n SHEET_ID: req.SHEET_ID,\n SHEET_NAME: req.SHEET_NAME\n },\n binary: {\n reportFile: {\n data: reportBase64,\n mimeType: 'text/markdown',\n fileName: `report-${req.request_id}.md`,\n fileExtension: 'md'\n }\n }\n}];"
},
"id": "29d8b57d-c409-4fec-93c4-eba5a7d7942e",
"name": "Code: Prepare Files",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
256,
3088
]
},
{
"parameters": {
"jsCode": "// L\u01b0u folder ID v\u00e0 chu\u1ea9n b\u1ecb d\u1eef li\u1ec7u cho upload\nconst folderResponse = $input.first().json;\nconst prevData = $('Code: Prepare Files').first().json;\nconst prevBinary = $('Code: Prepare Files').first().binary;\n\nconst folderId = folderResponse.id || '';\nconst folderLink = folderId ? `https://drive.google.com/drive/folders/${folderId}` : '';\n\nreturn [{\n json: {\n ...prevData,\n folder_id: folderId,\n folder_link: folderLink\n },\n binary: prevBinary\n}];"
},
"id": "4bd39986-a8ac-4bed-9f65-32237bd75516",
"name": "Code: Save Folder ID",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
592,
3088
]
},
{
"parameters": {
"inputDataFieldName": "reportFile",
"name": "={{ $json.report_filename }}",
"driveId": {
"__rl": true,
"value": "My Drive",
"mode": "list",
"cachedResultName": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $json.folder_id }}",
"mode": "id"
},
"options": {}
},
"id": "49dbab36-7159-4c95-b0ac-ba29ddfd6cf5",
"name": "Drive: Upload Report",
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
768,
3088
],
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"jsCode": "// Build final data sau khi upload\nconst uploadResp = $input.first().json;\nconst prev = $('Code: Save Folder ID').first().json;\n\nconst fileId = uploadResp.id || '';\nconst fileLink = uploadResp.webViewLink || (fileId ? `https://drive.google.com/file/d/${fileId}/view` : '');\n\nreturn [{\n json: {\n request_id: prev.request_id,\n title: prev.title,\n overview: prev.overview,\n chat_id: prev.chat_id,\n folder_link: prev.folder_link,\n folder_id: prev.folder_id,\n file_link: fileLink,\n file_id: fileId,\n SHEET_ID: prev.SHEET_ID,\n SHEET_NAME: prev.SHEET_NAME\n }\n}];"
},
"id": "839d9690-84ba-4182-9087-aec7be90072c",
"name": "Code: Build Final Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
960,
3072
]
},
{
"parameters": {
"operation": "update",
"documentId": {
"__rl": true,
"value": "={{ $json.SHEET_ID }}",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "={{ $json.SHEET_NAME }}",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Request_ID": "={{ $json.request_id }}",
"Status": "done",
"Overview": "={{ $json.overview }}",
"Drive_Folder_Link": "={{ $json.folder_link }}",
"Report_Link": "={{ $json.file_link }}",
"Updated_At": "={{ new Date().toISOString() }}"
},
"matchingColumns": [
"Request_ID"
],
"schema": [
{
"id": "Request_ID",
"displayName": "Request_ID",
"required": false,
"defaultMatch": true,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Status",
"displayName": "Status",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Overview",
"displayName": "Overview",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Drive_Folder_Link",
"displayName": "Drive_Folder_Link",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Report_Link",
"displayName": "Report_Link",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Updated_At",
"displayName": "Updated_At",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
}
]
},
"options": {}
},
"id": "53d3819e-2aac-452f-849b-0988b47e5c7f",
"name": "Sheets: Update \u2192 Done",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
1168,
3072
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $node['Code: Build Final Data'].json.chat_id }}",
"text": "=\ud83d\udcca *B\u00e1o c\u00e1o Research Ho\u00e0n Th\u00e0nh!*\n\n\ud83d\udccc *Ch\u1ee7 \u0111\u1ec1:* {{ $node['Code: Build Final Data'].json.title }}\n\ud83c\udd94 *ID:* `{{ $node['Code: Build Final Data'].json.request_id }}`\n\n\ud83d\udcdd *T\u00f3m t\u1eaft:*\n{{ $node['Code: Build Final Data'].json.overview }}\n\n\ud83d\udcc1 *Folder Drive:* [Xem t\u1ea1i \u0111\u00e2y]({{ $node['Code: Build Final Data'].json.folder_link }})\n\ud83d\udcc4 *File B\u00e1o C\u00e1o:* [T\u1ea3i v\u1ec1]({{ $node['Code: Build Final Data'].json.file_link }})\n\n\u2705 Nghi\u00ean c\u1ee9u ho\u00e0n t\u1ea5t! M\u1eddi b\u1ea1n xem chi ti\u1ebft b\u00e1o c\u00e1o.",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "075b56e7-6e40-474d-b3b5-4b9b6b3a842c",
"name": "Telegram: Notify Done",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1376,
3072
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Kh\u00f4ng c\u00f3 y\u00eau c\u1ea7u \u0111ang ch\u1edd \u2192 d\u1eebng l\u1ea1i\nconsole.log('[Research Processor] Kh\u00f4ng c\u00f3 y\u00eau c\u1ea7u n\u00e0o trong queue.');\nreturn [];"
},
"id": "345dac17-3130-4508-b9a5-1ce2c83c01b4",
"name": "No Op: No Pending",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-944,
3568
]
},
{
"parameters": {
"jsCode": "// X\u1eed l\u00fd l\u1ed7i - l\u1ea5y data t\u1eeb $input (ch\u1ee9a full context t\u1eeb Code: Rebuild Context)\n// KH\u00d4NG d\u00f9ng $node[] reference v\u00ec error branch c\u00f3 th\u1ec3 kh\u00f4ng truy c\u1eadp \u0111\u01b0\u1ee3c\nconst failedItem = $input.first();\nconst data = failedItem.json || {};\n\n// L\u1ea5y th\u00f4ng tin l\u1ed7i t\u1eeb field error (n8n th\u00eam v\u00e0o khi continueErrorOutput)\nconst errorObj = data.error || {};\nconst errorMessage = typeof errorObj === 'string'\n ? errorObj\n : (errorObj.message || errorObj.description || JSON.stringify(errorObj) || 'Unknown error');\n\nreturn [{\n json: {\n request_id: data.request_id || 'UNKNOWN',\n title: data.title || 'Unknown request',\n chat_id: data.chat_id || '',\n error_message: String(errorMessage).substring(0, 500),\n SHEET_ID: data.SHEET_ID || '',\n SHEET_NAME: data.SHEET_NAME || ''\n }\n}];"
},
"id": "4f4b1d71-a997-4269-aaaf-c7a338e82f7c",
"name": "Code: Handle Error",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
960,
3360
]
},
{
"parameters": {
"operation": "update",
"documentId": {
"__rl": true,
"value": "={{ $json.SHEET_ID }}",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "={{ $json.SHEET_NAME }}",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Request_ID": "={{ $json.request_id }}",
"Status": "error",
"Overview": "=L\u1ed7i x\u1eed l\u00fd: {{ $json.error_message.substring(0, 200) }}",
"Updated_At": "={{ new Date().toISOString() }}"
},
"matchingColumns": [
"Request_ID"
],
"schema": [
{
"id": "Request_ID",
"displayName": "Request_ID",
"required": false,
"defaultMatch": true,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Status",
"displayName": "Status",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Overview",
"displayName": "Overview",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
},
{
"id": "Updated_At",
"displayName": "Updated_At",
"required": false,
"defaultMatch": false,
"canBeUsedToMatch": true,
"display": true,
"type": "string",
"removed": false
}
]
},
"options": {}
},
"id": "9233a352-43e1-43e8-b530-4024fd3be55f",
"name": "Sheets: Update \u2192 Error",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
1184,
3360
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $json.chat_id }}",
"text": "=\u274c *C\u00f3 l\u1ed7i khi x\u1eed l\u00fd y\u00eau c\u1ea7u!*\n\n\ud83c\udd94 *ID:* `{{ $json.request_id }}`\n\ud83d\udccc *Ch\u1ee7 \u0111\u1ec1:* {{ $json.title }}\n\n\u26a0\ufe0f Vui l\u00f2ng ki\u1ec3m tra l\u1ea1i sau ho\u1eb7c th\u1eed l\u1ea1i y\u00eau c\u1ea7u.\n\n_Chi ti\u1ebft l\u1ed7i \u0111\u00e3 \u0111\u01b0\u1ee3c ghi nh\u1eadn trong h\u1ec7 th\u1ed1ng._",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "5aee0456-f2e2-4c60-a10c-9b71b85c0e83",
"name": "Telegram: Notify Error",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1376,
3360
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Sheets Update node ch\u1ec9 pass qua {Request_ID, Status, Updated_At} \u2192 c\u1ea7n restore full context\n// D\u00f9ng $() accessor (n8n v1+ recommended) \u0111\u1ec3 l\u1ea5y data t\u1eeb Code: Filter Queue \u0111\u00e3 ch\u1ea1y tr\u01b0\u1edbc\nlet queue;\ntry {\n // C\u00e1ch 1: n8n v1+ item accessor - recommended\n queue = $('Code: Filter Queue').first().json;\n} catch (e1) {\n try {\n // C\u00e1ch 2: node reference accessor - fallback\n queue = $('Code: Filter Queue').first().json;\n } catch (e2) {\n throw new Error('Cannot restore context. Err1: ' + e1.message + ' | Err2: ' + e2.message);\n }\n}\n\nif (!queue || !queue.request_id) {\n throw new Error('Context empty after restore - check Code: Filter Queue output');\n}\n\nreturn [{ json: queue }];"
},
"id": "acf568ad-3468-4c7b-998c-d0e905bddba6",
"name": "Code: Rebuild Context",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-672,
3344
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "c01",
"name": "SHEET_ID",
"value": "YOUR_SHEET_ID",
"type": "string"
},
{
"id": "c02",
"name": "SHEET_NAME",
"value": "Research Queue",
"type": "string"
},
{
"id": "c03",
"name": "DRIVE_ROOT_FOLDER_ID",
"value": "YOUR_DRIVE_ROOT_FOLDER_ID",
"type": "string"
},
{
"id": "c05",
"name": "RESEARCH_SOURCES",
"value": "vnexpress.net,cafef.vn,tuoitre.vn,thanhnien.vn,mof.gov.vn,gso.gov.vn,statista.com,reuters.com,bloomberg.com,techcrunch.com,mckinsey.com,deloitte.com",
"type": "string"
},
{
"id": "c06",
"name": "MAX_SEARCH_RESULTS",
"value": "8",
"type": "string"
},
{
"id": "c07",
"name": "TOP_URLS_TO_SCRAPE",
"value": "5",
"type": "string"
},
{
"id": "c08",
"name": "REPORT_LANGUAGE",
"value": "Vietnamese",
"type": "string"
}
]
},
"options": {}
},
"id": "44d2ad08-c14e-48b0-9dbf-40f773267f04",
"name": "Set: Config Vars1",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-2064,
3472
],
"notes": "\u2699\ufe0f C\u1ea4U H\u00ccNH CH\u00cdNH: \u0110i\u1ec1n SHEET_ID, DRIVE_ROOT_FOLDER_ID v\u00e0 danh s\u00e1ch ngu\u1ed3n \u01b0u ti\u00ean RESEARCH_SOURCES t\u1ea1i \u0111\u00e2y. Firecrawl API Key c\u1ea5u h\u00ecnh trong Credentials c\u1ee7a node Firecrawl."
},
{
"parameters": {
"operation": "scrape",
"url": "={{ $fromAI('url', 'URL \u0111\u1ea7y \u0111\u1ee7 c\u1ee7a trang c\u1ea7n \u0111\u1ecdc n\u1ed9i dung, v\u00ed d\u1ee5: https://vnexpress.net/bai-viet-123') }}",
"requestOptions": {}
},
"type": "@mendable/n8n-nodes-firecrawl.firecrawlTool",
"typeVersion": 1,
"position": [
-144,
3648
],
"id": "d8de1969-a98b-40d7-baba-5e81b974bbbc",
"name": "/scrape in Firecrawl2",
"credentials": {
"firecrawlApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "MapSearch",
"operation": "search",
"query": "={{ $fromAI('query', 'T\u1eeb kh\u00f3a t\u00ecm ki\u1ebfm th\u00f4ng tin, v\u00ed d\u1ee5: th\u1ecb tr\u01b0\u1eddng xe \u0111i\u1ec7n Vi\u1ec7t Nam 2025') }}",
"requestOptions": {}
},
"type": "@mendable/n8n-nodes-firecrawl.firecrawlTool",
"typeVersion": 1,
"position": [
-304,
3648
],
"id": "8f4d62ed-5921-4abb-acda-2da91b7803d9",
"name": "/search in Firecrawl",
"credentials": {
"firecrawlApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "folder",
"name": "={{ $json.folder_name }}",
"driveId": {
"__rl": true,
"value": "={{ $json.DRIVE_ROOT_FOLDER_ID }}",
"mode": "id"
},
"folderId": {
"__rl": true,
"value": "={{ $json.DRIVE_ROOT_FOLDER_ID }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
432,
3088
],
"id": "38da1467-a362-40fc-859b-9e09f671c090",
"name": "Create folder",
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Set: Config Vars1",
"type": "main",
"index": 0
}
]
]
},
"Manual Trigger": {
"main": [
[
{
"node": "Set: Config Vars1",
"type": "main",
"index": 0
}
]
]
},
"Sheets: Get All Rows": {
"main": [
[
{
"node": "Code: Filter Queue",
"type": "main",
"index": 0
}
]
]
},
"Code: Filter Queue": {
"main": [
[
{
"node": "IF: Has Pending?",
"type": "main",
"index": 0
}
]
]
},
"IF: Has Pending?": {
"main": [
[
{
"node": "Sheets: Update \u2192 Processing",
"type": "main",
"index": 0
}
],
[
{
"node": "No Op: No Pending",
"type": "main",
"index": 0
}
]
]
},
"Sheets: Update \u2192 Processing": {
"main": [
[
{
"node": "Code: Rebuild Context",
"type": "main",
"index": 0
}
]
]
},
"Gemini Flash (Agent)": {
"ai_languageModel": [
[
{
"node": "AI Agent: Research",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"AI Agent: Research": {
"main": [
[
{
"node": "Gemini: Write Report",
"type": "main",
"index": 0
}
],
[
{
"node": "Code: Handle Error",
"type": "main",
"index": 0
}
]
]
},
"Gemini Flash (Report)": {
"ai_languageModel": [
[
{
"node": "Gemini: Write Report",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Gemini: Write Report": {
"main": [
[
{
"node": "Code: Prepare Files",
"type": "main",
"index": 0
}
]
]
},
"Code: Prepare Files": {
"main": [
[
{
"node": "Create folder",
"type": "main",
"index": 0
}
]
]
},
"Code: Save Folder ID": {
"main": [
[
{
"node": "Drive: Upload Report",
"type": "main",
"index": 0
}
]
]
},
"Drive: Upload Report": {
"main": [
[
{
"node": "Code: Build Final Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Code: Handle Error",
"type": "main",
"index": 0
}
]
]
},
"Code: Build Final Data": {
"main": [
[
{
"node": "Sheets: Update \u2192 Done",
"type": "main",
"index": 0
}
]
]
},
"Sheets: Update \u2192 Done": {
"main": [
[
{
"node": "Telegram: Notify Done",
"type": "main",
"index": 0
}
]
]
},
"Code: Handle Error": {
"main": [
[
{
"node": "Sheets: Update \u2192 Error",
"type": "main",
"index": 0
}
]
]
},
"Sheets: Update \u2192 Error": {
"main": [
[
{
"node": "Telegram: Notify Error",
"type": "main",
"index": 0
}
]
]
},
"Code: Rebuild Context": {
"main": [
[
{
"node": "AI Agent: Research",
"type": "main",
"index": 0
}
]
]
},
"Set: Config Vars1": {
"main": [
[
{
"node": "Sheets: Get All Rows",
"type": "main",
"index": 0
}
]
]
},
"/scrape in Firecrawl2": {
"ai_tool": [
[
{
"node": "AI Agent: Research",
"type": "ai_tool",
"index": 0
}
]
]
},
"/search in Firecrawl": {
"ai_tool": [
[
{
"node": "AI Agent: Research",
"type": "ai_tool",
"index": 0
}
]
]
},
"Create folder": {
"main": [
[
{
"node": "Code: Save Folder ID",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false
},
"versionId": "a429f0ce-3922-438c-b892-f5d5e29e3dfd",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "evvOm6EdqHzoPPYU",
"tags": [
{
"updatedAt": "2026-05-29T07:46:11.087Z",
"createdAt": "2026-05-29T07:46:11.087Z",
"id": "pHmdbe3WVnvAQV7N",
"name": "research-agent"
}
]
}
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.
firecrawlApigoogleDriveOAuth2ApigooglePalmApigoogleSheetsOAuth2ApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
19-telegram-research-agent-02-research-processor. Uses googleSheets, agent, lmChatGoogleGemini, chainLlm. Scheduled trigger; 25 nodes.
Source: https://github.com/congdinh2008/n8n-compose/blob/981f311fd5a6a76e50dbc259afccd1bdf346a833/workflows/enterprise/19-telegram-research-agent/02-research-processor.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.
Sign up for Decodo — get better pricing here
Customer support calls contain a wealth of valuable feedback and urgent issues, but manually reviewing audio files is inefficient. This workflow acts as an AI assistant for your call log, transforming
This workflow creates a multi-talented AI assistant named Simran that interacts with users via Telegram. It can handle text and voice messages, understand the user's intent, and perform various tasks.
Arvifund - Supabase. Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.
Arvifund - Supabase (Fixed v2). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.