This workflow corresponds to n8n.io template #16932 — we link there as the canonical source.
This workflow follows the Chainllm → Gmail 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 →
{
"id": "n8WcMU2nPWiadNKw",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Inbox Autopilot (Ollama)",
"tags": [],
"nodes": [
{
"id": "9c511863-c842-499b-8a22-255c26d52533",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-4240,
2704
],
"parameters": {},
"typeVersion": 1
},
{
"id": "ed69733b-3659-4367-b2ec-b581b2ad439b",
"name": "Get Unread Emails",
"type": "n8n-nodes-base.gmail",
"position": [
-4016,
2704
],
"parameters": {
"limit": 10,
"simple": false,
"filters": {
"q": "is:unread"
},
"options": {},
"operation": "getAll"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "74049a95-5cbc-442c-a7c3-b5022cef9cc9",
"name": "BuildClassifierRequest",
"type": "n8n-nodes-base.code",
"position": [
-3792,
2704
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Normalize a Gmail message into the simple shape the rest of the flow expects:\n// { id, threadId, from, subject, body }. Works with Gmail \"Simplify\" on or off.\nconst m = $input.item.json;\n\nconst from =\n m.from?.value?.[0]?.address || // Gmail (Simplify off) -> parsed address object\n m.from?.text || // \"Name <email>\"\n (typeof m.from === 'string' ? m.from : '') ||\n m.headers?.from ||\n '';\n\nconst subject = m.subject || m.headers?.subject || '(no subject)';\n\nconst body = (m.text || m.textAsHtml || m.html || m.snippet || '')\n .toString()\n .trim();\n\nreturn {\n json: {\n id: m.id || m.messageId || null,\n threadId: m.threadId || null,\n from,\n subject,\n body,\n },\n};\n"
},
"typeVersion": 2
},
{
"id": "be609bd4-94fc-4a35-970f-ff1216107747",
"name": "IfNeedsProcessing",
"type": "n8n-nodes-base.if",
"position": [
-3216,
2704
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "26d75eff-be52-4464-9e61-0dd1b7f1cbf4",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.output?.category }}",
"rightValue": "urgent"
},
{
"id": "f1ffee33-dc34-441c-979d-878d33081a5e",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.output?.category }}",
"rightValue": "action_needed"
}
]
}
},
"typeVersion": 2
},
{
"id": "a8dd4b42-4c7f-4129-aab2-fae9c8c3cdd8",
"name": "Skip-NoActionNeeded",
"type": "n8n-nodes-base.set",
"position": [
-2848,
2896
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a1",
"name": "status",
"type": "string",
"value": "skipped"
},
{
"id": "a2",
"name": "skip_reason",
"type": "string",
"value": "={{ 'Category is ' + $json.category + ' - no action required.' }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "b5f2fec2-9c56-46d9-8200-e19ad012c488",
"name": "Ollama Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOllama",
"position": [
-2992,
2512
],
"parameters": {
"model": "llama3.1:latest",
"options": {}
},
"credentials": {
"ollamaApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "f400c91e-75b4-4a4d-bad9-4715f794298a",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-3488,
2928
],
"parameters": {
"jsonSchemaExample": "{\n \"category\": \"urgent\",\n \"confidence\": 0.9,\n \"reasoning\": \"one sentence explanation\"\n}"
},
"typeVersion": 1.3
},
{
"id": "eb846b00-1ee4-494a-8edd-248dfbf2ba7a",
"name": "Ollama Chat Model1",
"type": "@n8n/n8n-nodes-langchain.lmChatOllama",
"position": [
-2784,
2720
],
"parameters": {
"model": "llama3.1:latest",
"options": {
"numPredict": -1
}
},
"credentials": {
"ollamaApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "dbc272d4-2895-4e0d-b243-a42695864f16",
"name": "Classifier Chain",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-3568,
2704
],
"parameters": {
"text": "=From: {{ $json.from }}\nSubject: {{ $json.subject }}\nBody: {{ $json.body }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "You are an email triage classifier. Classify the email into exactly one category: \"urgent\", \"action_needed\", \"fyi\", or \"spam\". Respond with ONLY a raw JSON object. No preamble, no explanation, no markdown code fences, no text before or after. Schema: {\"category\": \"one of the four values above\", \"confidence\": number between 0.0 and 1.0, \"reasoning\": \"one sentence\"}"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.9
},
{
"id": "1e1b1e69-6699-4d07-b5ea-d68e16ce7402",
"name": "Telegram Approval",
"type": "n8n-nodes-base.telegram",
"notes": "Pauses the workflow and pings your Telegram until you tap Approve or Decline. Attach your Telegram API credential after import and replace the chatId placeholder with your own chat ID.",
"position": [
-2128,
2496
],
"parameters": {
"message": "=\ud83d\udce7 {{ $('BuildClassifierRequest').item.json.subject }}\nFrom: {{ $('BuildClassifierRequest').item.json.from }}\nCategory: {{ $('Classifier Chain').item.json.output.category }}\n\nTask: {{ $('Extractor Chain').item.json.output.task_title }}\nDeadline: {{ $('Extractor Chain').item.json.output.deadline }}\n\nDraft reply:\n{{ $('Drafter Chain').item.json.output.draft_text }}\n\nReasoning: {{ $('Drafter Chain').item.json.output.reasoning }}",
"options": {},
"operation": "sendAndWait",
"approvalOptions": {
"values": {
"approvalType": "double"
}
}
},
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "73c8186d-79d3-41cc-8855-9a2ea2aad71b",
"name": "Approved?",
"type": "n8n-nodes-base.if",
"position": [
-1904,
2496
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "dffe3d9c-58d5-41da-87f1-bd7ceb96262a",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $('Telegram Approval').item.json.data.approved }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "c1cce5b5-d5f8-47bf-8edf-802ed6c3fa99",
"name": "Create Draft Reply",
"type": "n8n-nodes-base.gmail",
"position": [
-1680,
2432
],
"parameters": {
"message": "={{ $('Drafter Chain').item.json.output.draft_text }}",
"options": {
"sendTo": "={{ $('BuildClassifierRequest').item.json.from }}",
"threadId": "={{ $('BuildClassifierRequest').item.json.threadId }}"
},
"subject": "=Re: {{ $('BuildClassifierRequest').item.json.subject }}",
"resource": "draft"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "69cf6ee8-b487-4fd4-9463-8834e89de28a",
"name": "Label as Triaged",
"type": "n8n-nodes-base.gmail",
"position": [
-1456,
2432
],
"parameters": {
"labelIds": [
"STARRED"
],
"messageId": "={{ $('BuildClassifierRequest').item.json.id }}",
"operation": "addLabels"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "b27b0a3a-81ba-4323-9723-e6683c00ea62",
"name": "Extractor Chain",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-2960,
2288
],
"parameters": {
"text": "=Subject: {{ $('BuildClassifierRequest').item.json.subject }}\nBody: {{ $('BuildClassifierRequest').item.json.body }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "You extract action items from emails. Determine if there's a concrete task with an owner (the recipient) implied. Respond ONLY with JSON: {\"has_action_item\": true/false, \"task_title\": \"short title or null\", \"deadline\": \"date/phrase or null\", \"reasoning\": \"one sentence\"}"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.9
},
{
"id": "88b94a5f-ff44-4bc4-a38e-7a63997ee783",
"name": "Drafter Chain",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-2480,
2496
],
"parameters": {
"text": "=Subject: {{ $('BuildClassifierRequest').item.json.subject }}\nBody: {{ $('BuildClassifierRequest').item.json.body }}\nCategory: {{ $('Classifier Chain').item.json.output.category }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "You draft short, professional email replies. Only draft a reply if the email genuinely needs one. Respond ONLY with JSON: {\"should_draft\": true/false, \"draft_text\": \"reply or null\", \"reasoning\": \"one sentence\"}"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.9
},
{
"id": "cb336520-e706-45c4-9cc0-e00b21626cea",
"name": "Drafter Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-2496,
2720
],
"parameters": {
"autoFix": true,
"jsonSchemaExample": "{\n \"should_draft\": true,\n \"draft_text\": \"Hi Sarah, thanks for flagging the contract renewal. I'll have the updated agreement over to you by Friday.\",\n \"reasoning\": \"Urgent business email with a clear deadline deserves a prompt acknowledgment.\"\n}"
},
"typeVersion": 1.3
},
{
"id": "aba2c3d8-b51e-4281-a70d-deb4d7313714",
"name": "Extractor Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-2864,
2512
],
"parameters": {
"autoFix": true,
"jsonSchemaExample": "{\n \"has_action_item\": true,\n \"task_title\": \"Follow up on contract renewal\",\n \"deadline\": \"Friday\",\n \"reasoning\": \"one sentence explanation\"\n}"
},
"typeVersion": 1.3
},
{
"id": "9b0f32bb-3f4c-465c-9a98-b85d6fbe7eb9",
"name": "Log Approved Decision",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1232,
2496
],
"parameters": {
"columns": {
"value": {
"sender": "={{ $('BuildClassifierRequest').item.json.from }}",
"subject": "={{ $('BuildClassifierRequest').item.json.subject }}",
"category": "={{ $('Classifier Chain').item.json.output.category }}",
"deadline": "={{ $('Extractor Chain').item.json.output.deadline }}",
"decision": "={{ $('Telegram Approval').item.json.data.approved ? 'approved' : 'rejected' }}",
"timestamp": "={{ $now }}",
"draft_text": "={{ $('Drafter Chain').item.json.output.draft_text }}",
"task_title": "={{ $('Extractor Chain').item.json.output.task_title }}"
},
"schema": [
{
"id": "timestamp",
"type": "string",
"display": true,
"required": false,
"displayName": "timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "sender",
"type": "string",
"display": true,
"required": false,
"displayName": "sender",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "subject",
"type": "string",
"display": true,
"required": false,
"displayName": "subject",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "category",
"type": "string",
"display": true,
"required": false,
"displayName": "category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "task_title",
"type": "string",
"display": true,
"required": false,
"displayName": "task_title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "deadline",
"type": "string",
"display": true,
"required": false,
"displayName": "deadline",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "draft_text",
"type": "string",
"display": true,
"required": false,
"displayName": "draft_text",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "decision",
"type": "string",
"display": true,
"required": false,
"displayName": "decision",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1yKTuLzDIa8tBuiXdcnrK0FY-Ad0rLilmLJQPxEc09U8/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "d1fe8bb4-71fb-40e2-b989-4f4fbd7b239f",
"name": "Sticky Note - Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-5072,
2288
],
"parameters": {
"width": 700,
"height": 840,
"content": "# \ud83d\udce5 Inbox Autopilot (Ollama)\n\nTriage your inbox with a **local, private** LLM \u2014 no API keys, no per-token cost. Unread Gmail is classified, action items are extracted, a reply is drafted, and **you approve it from Telegram**. On approval a **draft reply lands back in Gmail, in-thread** \u2014 a human always sends the final message.\n\n### How it works\n1. **Ingest & classify** \u2014 unread emails are pulled from **Gmail**, then the Classifier tags each as `urgent`, `action_needed`, `fyi`, or `spam`.\n2. **Route** \u2014 only `urgent` / `action_needed` mail moves on; the rest is marked *skipped*.\n3. **Extract & draft** \u2014 the Extractor pulls the task + deadline, then the Drafter writes a short reply.\n4. **Approve \u2192 reply \u2192 log** \u2014 Telegram sends the summary + draft to approve/reject. On approval a **threaded draft reply** is created in Gmail and the email is labeled; every decision is logged to Google Sheets.\n\n### Setup\n1. Install **Ollama** and run `ollama pull llama3.1`. Make sure n8n can reach it (default `http://localhost:11434`).\n2. Connect your **Gmail** account on *Get Unread Emails* (the draft + label nodes reuse it).\n3. Add credentials to both **Ollama Chat Model** nodes.\n4. Connect your **Telegram** bot and set your `chatId`.\n5. Connect **Google Sheets** and pick your Decision Log sheet.\n6. Click **Test workflow** to triage the current unread batch.\n\n### Customization\n- *Label as Triaged* stars the email by default \u2014 swap in a custom **Triaged** label and set the fetch to `is:unread -is:starred` to stop re-processing.\n- Change the Gmail search filter, category list, or reply tone in each chain's prompt.\n- Swap the Manual Trigger for a **Gmail Trigger** to run automatically."
},
"typeVersion": 1
},
{
"id": "fa2b60c1-1523-4c72-b6dd-8e0961e21e89",
"name": "Sticky Note - Section 1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4336,
2496
],
"parameters": {
"color": 7,
"width": 1050,
"height": 632,
"content": "## \u2460 Ingest & classify\nFetches your unread Gmail messages, normalizes each to a simple shape, then tags it as urgent / action_needed / fyi / spam as structured JSON."
},
"typeVersion": 1
},
{
"id": "415d01cc-153f-41dd-9a71-a67bb0f7dcd5",
"name": "Sticky Note - Section 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3264,
2128
],
"parameters": {
"color": 7,
"width": 1030,
"height": 968,
"content": "## \u2461 Extract & draft\nRoutes only actionable mail forward, extracts the task + deadline, then drafts a short reply. One shared Ollama model powers all three chains."
},
"typeVersion": 1
},
{
"id": "96ec55cf-0366-4d6c-a7f8-f0cb09ad75f1",
"name": "Sticky Note - Section 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2208,
2256
],
"parameters": {
"color": 7,
"width": 1160,
"height": 488,
"content": "## \u2462 Approve, reply & log\nTelegram asks you to approve or reject each draft. On approval it creates a **threaded Gmail draft reply** and labels the email; every decision (approved / rejected) is logged to Google Sheets."
},
"typeVersion": 1
},
{
"id": "fc6c3f54-7db1-4f0f-bede-cc2ab8f2977b",
"name": "Sticky Note - Warning",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3264,
3120
],
"parameters": {
"color": 3,
"width": 1023,
"height": 131,
"content": "## \u26a0\ufe0f Ollama must be running\nStart Ollama and run `ollama pull llama3.1` **before** executing. If n8n can't reach `localhost:11434`, every chain fails here."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "46906a5c-4544-4305-8877-5594bad56bf0",
"nodeGroups": [],
"connections": {
"Approved?": {
"main": [
[
{
"node": "Create Draft Reply",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Approved Decision",
"type": "main",
"index": 0
}
]
]
},
"Drafter Chain": {
"main": [
[
{
"node": "Telegram Approval",
"type": "main",
"index": 0
}
]
]
},
"Manual Trigger": {
"main": [
[
{
"node": "Get Unread Emails",
"type": "main",
"index": 0
}
]
]
},
"Extractor Chain": {
"main": [
[
{
"node": "Drafter Chain",
"type": "main",
"index": 0
}
]
]
},
"Classifier Chain": {
"main": [
[
{
"node": "IfNeedsProcessing",
"type": "main",
"index": 0
}
]
]
},
"Label as Triaged": {
"main": [
[
{
"node": "Log Approved Decision",
"type": "main",
"index": 0
}
]
]
},
"Get Unread Emails": {
"main": [
[
{
"node": "BuildClassifierRequest",
"type": "main",
"index": 0
}
]
]
},
"IfNeedsProcessing": {
"main": [
[
{
"node": "Extractor Chain",
"type": "main",
"index": 0
}
],
[
{
"node": "Skip-NoActionNeeded",
"type": "main",
"index": 0
}
]
]
},
"Ollama Chat Model": {
"ai_languageModel": [
[
{
"node": "Classifier Chain",
"type": "ai_languageModel",
"index": 0
},
{
"node": "Extractor Chain",
"type": "ai_languageModel",
"index": 0
},
{
"node": "Drafter Chain",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Telegram Approval": {
"main": [
[
{
"node": "Approved?",
"type": "main",
"index": 0
}
]
]
},
"Create Draft Reply": {
"main": [
[
{
"node": "Label as Triaged",
"type": "main",
"index": 0
}
]
]
},
"Ollama Chat Model1": {
"ai_languageModel": [
[
{
"node": "Extractor Output Parser",
"type": "ai_languageModel",
"index": 0
},
{
"node": "Drafter Output Parser",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Drafter Output Parser": {
"ai_outputParser": [
[
{
"node": "Drafter Chain",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"BuildClassifierRequest": {
"main": [
[
{
"node": "Classifier Chain",
"type": "main",
"index": 0
}
]
]
},
"Extractor Output Parser": {
"ai_outputParser": [
[
{
"node": "Extractor Chain",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "Classifier Chain",
"type": "ai_outputParser",
"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.
gmailOAuth2googleSheetsOAuth2ApiollamaApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow pulls unread Gmail messages, uses a local Ollama Llama 3.1 model to classify and extract action items, drafts a reply, and sends an approval request to Telegram before creating a threaded Gmail draft and logging the decision to Google Sheets. Runs manually and…
Source: https://n8n.io/workflows/16932/ — 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.
leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
Automatically capture, categorize, and log expenses from receipts, PDFs, voice notes, or text — powered by AI and integrated with Telegram and Google Sheets.
This n8n workflow automates the entire lead nurturing process from initial contact through a 3-email follow-up sequence, with intelligent reply detection and personalized AI-generated content. It's de
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.