Telegram + Google Sheets, expenses logged and categorized
Expense tracking usually fails for one boring reason. You don’t forget to spend money, you forget to write it down, then you end up “catching up” on a Sunday night with a pile of receipts.
Marketing managers doing client reimbursements feel it. Small business owners feel it harder. And if you’re a freelancer juggling tools, Telegram expense logging automation is the first thing that makes tracking feel effortless instead of annoying.
This n8n workflow turns your Telegram messages (text, voice, photos, PDFs) into clean, categorized rows in Google Sheets, then lets you ask questions like “How much on groceries this month?” without building a spreadsheet monster.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Telegram + Google Sheets, expenses logged and categorized
flowchart LR
subgraph sg0["JsonStorageMcp Flow"]
direction LR
n0["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>Trigger"]
n1@{ icon: "mdi:wrench", form: "rounded", label: "JsonStorageMcp", pos: "b", h: 48 }
n2@{ icon: "mdi:wrench", form: "rounded", label: "Calculator", pos: "b", h: 48 }
n3@{ icon: "mdi:wrench", form: "rounded", label: "ExpenseAssistant", pos: "b", h: 48 }
n4@{ icon: "mdi:brain", form: "rounded", label: "Gpt4o", pos: "b", h: 48 }
n5@{ icon: "mdi:wrench", form: "rounded", label: "Think", pos: "b", h: 48 }
n6@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Switch", pos: "b", h: 48 }
n7@{ icon: "mdi:swap-vertical", form: "rounded", label: "TextOutput", pos: "b", h: 48 }
n8["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>WelcomeMessage"]
n9@{ icon: "mdi:swap-vertical", form: "rounded", label: "AudioOutput", pos: "b", h: 48 }
n10@{ icon: "mdi:swap-vertical", form: "rounded", label: "AgentInput", pos: "b", h: 48 }
n11["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>NotAuthorizedMessage"]
n12@{ icon: "mdi:swap-horizontal", form: "rounded", label: "IfAccessAllowed", pos: "b", h: 48 }
n13@{ icon: "mdi:swap-horizontal", form: "rounded", label: "IfFirstRun", pos: "b", h: 48 }
n14["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>ReplyText"]
n15@{ icon: "mdi:swap-vertical", form: "rounded", label: "FileOutput", pos: "b", h: 48 }
n16["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>GetAudioFile"]
n17["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>ExtractFileText"]
n18@{ icon: "mdi:memory", form: "rounded", label: "AssitantMemory", pos: "b", h: 48 }
n19@{ icon: "mdi:memory", form: "rounded", label: "SimpleMemory", pos: "b", h: 48 }
n20@{ icon: "mdi:robot", form: "rounded", label: "AIAgent", pos: "b", h: 48 }
n21["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>TranscribeAudio"]
n22@{ icon: "mdi:swap-vertical", form: "rounded", label: "Input", pos: "b", h: 48 }
n23["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>GetAttachedFile"]
n24@{ icon: "mdi:swap-horizontal", form: "rounded", label: "IfStateExist", pos: "b", h: 48 }
n25["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>DeleteProcessing"]
n26@{ icon: "mdi:wrench", form: "rounded", label: "Think1", pos: "b", h: 48 }
n31["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>GetAttachedPhoto"]
n32@{ icon: "mdi:swap-vertical", form: "rounded", label: "ImageOutput", pos: "b", h: 48 }
n33["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>ExtractImageText"]
n34["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/telegram.svg' width='40' height='40' /></div><br/>SendProcessing"]
n35["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>GetExecutionState"]
n36@{ icon: "mdi:brain", form: "rounded", label: "Sonnet45", pos: "b", h: 48 }
n37["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>GetAppSettings"]
n38["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>SaveAppSettings"]
n39["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>SaveExecutionState"]
n4 -.-> n20
n22 --> n37
n5 -.-> n3
n6 --> n8
n6 --> n7
n6 --> n16
n6 --> n23
n6 --> n31
n26 -.-> n20
n20 --> n14
n0 --> n22
n36 -.-> n3
n10 --> n20
n2 -.-> n3
n15 --> n10
n13 --> n38
n7 --> n10
n9 --> n10
n32 --> n10
n16 --> n21
n24 --> n25
n19 -.-> n20
n18 -.-> n3
n37 --> n13
n37 --> n12
n1 -.-> n3
n34 --> n39
n17 --> n15
n23 --> n17
n12 --> n6
n12 --> n34
n12 --> n35
n12 --> n11
n21 --> n9
n3 -.-> n20
n33 --> n32
n31 --> n33
n35 --> n24
end
subgraph sg1["Cleanup Flow"]
direction LR
n27@{ icon: "mdi:play-circle", form: "rounded", label: "Cleanup", pos: "b", h: 48 }
n28["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>ForEachCategory"]
n29["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>ForEachCategoryItem"]
n30["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>DeleteItem"]
n27 --> n28
n28 --> n29
n29 --> n30
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0,n27 trigger
class n20 ai
class n4,n36 aiModel
class n1,n2,n3,n5,n26 ai
class n18,n19 ai
class n6,n12,n13,n24 decision
class n17,n21,n33,n35,n37,n38,n39,n28,n29,n30 api
classDef customIcon fill:none,stroke:none
class n0,n8,n11,n14,n16,n17,n21,n23,n25,n31,n33,n34,n35,n37,n38,n39,n28,n29,n30 customIcon
The Problem: Expense Tracking Breaks the Moment You Get Busy
You already have the intent to track expenses. The breakdown happens in the tiny gap between “I just paid” and “I should log that.” Receipts end up in your pocket, screenshots disappear into camera roll, and voice notes sit in your head until they don’t. Then you’re stuck reverse-engineering dates, vendors, and categories from messy bank statements. Honestly, the worst part isn’t the time. It’s the constant mental tab you keep open because you know your numbers aren’t current.
It adds up fast. Here’s where it usually goes sideways.
- Manual entry turns into a weekly chore, so you delay it and your data becomes unreliable.
- Receipts come in different formats (photos, PDFs, crumpled paper), which makes “one process” impossible.
- Categories drift over time, so “Dining Out” becomes “Food,” “Meals,” and “Restaurants” in the same month.
- You can’t answer basic questions quickly, because totals and breakdowns require extra spreadsheet work.
The Solution: Log Expenses From Telegram (Text, Voice, Receipts)
This workflow gives you a private Telegram bot that behaves like an expense assistant. You send a receipt photo, a PDF invoice, a voice message (“Bought coffee for five euros”), or a quick text (“50 uber”). The workflow pulls the content from Telegram, converts it into usable text (OCR for images and documents, transcription for audio), and hands it to an AI agent that extracts the important parts: amount, date, vendor, currency, and category. Then it stores the structured result in your expense store and writes a clean, consistent log in Google Sheets so you can filter, sort, and summarize whenever you want.
It starts with a Telegram message trigger. From there, n8n routes the message type, runs OCR or transcription when needed, and sends one standardized payload to the AI router. Finally, the bot replies back in Telegram with a friendly “Added: 45.50 EUR – Groceries (Lidl)” style confirmation, and your sheet stays up to date in the background.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you log 5 expenses a day. Manually, it’s usually about 5 minutes each once you find the receipt, remember the category, and type it in, so you’re at roughly 25 minutes a day (and more when you miss a day). With this workflow, you forward a receipt photo or voice note in Telegram in about 1 minute and you’re done. The AI processing runs while you move on, then your Google Sheet is already updated for month-end totals.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Telegram Bot to receive messages and reply.
- Google Sheets to store a clean expense ledger.
- OpenRouter API key (get it from your OpenRouter dashboard).
Skill level: Intermediate. You’ll connect accounts, paste API keys, and tweak a couple of AI prompts for categories and currency.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
Telegram message comes in. The workflow triggers on any message to your bot, then captures context (chat ID, attachments, message type) so replies go to the right place.
Privacy lock and routing happen immediately. On first use, the bot “claims” a single user and saves preferences. After that, unauthorized chats get rejected, which keeps your expense data private.
Voice, receipt photos, and PDFs get converted into text. Audio files are fetched from Telegram and transcribed. Documents and images are pulled and run through OCR so the AI can read totals, dates, and vendor names.
The AI agent extracts and categorizes, then stores the result. The router sends the standardized payload to the expense assistant, which validates the structure, applies categories, calculates stats when you ask, and writes output for Google Sheets and Telegram.
You can easily modify the default currency and category list to match your bookkeeping. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Telegram Trigger
Set up the workflow entry point to capture Telegram messages and attachments.
- Add and open Telegram Intake Trigger.
- Set Updates to
message. - In Additional Fields, enable Download and set Image Size to
extraLarge. - Credential Required: Connect your telegramApi credentials.
Step 2: Connect App Preferences and Authorization
Capture language context, retrieve app settings, and control who can use the bot.
- Open Capture Message Context and set the assignment document_language to
{{ $json.message.from.language_code ?? "en" }}with Include Other Fields enabled. - Open Fetch App Preferences and set URL to
https://api.ainoflow.io/api/v1/storage/json/config/expense-app-settings. - Credential Required: Connect your httpBearerAuth credentials in Fetch App Preferences.
- Configure Authorize Chat Access to compare
{{ $('Telegram Intake Trigger').item.json.message.chat.id }}with{{ $json.chat_id ?? $('Telegram Intake Trigger').item.json.message.chat.id }}, and allow a404status for first-time setup. - Ensure Fetch App Preferences outputs to both Detect First Use and Authorize Chat Access in parallel.
- In Detect First Use, keep the condition to check
{{ $json.error.status }}equals404, then connect to Store App Preferences. - Open Store App Preferences and map fields:
{{ $('Telegram Intake Trigger').item.json.message.chat.id }},{{ $('Telegram Intake Trigger').item.json.message.date }}, and{{ $('Telegram Intake Trigger').item.json.message.chat.first_name }}. - Credential Required: Connect your httpBearerAuth credentials in Store App Preferences.
Step 3: Configure Message Routing and Input Mapping
Route Telegram messages into text, voice, document, and photo processing paths and normalize all content into a single agent input format.
- Open Route Message Type and keep the outputs for
/start, text, voice, document, and photo using expressions like{{ $('Telegram Intake Trigger').item.json.message.text }}and{{ $('Telegram Intake Trigger').item.json.message.voice.file_id }}. - Authorize Chat Access outputs to Route Message Type, Post Processing Notice, and Fetch Processing State in parallel.
- Configure Send Welcome Note to use Chat ID
{{ $('Telegram Intake Trigger').item.json.message.chat.id }}and the provided welcome message text. - Credential Required: Connect your telegramApi credentials in Send Welcome Note.
- For text messages, open Map Text Payload and map text to
{{ $('Telegram Intake Trigger').item.json.message.text }}and chat_id to{{ $('Telegram Intake Trigger').item.json.message.chat.id }}. - For voice messages, configure Retrieve Voice File using
{{ $('Telegram Intake Trigger').item.json.message.voice.file_id }}, then Transcribe Voice with the languages field set to{{ $('Capture Message Context').item.json.document_language }}. - Credential Required: Connect your telegramApi credentials in Retrieve Voice File and httpBearerAuth credentials in Transcribe Voice.
- Map transcriptions in Map Voice Payload using
{{ $json.content[0].text }}and{{ $('Telegram Intake Trigger').item.json.message.chat.id }}. - For documents and photos, configure Retrieve Document File and Retrieve Photo File with file IDs from the trigger, then run Extract Document Text and Extract Image Text with
multipart-form-dataand{{ $('Capture Message Context').item.json.document_language }}. - Credential Required: Connect your telegramApi credentials to file retrieval nodes and httpBearerAuth credentials to the extraction nodes.
- Normalize all document/photo outputs in Map File Payload and Map Image Payload using the combined text template:
User uploaded document / photo: {{ $('Capture Message Context').item.json.message.caption ?? "" }} {{ $json.content ? $json.content[0].text : "The uploaded file does not contain text content." }}. - Finally, open Prepare Agent Input and map text to
{{ $json.text }}and chat_id to{{ $json.chat_id }}.
Step 4: Set Up AI Routing and Tools
Configure the primary AI router and its specialized tools to interpret and store expenses.
- Open Primary AI Router and set Text to
{{ $('Prepare Agent Input').item.json.text }}with the provided system prompt. - Attach Main Memory Buffer to Primary AI Router using Session Key
agent_{{ $('Prepare Agent Input').item.json.chat_id }}and Context Window Length30. - Attach Reflection Tool as a tool for Primary AI Router.
- Connect OpenAI Model as the language model with Model set to
openai/gpt-4o. - Credential Required: Connect your openRouterApi credentials in OpenAI Model.
- Open Expense Specialist and keep the tool description and system message intact for storage rules and categories.
- Attach Assistant Memory Buffer to Expense Specialist with Session Key
expense_assitant_{{ $('Prepare Agent Input').item.json.chat_id }}. - Connect Sonnet Model as the language model for Expense Specialist with Model
anthropic/claude-sonnet-4.5. - Credential Required: Connect your openRouterApi credentials in Sonnet Model.
- Attach Reasoning Tool, JSON Storage Tool, and Math Helper to Expense Specialist as tools.
- Credential Required: Connect your httpBearerAuth credentials in JSON Storage Tool.
Step 5: Configure Processing Notices and State Cleanup
Use Telegram notices and HTTP storage to display processing status and remove temporary messages once done.
- Open Post Processing Notice and set Chat ID to
{{ $('Telegram Intake Trigger').item.json.message.chat.id }}. - Credential Required: Connect your telegramApi credentials in Post Processing Notice.
- Open Record Processing State and set URL to
https://api.ainoflow.io/api/v1/storage/json/expense-app-processing/{{ $('Telegram Intake Trigger').item.json.update_id }}. - Map Body Parameters to
{{ $json.result.chat.id }}and{{ $json.result.message_id }}, and set expiresMs to3600000. - Credential Required: Connect your httpBearerAuth credentials in Record Processing State.
- Open Fetch Processing State with the same URL pattern and connect to Check State Presence, which then deletes the status via Remove Processing Note.
- Credential Required: Connect your httpBearerAuth credentials in Fetch Processing State and telegramApi credentials in Remove Processing Note.
- Keep Send Unauthorized Alert connected to the false path of Authorize Chat Access with message
You are not authorized to use this bot.. - Credential Required: Connect your telegramApi credentials in Send Unauthorized Alert.
Step 6: Configure Agent Output Delivery
Deliver AI responses back to the user after processing the input.
- Open Send Agent Reply and set Text to
{{ $json.output }}. - Set Chat ID to
{{ $('Prepare Agent Input').item.json.chat_id }}. - Ensure Additional Fields include parse_mode set to
Markdown. - Credential Required: Connect your telegramApi credentials in Send Agent Reply.
Step 7: Configure Manual Cleanup Utilities
Use the manual cleanup path to purge stored expense data when required.
- Open Manual Cleanup Trigger to allow on-demand execution.
- Configure Iterate Categories with URL
https://api.ainoflow.io/api/v1/storage/json. - Configure Iterate Category Items with URL
https://api.ainoflow.io/api/v1/storage/json/{{ $json.category }}. - Configure Delete Stored Item with URL
https://api.ainoflow.io/api/v1/storage/json/{{ $json.category }}/{{ $json.key }}and MethodDELETE. - Credential Required: Connect your httpBearerAuth credentials in Iterate Categories, Iterate Category Items, and Delete Stored Item.
Step 8: Test and Activate Your Workflow
Validate all paths and activate the workflow for production use.
- Click Execute Workflow and send a Telegram text like
spent 50 on groceriesto confirm Send Agent Reply returns a formatted response. - Test a voice note and a receipt photo to confirm Transcribe Voice and Extract Image Text flow into Prepare Agent Input.
- Verify the processing notice appears from Post Processing Notice and is removed by Remove Processing Note.
- If a chat is not authorized, confirm Send Unauthorized Alert is sent.
- When all tests pass, toggle the workflow to Active to enable production execution.
Common Gotchas
- Telegram bot credentials can expire or be mis-scoped. If replies stop working, check the BotFather token and the Telegram node credentials in n8n first.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- Default prompts in AI nodes are generic. Add your brand voice early or you’ll be editing outputs forever.
Frequently Asked Questions
About 30 minutes if you already have your Telegram bot token and API keys ready.
No coding required. You’ll mostly be connecting accounts and pasting API keys into n8n.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in OpenRouter API usage for AI processing and OCR/transcription API costs.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and you should. You can update the category list inside the Expense Assistant prompt, and change the default currency there as well (it defaults to EUR). If you prefer different AI models, swap the OpenRouter chat model nodes used for the main processing and the assistant responses. Many people also tweak the reply formatting so Google Sheets gets short descriptions while Telegram gets a nicer summary.
Most of the time it’s an invalid or rotated bot token in your Telegram credentials. It can also be a privacy setting mismatch (wrong chat, wrong bot) or missing permissions to fetch files, which shows up when audio/photo/document retrieval nodes start failing. Check the Telegram node credential in n8n, then send a simple text message to confirm the trigger still fires before testing receipts again.
A typical setup can handle hundreds of expenses a month without issue; the real limits come from your n8n plan and your OCR/transcription/AI API rate limits.
It depends on what you’re doing. If you only want “Telegram message → add a row,” Zapier or Make can be quicker to click together. This workflow is more than that: it routes message types, fetches Telegram files, runs OCR and transcription, and uses an AI agent with memory to both log expenses and answer questions like “compare last 3 months.” n8n also gives you a self-hosting option, which is useful if you don’t want per-task pricing every time you log a receipt. If you want someone to sanity-check your setup, Talk to an automation expert.
Once this is running, your sheet stays current without you thinking about it. The bot handles the repetitive capture, and you finally get clean monthly totals when you need them.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.