n8n Tutorial: Expense Report Automation — Receipt Photo on Telegram → Google Sheets Automation
Your ops team burns hours every month on the same soul-crushing loop: employees hoard crumpled receipts, someone chases them over Slack, amounts get typed into a spreadsheet by hand, and half the entr
Your ops team burns hours every month on the same soul-crushing loop: employees hoard crumpled receipts, someone chases them over Slack, amounts get typed into a spreadsheet by hand, and half the entries are wrong by reconciliation day. It doesn't scale, it's error-prone, and it's the kind of work that makes good people quit. This tutorial shows you how to kill that loop entirely with a single n8n workflow: an employee photographs a receipt in Telegram, GPT-4o Vision reads it, and the structured expense lands in Google Sheets before the phone leaves their hand.
The problem: manual expense capture doesn't scale
Every manual expense process has the same three failure points. First, capture latency — receipts get lost between the purchase and the moment someone finally logs them, sometimes weeks later, when nobody remembers what the $47 charge was for. Second, transcription error — humans typing amounts, dates, and vendor names into cells make mistakes, and those mistakes surface at the worst possible time, during close or audit. Third, coordination overhead — the finance person becomes a nag, pinging people for missing receipts and re-categorizing everything by hand.
The traditional fix is expensive SaaS (Expensify, Ramp, Concur) with per-seat pricing, forced card issuance, and data locked inside a vendor's walls. For a lean team that already lives in Telegram and Google Sheets, that's overkill. You don't need a platform. You need a pipe that turns a photo into a row.
The solution: Telegram in, structured row out
The architecture is deliberately simple. Telegram is the capture surface because your team already has it open — no app to install, no login, no training. A receipt photo is a message. n8n listens for that message, pulls the image, and hands it to GPT-4o Vision with a strict extraction prompt. The model returns four fields — amount, date, category, and merchant — as clean JSON. n8n appends that JSON as a new row in a monthly Google Sheet and fires a confirmation back to the employee so they know it landed.
End to end, it takes seconds. No transcription, no chasing, no separate app. The receipt is captured at the exact moment of purchase, while context is fresh, and the data is machine-structured from the start so your close is clean. Because everything runs on your own n8n instance, your financial data never touches a third-party expense vendor.
Step-by-step: building the workflow in n8n
Here is the full node chain. Six nodes, one credential setup, and you're live.
1. Telegram Trigger node. Create a bot via @BotFather in Telegram, copy the token, and add it as a Telegram API credential in n8n. Set the trigger's Updates field to message. This fires every time an employee sends the bot a message. To restrict the bot to your team, note each employee's Telegram user ID and filter on it downstream — otherwise anyone who finds the bot can write to your sheet.
2. Telegram node — Get File. A receipt sent as a photo arrives as an array of sizes in message.photo. Reference the last element for the highest resolution: {{ $json.message.photo[$json.message.photo.length - 1].file_id }}. Set the operation to Get File and enable Download so the binary lands in the item. If your team sends receipts as documents (PDF or uncompressed image) instead of photos, add an IF node to branch on message.document versus message.photo.
3. OpenAI node — Analyze Image (GPT-4o Vision). Add your OpenAI credential, set Resource to Image and Operation to Analyze Image, and select model gpt-4o. Point the input to the binary property from the previous node (usually data). The prompt is where the accuracy lives. Use something strict:
"You are a receipt parser. Extract these fields from the receipt image and return ONLY valid JSON, no markdown: {\"amount\": number, \"date\": \"YYYY-MM-DD\", \"category\": string, \"merchant\": string}. Category must be one of: Meals, Travel, Software, Office, Other. If a field is unreadable, use null. Amount must be the final total including tax."
Pinning the category to a fixed enum is what makes the output usable in a pivot table instead of a mess of 40 free-text variants of "food."
4. Code or Edit Fields node — parse and validate. GPT-4o occasionally wraps JSON in a code fence despite instructions. Add a small Code node to strip fences and JSON.parse the response, with a try/catch that routes failures to an error branch. This is your guardrail against a malformed row silently corrupting the sheet.
5. Google Sheets node — Append Row. Connect a Google Sheets OAuth2 credential, choose your monthly spreadsheet, and set the operation to Append. Map columns: Amount, Date, Category, Merchant, plus a server-side timestamp ({{ $now }}) and the employee's Telegram username ({{ $('Telegram Trigger').item.json.message.from.username }}) so every row is attributable. To split by month automatically, use an expression for the sheet name like {{ $now.format('yyyy-MM') }}.
6. Telegram node — Send Message. Close the loop with a confirmation: "✅ Logged: {{merchant}} — ${{amount}} ({{category}}) on {{date}}". This does double duty — it reassures the employee and it surfaces extraction errors instantly, so a wrong amount gets caught in the chat instead of at month-end.
Benefits: what this actually saves you
For a team of ten submitting fifteen receipts a month, that's 150 manual entries someone no longer types. At roughly two minutes of handling each — capture, transcribe, categorize — you reclaim about five hours of finance time monthly, and eliminate the entire chasing-people-for-receipts tax that never shows up on a timesheet but grinds everyone down.
The compounding win is data quality. Because categories are constrained and amounts are extracted from the source image, your monthly sheet is analysis-ready. You can pivot spend by category, by employee, or by merchant with zero cleanup. Reconciliation stops being a scavenger hunt. And the marginal cost is trivial: a GPT-4o Vision call on a receipt runs a fraction of a cent, versus $5–15 per seat per month for commercial expense software.
Common pitfalls and how to avoid them
Trusting the amount blindly. Vision models can misread a total, especially on faded thermal receipts or when tip lines confuse the final figure. The confirmation message is your safety net — but for anything above a threshold you care about, add an IF node that flags high-value receipts for a quick human glance before they're marked final.
Grabbing the wrong image resolution. A frequent bug: reading message.photo[0], which is Telegram's tiny thumbnail. Always take the last array element for full resolution, or GPT-4o will squint at a blurry postage stamp and hallucinate.
Timezone drift on dates. Receipts print local dates; $now in n8n uses your instance timezone. If your team is distributed, set the timezone explicitly in the workflow settings so a late-night purchase doesn't land on the wrong day and skew your monthly totals.
No open-bot lockdown. A Telegram bot is discoverable. Without a user-ID filter after the trigger, a stranger can inject junk rows into your finance sheet. Whitelist your team's IDs early — it's one Filter node and it saves you a nasty surprise.
Unhandled non-receipt messages. Someone will inevitably send the bot a "hey" or a meme. Route anything without a photo to a friendly reply ("Please send a receipt photo 📸") so those messages don't error out or hit the OpenAI node needlessly.
Build it once, filter it tight, and your expense process runs itself — a photo in Telegram, a clean row in Sheets, and a finance team that finally stops chasing paper.
Ja construimos isso pra voce
Nao comece do zero. O Expense Report Automation — Receipt Photo on Telegram → Google Sheets e um workflow n8n pronto para instalar — conecta suas ferramentas em minutos, sem codigo.
Instalar por $29.0 →