This workflow corresponds to n8n.io template #13557 — we link there as the canonical source.
This workflow follows the Agent → Google Sheets Tool 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": "4W7C2uWqNqjMOKSu",
"name": "[FOR TEMPLATE] AI Expense Tracker Agent",
"tags": [],
"nodes": [
{
"id": "telegram-trigger",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
16,
1280
],
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"typeVersion": 1.2
},
{
"id": "typing",
"name": "Typing...",
"type": "n8n-nodes-base.telegram",
"position": [
272,
1152
],
"parameters": {
"chatId": "={{ $json.message.chat.id }}",
"operation": "sendChatAction"
},
"typeVersion": 1.2
},
{
"id": "ai-agent",
"name": "AI Agent - Expense Tracker",
"type": "@n8n/n8n-nodes-langchain.agent",
"onError": "continueErrorOutput",
"position": [
480,
1280
],
"parameters": {
"text": "={{ $json.message.text }}",
"options": {
"systemMessage": "=You are an AI Expense Tracker assistant.\n\n## TOOLS\n\n| Tool | Function | When to Use |\n|------|----------|-------------|\n| add_expense | Record 1 expense to monthly sheet | Each expense item |\n| get_expense | View all expense data | Analysis, viewing |\n| delete_expense | Delete last N rows | User requests delete |\n| add_income | Record 1 income | Each income item |\n| get_income | View all income data | Analysis |\n| Calculator | Math operations | Totals, averages |\n| Think | Deep reasoning | Complex operations |\n\n## CORE RULES\n\n1. ALWAYS call tool first, THEN confirm to user.\n2. Multiple items = multiple sequential tool calls.\n3. Use Calculator for: sums, totals, averages.\n4. Use Think before: 3+ step operations or deletes.\n5. If tool fails, be honest with user.\n\n## DATA FORMAT\n\nExpense Sheet (dynamic monthly name like \"February 2026\"):\nDate (yyyy-mm-dd) | Category | Description | Amount | Payment Method\n\nIncome Log (static sheet \"Income Log\"):\nDate (yyyy-mm-dd) | Category | Description | Amount | Account\n\n### Expense Categories:\nFood | Transport | Shopping | Entertainment | Bills | Others\n\n### Income Categories:\nSalary | Freelance | Investment | Bonus | Transfer | Others\n\n### Payment Methods:\ngopay/ovo/dana \u2192 E-wallet | debit \u2192 Debit | cc \u2192 Credit Card | transfer \u2192 Transfer | cash \u2192 Cash\n\n### Amount Parsing:\n25rb/25k = 25000 | 1.5jt = 1500000\n\n## WORKFLOWS\n\nRecord Expense:\n1. Parse input (amount, category, desc, payment, date)\n2. Call add_expense \u2192 wait for result\n3. If success \u2192 Calculator(total today) \u2192 respond\n4. Multiple items: repeat per item\n\nRecord Income:\n1. Parse input\n2. Call add_income \u2192 wait\n3. Respond with confirmation\n\nView Data:\n1. Call get_expense/get_income\n2. Filter by period\n3. Calculator \u2192 sums\n4. Format response\n\nDelete:\n\u26a0\ufe0f Can ONLY delete last N rows!\n1. Think \u2192 analyze request\n2. Call get_expense \u2192 check data\n3. If entry is last row \u2192 confirm \u2192 delete\n4. If not last row \u2192 explain limitation\n\n## RESPONSES\n\nExpense recorded:\n\u2705 [Description] - $[amount]\n\ud83d\udcc2 [Category] | \ud83d\udcb3 [Payment]\nToday's total: $[total]\n\nIncome recorded:\n\ud83d\udcb0 [Description] - $[amount] \u2192 [Account]\n\nView data:\n\ud83d\udcca [Period] expenses:\n[Category]: $[total]\n \u2022 [item] - $[amount]\n\u2501\u2501\u2501\n\ud83d\udcb0 Total: $[grand total]\n\n## COMMANDS\n/help \u2192 all commands | /today \u2192 today's expenses | /week \u2192 this week\n/month \u2192 monthly summary | /delete \u2192 delete entry | /stats \u2192 statistics\n\nCurrent time: {{ $now.format('cccc DD HH:mm') }}\nDate format: yyyy-mm-dd"
},
"promptType": "define"
},
"executeOnce": false,
"retryOnFail": true,
"typeVersion": 1.7,
"waitBetweenTries": 5000
},
{
"id": "gemini-model",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
80,
1520
],
"parameters": {
"options": {
"temperature": 0.6
}
},
"typeVersion": 1
},
{
"id": "memory",
"name": "Simple Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"position": [
224,
1520
],
"parameters": {
"sessionKey": "={{ $json.message.chat.id }}",
"sessionIdType": "customKey"
},
"typeVersion": 1.3
},
{
"id": "add-expense",
"name": "add_expense",
"type": "n8n-nodes-base.googleSheetsTool",
"position": [
352,
1520
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $fromAI('Date', '', 'string') }}",
"Amount": "={{ $fromAI('Amount', '', 'string') }}",
"Category": "={{ $fromAI('Category', '', 'string') }}",
"Description": "={{ $fromAI('Description', '', 'string') }}",
"Payment_Method": "={{ $fromAI('Payment_Method', '', 'string') }}"
},
"schema": [
{
"id": "Date",
"type": "string",
"displayName": "Date"
},
{
"id": "Category",
"type": "string",
"displayName": "Category"
},
{
"id": "Description",
"type": "string",
"displayName": "Description"
},
{
"id": "Amount",
"type": "string",
"displayName": "Amount"
},
{
"id": "Payment_Method",
"type": "string",
"displayName": "Payment_Method"
}
],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {
"cellFormat": "USER_ENTERED"
},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $now.setLocale('en').toFormat('MMMM yyyy') }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID_HERE"
},
"descriptionType": "manual",
"toolDescription": "Add one expense entry to Google Sheets. Parameters:\n- Date: YYYY-MM-DD\n- Category: Food/Transport/Shopping/Entertainment/Bills/Others\n- Description: Short description\n- Amount: Number only\n- Payment_Method: Cash/Card/E-wallet/etc"
},
"typeVersion": 4.7
},
{
"id": "get-expense",
"name": "get_expense",
"type": "n8n-nodes-base.googleSheetsTool",
"position": [
464,
1520
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $now.setLocale('en').toFormat('MMMM yyyy') }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID_HERE"
}
},
"typeVersion": 4.7
},
{
"id": "delete-expense",
"name": "delete_expense",
"type": "n8n-nodes-base.googleSheetsTool",
"position": [
592,
1520
],
"parameters": {
"operation": "delete",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $now.setLocale('en').toFormat('MMMM yyyy') }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID_HERE"
},
"numberToDelete": "={{ $fromAI('Number_of_Rows_to_Delete', '', 'number') }}"
},
"typeVersion": 4.7
},
{
"id": "add-income",
"name": "add_income",
"type": "n8n-nodes-base.googleSheetsTool",
"position": [
720,
1520
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $fromAI('Date', '', 'string') }}",
"Amount": "={{ $fromAI('Amount', '', 'string') }}",
"Account": "={{ $fromAI('Account', '', 'string') }}",
"Category": "={{ $fromAI('Category', '', 'string') }}",
"Description": "={{ $fromAI('Description', '', 'string') }}"
},
"schema": [
{
"id": "Date",
"type": "string",
"displayName": "Date"
},
{
"id": "Category",
"type": "string",
"displayName": "Category"
},
{
"id": "Description",
"type": "string",
"displayName": "Description"
},
{
"id": "Amount",
"type": "string",
"displayName": "Amount"
},
{
"id": "Account",
"type": "string",
"displayName": "Account"
}
],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Income Log"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID_HERE"
}
},
"typeVersion": 4.7
},
{
"id": "get-income",
"name": "get_income",
"type": "n8n-nodes-base.googleSheetsTool",
"position": [
848,
1520
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Income Log"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID_HERE"
}
},
"typeVersion": 4.7
},
{
"id": "calculator",
"name": "Calculator",
"type": "@n8n/n8n-nodes-langchain.toolCalculator",
"position": [
960,
1520
],
"parameters": {},
"typeVersion": 1
},
{
"id": "think",
"name": "Think",
"type": "@n8n/n8n-nodes-langchain.toolThink",
"position": [
1072,
1520
],
"parameters": {},
"typeVersion": 1.1
},
{
"id": "markdown-formatter",
"name": "Markdown to HTML Formatter",
"type": "n8n-nodes-base.code",
"position": [
832,
1104
],
"parameters": {
"jsCode": "/**\n * Markdown to HTML converter for Telegram\n */\n\nconst MAX_TELEGRAM = 4096;\nconst SAFE_BUDGET = 4000;\n\nfunction escapeHtml(text) {\n if (!text) return '';\n return String(text)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n}\n\nfunction markdownToHtml(inputText) {\n if (!inputText) return '';\n let text = String(inputText);\n const placeholders = [];\n let placeholderIndex = 0;\n \n const addPlaceholder = (html) => {\n const ph = `\\x00PH${placeholderIndex++}\\x00`;\n placeholders.push({ ph, html });\n return ph;\n };\n \n text = text.replace(/```([\\s\\S]*?)```/g, (m, code) => {\n return addPlaceholder(`<pre>${escapeHtml(code.trim())}</pre>`);\n });\n \n text = text.replace(/`([^`\\n]+)`/g, (m, code) => {\n return addPlaceholder(`<code>${escapeHtml(code)}</code>`);\n });\n \n text = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (m, label, url) => {\n return addPlaceholder(`<a href=\"${url.trim()}\">${escapeHtml(label)}</a>`);\n });\n \n text = text.replace(/\\*\\*([^*]+)\\*\\*/g, (m, content) => {\n return addPlaceholder(`<b>${escapeHtml(content)}</b>`);\n });\n \n text = text.replace(/(?<!\\*)\\*([^*\\n]+)\\*(?!\\*)/g, (m, content) => {\n return addPlaceholder(`<i>${escapeHtml(content)}</i>`);\n });\n \n text = escapeHtml(text);\n \n for (const { ph, html } of placeholders) {\n text = text.replace(ph, html);\n }\n \n text = text.replace(/\\n{3,}/g, '\\n\\n');\n return text.trim();\n}\n\nfunction chunkForTelegram(text, maxLen = SAFE_BUDGET) {\n if (!text || text.length <= maxLen) return [text || ''];\n const parts = [];\n const paragraphs = text.split(/\\n{2,}/);\n \n for (const p of paragraphs) {\n if (p.length <= maxLen) {\n parts.push(p);\n } else {\n const lines = p.split('\\n');\n for (const line of lines) {\n if (line.length <= maxLen) {\n parts.push(line);\n } else {\n const re = new RegExp(`.{1,${maxLen}}`, 'g');\n const pieces = line.match(re) || [];\n parts.push(...pieces);\n }\n }\n }\n }\n return parts.filter(p => p.length > 0);\n}\n\nconst inputItems = $input.all();\nconst out = [];\n\nfor (const item of inputItems) {\n const j = item.json || {};\n const raw = j.output ?? j.message ?? j.text ?? j.content ?? '';\n const formatted = markdownToHtml(raw);\n const chunks = chunkForTelegram(formatted, SAFE_BUDGET);\n\n chunks.forEach((chunk, idx) => {\n out.push({\n json: { ...j, output: chunk, message: chunk, part_index: idx + 1, parts_total: chunks.length },\n binary: item.binary,\n });\n });\n}\n\nreturn out;"
},
"typeVersion": 2
},
{
"id": "send-message",
"name": "Send Telegram Message",
"type": "n8n-nodes-base.telegram",
"position": [
1040,
1104
],
"parameters": {
"text": "={{ $json.message }}",
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"additionalFields": {
"parse_mode": "HTML",
"appendAttribution": false
}
},
"typeVersion": 1.2
},
{
"id": "send-error",
"name": "Send Error Message",
"type": "n8n-nodes-base.telegram",
"position": [
832,
1296
],
"parameters": {
"text": "=Error occurred. Please try again!\n\nIf the issue persists, contact support.",
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"additionalFields": {
"appendAttribution": false
}
},
"typeVersion": 1.2
},
{
"id": "sticky_prereq",
"name": "\ud83d\udccb Prerequisites",
"type": "n8n-nodes-base.stickyNote",
"position": [
-720,
-128
],
"parameters": {
"width": 720,
"height": 1280,
"content": "\ud83d\udccb PREREQUISITES\n\nBefore using this workflow, ensure you have:\n\n\u2460 Google Account\n\u2022 Access to Google Sheets\n\u2022 OAuth2 credentials from Google Cloud Console\n\n\u2461 Telegram Bot\n\u2022 Create bot via @BotFather\n\u2022 Save the Bot Token\n\u2022 Bot will be your expense assistant\n\n\u2462 Google Gemini API Key\n\u2022 Get from Google AI Studio (ai.google.dev)\n\u2022 Powers the AI agent\n\n\u2463 Google Spreadsheet Structure\n\nMonthly Expense Sheets (one per month):\n\u2022 Name format: \"February 2026\", \"March 2026\", etc.\n\u2022 Columns: Date | Category | Description | Amount | Payment_Method\n\nIncome Log (static sheet):\n\u2022 Name: \"Income Log\"\n\u2022 Columns: Date | Category | Description | Amount | Account\n\n---\n\nSupported Categories:\nFood \u2022 Transport \u2022 Shopping \u2022 Entertainment \u2022 Bills \u2022 Others\n\nPayment Methods:\nCash \u2022 Card \u2022 E-wallet (GoPay/OVO/DANA)\n\n---\n\n\ud83d\udc64 Created by: Andi Sakti\n\ud83d\udd17 LinkedIn: https://www.linkedin.com/in/andi-sakti-cpos-7922b4184/"
},
"typeVersion": 1
},
{
"id": "sticky_config",
"name": "\u2699\ufe0f Configuration",
"type": "n8n-nodes-base.stickyNote",
"position": [
32,
-128
],
"parameters": {
"width": 768,
"height": 592,
"content": "\u2699\ufe0f CONFIGURATION\n\nStep 1 \u2014 Create Credentials\nGo to Settings \u2192 Credentials:\n\n|Credential | Type | Source |\n|---|---|---|\n| Telegram API | Bot Token | @BotFather |\n| Google Sheets | OAuth2 | Google Cloud Console |\n| Google Gemini | API Key | ai.google.dev |\n\nStep 2 \u2014 Set Google Sheets Document ID\nUpdate YOUR_GOOGLE_SHEET_ID_HERE in ALL 5 Google Sheets nodes:\n\u270f\ufe0f add_expense | get_expense | delete_expense\n\u270f\ufe0f add_income | get_income\n\nFind Document ID in URL:\ndocs.google.com/spreadsheets/d/THIS_PART/edit\n\nStep 3 \u2014 Assign Credentials to Nodes\n\u2022 Telegram nodes (4 nodes) \u2192 Your Telegram API\n\u2022 Google Sheets nodes (5 nodes) \u2192 Your Google Sheets\n\u2022 Gemini Chat Model (1 node) \u2192 Your Gemini API\n\nStep 4 \u2014 Test & Activate\n1. Click \"Test Workflow\"\n2. Send message to your Telegram bot\n3. Verify response and Sheet entry\n4. Activate when working!\n\n\u26a0\ufe0f ALL 5 Google Sheets nodes need the Document ID!"
},
"typeVersion": 1
},
{
"id": "sticky_info",
"name": "\ud83d\udca1 Important Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
32,
480
],
"parameters": {
"width": 768,
"height": 672,
"content": "\ud83d\udca1 HOW IT WORKS\n\nThis is a Telegram-based AI expense tracker.\nSend natural language and AI will:\n\u2713 Log expenses & income to Google Sheets\n\u2713 Auto-categorize transactions\n\u2713 Calculate totals and summaries\n\u2713 Handle multiple items in one message\n\nDynamic Sheet Names:\nExpense sheets use automatic monthly targeting:\n$now.setLocale('en').toFormat('MMMM yyyy')\n\nThis selects the current month's sheet automatically.\nCreate sheets in advance!\n\nAvailable Commands:\n/help \u2014 Show all commands\n/today \u2014 Today's expenses\n/week \u2014 This week's expenses\n/month \u2014 Monthly summary\n/delete \u2014 Delete an entry\n/stats \u2014 Statistics\n\nFeatures:\n\u2022 Shows \"typing...\" while processing\n\u2022 Auto-splits long responses\n\u2022 Friendly error messages\n\u2022 Monthly sheet auto-selection\n\u2022 All calculations use Calculator tool\n\n\u26a0\ufe0f Delete Limitation:\nCan only delete the LAST N rows from the sheet!\nMiddle entries require manual deletion."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false,
"executionOrder": "v1",
"saveManualExecutions": true,
"saveExecutionProgress": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all"
},
"versionId": "a0cbacf7-ce47-40f4-aafb-0f1ad2030c28",
"connections": {
"Think": {
"ai_tool": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_tool",
"index": 0
}
]
]
},
"Calculator": {
"ai_tool": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_tool",
"index": 0
}
]
]
},
"add_income": {
"ai_tool": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_tool",
"index": 0
}
]
]
},
"get_income": {
"ai_tool": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_tool",
"index": 0
}
]
]
},
"add_expense": {
"ai_tool": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_tool",
"index": 0
}
]
]
},
"get_expense": {
"ai_tool": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_tool",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_memory",
"index": 0
}
]
]
},
"delete_expense": {
"ai_tool": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_tool",
"index": 0
}
]
]
},
"Telegram Trigger": {
"main": [
[
{
"node": "Typing...",
"type": "main",
"index": 0
},
{
"node": "AI Agent - Expense Tracker",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent - Expense Tracker",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"AI Agent - Expense Tracker": {
"main": [
[
{
"node": "Markdown to HTML Formatter",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Error Message",
"type": "main",
"index": 0
}
]
]
},
"Markdown to HTML Formatter": {
"main": [
[
{
"node": "Send Telegram Message",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Brief Description: Your personal finance assistant inside Telegram! Chat naturally with an AI agent to track expenses, log income, view spending history, and manage your budget—all through simple conversation. No forms, no spreadsheets, just chat.
Source: https://n8n.io/workflows/13557/ — 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 Trigger receives incoming messages (text, voice, photo, document). Switch routes by message type to appropriate processors: Text → forwarded as-is. Voice → downloaded and sent to Transcribe a
Transform your Telegram messenger into a powerful, multi-modal personal or team assistant. This n8n workflow creates an intelligent agent that can understand text, voice, images, and documents, and ta
Jarvis is a powerful multi-agent productivity assistant built in n8n. It works directly from Telegram and can understand both text messages and voice notes.
> AI-powered nutrition assistant for Telegram — log meals, set goals, and get personalized daily reports with Google Sheets integration.
This automation is designed to help you generate AI-powered music tracks, cover art, and fully rendered music videos — all triggered from a simple Telegram chat and managed via Google Sheets.