Smart Receipt Organizer
Shared 12/8/2025
15 views
Visual Workflow
JSON Code
{
"id": "4zHWtizvsvvFg5By",
"meta": {
"instanceId": "0023d4f5dfb57fec37ca9d230f69e0e19118190dca2bfc4d821fffd957cc37e7",
"templateCredsSetupCompleted": true
},
"name": "Smart Receipt Organizer",
"tags": [],
"nodes": [
{
"id": "b092b3b3-989e-4ac3-abc2-f785293eae72",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
-576,
-416
],
"webhookId": "auto-generated",
"parameters": {
"updates": [
"message",
"channel_post"
],
"additionalFields": {}
},
"credentials": {
"telegramApi": {
"id": "xuFTbOQ62YpwhOpY",
"name": "Telegram account"
}
},
"typeVersion": 1.1
},
{
"id": "867168cc-7ee7-4a42-af40-2e5bd1158f05",
"name": "Backup to Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
544,
-416
],
"parameters": {
"name": "={{ $json.newFileName }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.driveFolderId }}"
}
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "EkoZTEdJjBuNNGjR",
"name": "Google Drive account"
}
},
"typeVersion": 3
},
{
"id": "715fa366-277c-4081-b1df-e426a45d195c",
"name": "Format Data",
"type": "n8n-nodes-base.code",
"position": [
304,
-416
],
"parameters": {
"jsCode": "// --------------------------------------------------------\n// CONFIGURATION: PASTE YOUR GOOGLE DRIVE FOLDER IDS HERE\n// --------------------------------------------------------\nconst FOLDER_IDS = {\n \"Eating Out\": \"1g9DjOYM9DZIYWziVI5L5MzmLxMgGHB55\",\n \"Groceries\": \"16DmSj7kAA2H4PDLy_sOG3C_7a5ufNcUw\",\n \"Utilities\": \"1cR6bubHvq_QcP1SGTfmYZ-83k07vPHA4\",\n \"Transport\": \"1zr7kNNV030t3a912Q3ZL44cqFWId8sDr\",\n \"Shopping\": \"1ekTo1jjZJRccu-SRNouKq2Yl2pEdnvtP\",\n \"Subscriptions\": \"1xd6SzHSjm197h9SgQTgVhFy02UV-oC4l\",\n \"Tech & Office\": \"1_ucFcwTlQWeT0zbRH-CJQBnBnhTbt3ox\",\n \"Medical\": \"1z-dHiIqLLfI1c_HWsKo7XPloQGBinwj6\",\n \"Miscellaneous\": \"1edg_WVd9edu1UeynSSyn865vV72VgmtW\" \n};\n\n// --------------------------------------------------------\n// 1. EXTRACT AI RESPONSE\n// --------------------------------------------------------\nconst item = $input.first();\nconst json = item.json;\n\n// Handle Google Gemini's deeply nested structure\nlet aiOutput = json.candidates?.[0]?.content?.parts?.[0]?.text || json.text || json.response;\n\nif (!aiOutput) {\n throw new Error(\"Could not find text in AI response. Check input structure.\");\n}\n\n// --------------------------------------------------------\n// 2. CLEAN & PARSE JSON\n// --------------------------------------------------------\nif (aiOutput.includes('```')) {\n aiOutput = aiOutput.split('```')[1];\n aiOutput = aiOutput.replace(/^json\\s*/i, '');\n}\n\nlet receiptData = {};\ntry {\n receiptData = JSON.parse(aiOutput.trim());\n} catch (e) {\n receiptData = { \n summary: aiOutput, \n vendor: 'Parse Error', \n amount: 0, \n currency: 'CAD',\n category: 'Miscellaneous' \n }; \n}\n\n// --------------------------------------------------------\n// 3. LOGIC: FOLDERS, RENAMING & CURRENCY\n// --------------------------------------------------------\n\nconst category = receiptData.category || 'Miscellaneous';\nconst targetFolderId = FOLDER_IDS[category] || FOLDER_IDS['Miscellaneous'];\n\n// --- FILE EXTENSION FIX (PDF/JPG) ---\nconst binaryData = $('Get File').first().binary;\nconst binaryKey = Object.keys(binaryData)[0]; \nconst fileExtension = binaryData[binaryKey].fileExtension || 'jpg';\n\nconst date = receiptData.date || new Date().toISOString().split('T')[0];\nconst safeVendor = (receiptData.vendor || 'Unknown').replace(/[^a-zA-Z0-9]/g, ''); \nconst amount = receiptData.amount || 0;\n\n// Use the detected currency, or default to CAD if AI missed it\nconst currency = (receiptData.currency || 'CAD').toUpperCase();\n\n// Filename includes currency now for clarity: \"2025-11-28_GitHub_20_USD.pdf\"\nconst newFileName = `${date}_${safeVendor}_${amount}_${currency}.${fileExtension}`;\n\n// --------------------------------------------------------\n// 4. RETURN DATA\n// --------------------------------------------------------\nreturn {\n json: {\n vendor: receiptData.vendor || 'Unknown',\n date: date,\n amount: parseFloat(amount),\n // Pass the dynamic currency to Sheets\n currency: currency, \n category: category,\n summary: receiptData.summary || '',\n driveFolderId: targetFolderId,\n newFileName: newFileName\n },\n binary: binaryData\n};"
},
"typeVersion": 2
},
{
"id": "ffc80004-d07a-4d8e-8893-f484ea57a232",
"name": "Save to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
736,
-416
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $('Format Data').first().json.date }}",
"Link": "={{ $json.webViewLink }}",
"Amount": "={{ $('Format Data').first().json.amount }}",
"Vendor": "={{ $('Format Data').first().json.vendor }}",
"Category": "={{ $('Format Data').first().json.category }}",
"Currency": "={{ $('Format Data').first().json.currency }}"
},
"schema": [
{
"id": "Date",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Vendor",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Vendor",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Amount",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Amount",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Currency",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Currency",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Category",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Link",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Link",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1zdHPccdCdOzoV6TRQwddHdtZ4P-I91fnANsvQs4cvDU",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1zdHPccdCdOzoV6TRQwddHdtZ4P-I91fnANsvQs4cvDU/edit?usp=drivesdk",
"cachedResultName": "receipt_hub"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "Epz7c0im8OMtFCM4",
"name": "Google Sheets account 2"
}
},
"typeVersion": 4.5
},
{
"id": "416a7789-a767-41ba-a055-3cbec823c3f1",
"name": "Send Confirmation",
"type": "n8n-nodes-base.telegram",
"position": [
960,
-416
],
"webhookId": "a7e3681e-27de-44be-be8d-7c9fce3f8031",
"parameters": {
"text": "=ā
<b>Receipt saved!</b>\n\nš° Amount: ${{ $('Format Data').first().json.amount }} {{ $('Format Data').first().json.currency }}\nšŖ Vendor: {{ $('Format Data').first().json.vendor }}\nš Category: {{ $('Format Data').first().json.category }}\nš
Date: {{ $('Format Data').first().json.date }}\n\nš <a href=\"{{ $('Backup to Drive').first().json.webViewLink }}\">View in Drive</a>",
"chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
"additionalFields": {
"parse_mode": "HTML"
}
},
"credentials": {
"telegramApi": {
"id": "xuFTbOQ62YpwhOpY",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "21949401-5686-4352-9526-c239036a4d4d",
"name": "Analyze an image",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
64,
-576
],
"parameters": {
"text": "Analyze this receipt and return ONLY a raw JSON object (no markdown) with fields: \n- vendor\n- date (YYYY-MM-DD)\n- amount (number)\n- currency: Return the 3-letter ISO code (e.g. 'USD', 'CAD', 'EUR'). If the currency symbol is generic ($) and no address is found, assume 'CAD'.\n- summary\n- category: This MUST be exactly one of the following strings: 'Eating Out', 'Groceries', 'Utilities', 'Transport', 'Shopping', 'Subscriptions', 'Tech & Office', 'Medical', 'Miscellaneous'. If you are unsure, use 'Miscellaneous'.",
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-2.5-flash",
"cachedResultName": "models/gemini-2.5-flash"
},
"options": {},
"resource": "image",
"simplify": false,
"inputType": "binary",
"operation": "analyze"
},
"credentials": {
"googlePalmApi": {
"id": "p84bEFwG3jhgIwIp",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "795539c8-d668-4341-ba38-5c39eeb8ac0d",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
-144,
-416
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "39bc76ff-01b1-4699-8db3-00faa20e5a82",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $input.first().binary.data.mimeType }}",
"rightValue": "image"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "29857232-9d49-48c4-9dbf-2edb6717deff",
"name": "Analyze a file",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
64,
-304
],
"parameters": {
"text": "Analyze this receipt and return ONLY a raw JSON object (no markdown) with fields: \n- vendor\n- date (YYYY-MM-DD)\n- amount (number)\n- currency: Return the 3-letter ISO code (e.g. 'USD', 'CAD', 'EUR'). If the currency symbol is generic ($) and no address is found, assume 'CAD'.\n- summary\n- category: This MUST be exactly one of the following strings: 'Eating Out', 'Groceries', 'Utilities', 'Transport', 'Shopping', 'Subscriptions', 'Tech & Office', 'Medical', 'Miscellaneous'. If you are unsure, use 'Miscellaneous'.",
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-2.5-flash",
"cachedResultName": "models/gemini-2.5-flash"
},
"options": {},
"resource": "document",
"simplify": false,
"inputType": "binary"
},
"credentials": {
"googlePalmApi": {
"id": "p84bEFwG3jhgIwIp",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "4402927e-3d54-4282-8ba7-ea1e84fcfb2c",
"name": "Get File",
"type": "n8n-nodes-base.telegram",
"position": [
-368,
-416
],
"webhookId": "cbe76ba7-fcb6-445a-894a-9c7088f8822f",
"parameters": {
"fileId": "={{ $json.message.photo ? $json.message.photo[$json.message.photo.length - 1].file_id : $json.message.document.file_id }}",
"resource": "file",
"additionalFields": {}
},
"credentials": {
"telegramApi": {
"id": "xuFTbOQ62YpwhOpY",
"name": "Telegram account"
}
},
"typeVersion": 1.2
}
],
"active": true,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "91550b15-bca1-4aec-a1ea-6779c9ef35ed",
"connections": {
"If": {
"main": [
[
{
"node": "Analyze an image",
"type": "main",
"index": 0
}
],
[
{
"node": "Analyze a file",
"type": "main",
"index": 0
}
]
]
},
"Get File": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Format Data": {
"main": [
[
{
"node": "Backup to Drive",
"type": "main",
"index": 0
}
]
]
},
"Analyze a file": {
"main": [
[
{
"node": "Format Data",
"type": "main",
"index": 0
}
]
]
},
"Save to Sheets": {
"main": [
[
{
"node": "Send Confirmation",
"type": "main",
"index": 0
}
]
]
},
"Backup to Drive": {
"main": [
[
{
"node": "Save to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Analyze an image": {
"main": [
[
{
"node": "Format Data",
"type": "main",
"index": 0
}
]
]
},
"Telegram Trigger": {
"main": [
[
{
"node": "Get File",
"type": "main",
"index": 0
}
]
]
}
}
}