This workflow follows the Agent → Google Drive 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 →
{
"name": "optimize-added-expenses",
"nodes": [
{
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.1,
"position": [
-5200,
112
],
"id": "bcb6389d-2f59-4aaa-9483-c09705224700",
"name": "Telegram Trigger",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"promptType": "define",
"text": "Zarejestrowany wydatek: \"{{ $json.text }}\" lub \"{{ $json.content }}\" lub {{ $binary }}",
"options": {
"systemMessage": "=Instrukcje Systemowe dla Agenta\n\nJeste\u015b asystentem do zarz\u0105dzania wydatkami. Twoim zadaniem jest przeanalizowanie wiadomo\u015bci i ewentualnych za\u0142\u0105cznik\u00f3w (paragon\u00f3w, faktur) oraz dodanie wydatku do arkusza Google Sheets. Je\u015bli to mo\u017cliwe, prze\u015blij te\u017c za\u0142\u0105cznik do Google Drive. Dzia\u0142asz w jednej iteracji.\n\n\u2e3b\n\nWyodr\u0119bnij nast\u0119puj\u0105ce dane:\n\t1.\tData \u2013 w formacie RRRR-MM-DD, strefa Europe/Warsaw.\n\t\u2022\tWzgl\u0119dne daty (np. \u201cwczoraj\u201d) przelicz wzgl\u0119dem {{ $now.setZone('Europe/Warsaw') }}.\n\t\u2022\tJe\u015bli brak \u2192 null.\n\t2.\tMiejsce/Przedmiot \u2013 gdzie/na co wydano.\n\t\u2022\tJe\u015bli brak \u2192 null.\n\t3.\tKwota \u2013 liczba dziesi\u0119tna z kropk\u0105 jako separatorem, bez symbolu waluty.\n\t\u2022\tPrzyk\u0142ad: \u201c49,99 z\u0142\u201d \u2192 49.99.\n\t\u2022\tJe\u015bli brak \u2192 null.\n\t4.\tKategoria \u2013 Prywatne, MT HUB, FHU Gabriela.\nZasady klasyfikacji:\n\t\u2022\tJe\u015bli dokument zawiera NIP 8393229228 \u2192 MT HUB\n\t\u2022\tJe\u015bli dokument zawiera NIP 8393251475 \u2192 FHU\n\t\u2022\tW pozosta\u0142ych przypadkach, je\u015bli nie ma pewno\u015bci lub brak danych \u2192 prywatny\n\t\t5.\tSubKategoria:\n\u2022 Je\u015bli kategoria to Prywatne, przypisz szczeg\u00f3\u0142ow\u0105 subkategori\u0119 wydatku, np. \u017bywno\u015b\u0107, Odzie\u017c, Transport, Rozrywka, Zdrowie, Hobby, RTV/AGD, inne.\n\u2022 Je\u015bli kategoria to MT HUB \u2192 subkategoria: MT_HUB\n\u2022 Je\u015bli kategoria to FHU Gabriela \u2192 subkategoria: FHU\n\u2e3b\n\ud83d\udd20 UWAGA: Zwracaj szczeg\u00f3ln\u0105 uwag\u0119 na wielko\u015b\u0107 liter. Kategoria i subkategoria musz\u0105 dok\u0142adnie odpowiada\u0107 wzorcowi (case-sensitive). Nie przekszta\u0142caj ani nie normalizuj wielko\u015bci liter.\nPrzyk\u0142adowo:\n\t\u2022\tPrywatne, Firmowe - prawid\u0142owe\n \u2022\tMT_HUB, FHU \u2013 prawid\u0142owe\n\t\u2022\tMt Hub, mt hub, MTHub, prywatne, firmowe \u2013 nieprawid\u0142owe\n\nTo samo dotyczy kategorii Prywatne i FHU.\n\nNarz\u0119dzia dodaj_wydatek_do_bazy (u\u017cyj tylko, je\u015bli wszystkie dane s\u0105 dost\u0119pne)\nwydatek_folder_id:\n\t\u2022\tJe\u015bli Prywatne \u2192 {{ $('Get a row').item.json.prywatne_id }}\n\t\u2022\tJe\u015bli MT HUB \u2192 {{ $('Get a row').item.json.mt_hub_id }}\n\t\u2022\tJe\u015bli FHU \u2192 {{ $('Get a row').item.json.fhu_id }}\n\n\n\nDla receipt_image_url: {{ $json.secure_url }}\n\u2e3b\n\nFormat odpowiedzi\n\nSukces\n\n\u2705 Wydatek dodany:\n\u2022 \ud83d\udcc5 Data:\n\u2022 \ud83c\udff7\ufe0f Miejsce/Przedmiot:\n\u2022 \ud83d\udcb8 Kwota: (z\u0142, przecinek jako separator)\n\u2022 \ud83d\udd16 Kategoria:\n\n\nTylko za\u0142\u0105cznik\n\n\ud83d\udcf8 Za\u0142\u0105cznik przes\u0142any do Google Drive. Brak danych do dodania wydatku.\n\nProblem\n\n\ud83d\udd34 Problem z dodaniem wydatku. Brakuje danych o:",
"passthroughBinaryImages": true
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.7,
"position": [
-880,
80
],
"id": "7a0a7ad8-ccdb-4ef9-9e70-32fe18db4ea6",
"name": "AI Agent",
"onError": "continueErrorOutput"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"leftValue": "={{ $json.message.text }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"id": "7c91a664-3634-41e0-a1c8-2e195b9b509e"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Text"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "b6f5f508-c214-4f1a-925b-16dca2a94a74",
"leftValue": "={{ $('Telegram Trigger').item.json.message.voice.file_id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "=Voice"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "22c624df-56e4-4bc2-bf8a-6d41614556d9",
"leftValue": "={{ $('Telegram Trigger').item.json.message.photo.last().file_id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Image"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
-2320,
112
],
"id": "de35a0f4-fe98-40b3-bb42-661c114a61cd",
"name": "Switch"
},
{
"parameters": {
"resource": "file",
"fileId": "={{ $('Telegram Trigger').item.json.message.photo.last().file_id }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-2112,
272
],
"id": "4bb7bb22-5a7b-4408-8b5b-71d036627642",
"name": "Get a file",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
-896,
304
],
"id": "9292110e-1a01-4f15-b32b-508c242450c5",
"name": "OpenAI Chat Model",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const inputItems = $input.all(); // Pobierz wszystkie elementy z poprzedniego w\u0119z\u0142a\n\nconst monthTranslations = {\n \"January\": \"Stycze\u0144\",\n \"February\": \"Luty\",\n \"March\": \"Marzec\",\n \"April\": \"Kwiecie\u0144\",\n \"May\": \"Maj\",\n \"June\": \"Czerwiec\",\n \"July\": \"Lipiec\",\n \"August\": \"Sierpie\u0144\",\n \"September\": \"Wrzesie\u0144\",\n \"October\": \"Pa\u017adziernik\",\n \"November\": \"Listopad\",\n \"December\": \"Grudzie\u0144\"\n};\n\nconst monthNumbers = {\n \"January\": \"01\",\n \"February\": \"02\",\n \"March\": \"03\",\n \"April\": \"04\",\n \"May\": \"05\",\n \"June\": \"06\",\n \"July\": \"07\",\n \"August\": \"08\",\n \"September\": \"09\",\n \"October\": \"10\",\n \"November\": \"11\",\n \"December\": \"12\"\n};\n\nconst WARSAW_TIMEZONE = 'Europe/Warsaw';\n\nconst outputData = inputItems.map(item => {\n // Odczytaj znacznik czasu z pola 'currentTimestamp' z poprzedniego w\u0119z\u0142a\n // Je\u015bli z jakiego\u015b powodu go nie ma, u\u017cyj aktualnej daty\n const currentMoment = item.json.currentTimestamp ? new Date(item.json.currentTimestamp) : new Date();\n\n // Utw\u00f3rz obiekt Intl.DateTimeFormat dla strefy czasowej Warszawy\n const formatter = new Intl.DateTimeFormat('en-US', {\n year: 'numeric',\n month: 'long',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false, // U\u017cyj formatu 24-godzinnego dla \u0142atwiejszej ekstrakcji\n timeZone: WARSAW_TIMEZONE\n });\n\n // Sformatuj dat\u0119 i czas w strefie czasowej Warszawy\n const parts = formatter.formatToParts(currentMoment);\n const getPart = (type) => parts.find(part => part.type === type)?.value;\n\n const year = getPart('year');\n const englishMonth = getPart('month'); // Nazwa miesi\u0105ca w j\u0119zyku angielskim\n const dayOfMonth = getPart('day');\n const hour = getPart('hour');\n const minute = getPart('minute');\n const second = getPart('second');\n\n // T\u0142umaczenie miesi\u0105ca na polski\n const polishMonth = monthTranslations[englishMonth] || englishMonth;\n const monthNumber = monthNumbers[englishMonth] || \"\";\n\n // Aby uzyska\u0107 \"Day of week\" w strefie czasowej Warszawy:\n const dayOfWeekFormatter = new Intl.DateTimeFormat('en-US', { weekday: 'long', timeZone: WARSAW_TIMEZONE });\n const dayOfWeek = dayOfWeekFormatter.format(currentMoment);\n\n // Aby uzyska\u0107 \"Readable date\" i \"Readable time\" z uwzgl\u0119dnieniem AM/PM (je\u015bli chcesz)\n // w strefie czasowej Warszawy:\n const readableDateFormatter = new Intl.DateTimeFormat('en-US', {\n month: 'long', day: 'numeric', year: 'numeric',\n hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: true,\n timeZone: WARSAW_TIMEZONE\n });\n const readableTimeFormatter = new Intl.DateTimeFormat('en-US', {\n hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: true,\n timeZone: WARSAW_TIMEZONE\n });\n\n // NOWA FUNKCJONALNO\u015a\u0106: Formatowanie daty w formacie 2025-07-23 10:43:00+00\n // Uwzgl\u0119dniaj\u0105c stref\u0119 czasow\u0105 Polski (CEST/CET)\n const formatPolishDateTime = () => {\n const date = new Date(currentMoment);\n \n // Formatowanie w strefie czasowej Polski\n const polishFormatter = new Intl.DateTimeFormat('en-CA', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n timeZone: WARSAW_TIMEZONE\n });\n \n const polishParts = polishFormatter.formatToParts(date);\n const getPolishPart = (type) => polishParts.find(part => part.type === type)?.value;\n \n const polishYear = getPolishPart('year');\n const polishMonth = getPolishPart('month');\n const polishDay = getPolishPart('day');\n const polishHour = getPolishPart('hour');\n const polishMinute = getPolishPart('minute');\n const polishSecond = getPolishPart('second');\n \n // Okre\u015bl offset dla Polski (CEST +02:00 latem, CET +01:00 zim\u0105)\n const isDST = (date) => {\n const jan = new Date(date.getFullYear(), 0, 1);\n const jul = new Date(date.getFullYear(), 6, 1);\n return date.getTimezoneOffset() < Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());\n };\n \n // Sprawd\u017a czy to czas letni czy zimowy w Polsce\n const tempDate = new Date();\n tempDate.setFullYear(parseInt(polishYear), parseInt(polishMonth) - 1, parseInt(polishDay));\n tempDate.setHours(parseInt(polishHour), parseInt(polishMinute), parseInt(polishSecond));\n \n // Polska u\u017cywa CEST (+02) latem i CET (+01) zim\u0105\n const offset = isDST(tempDate) ? '+02' : '+01';\n \n return `${polishYear}-${polishMonth}-${polishDay} ${polishHour}:${polishMinute}:${polishSecond}${offset}`;\n };\n\n return {\n json: {\n // timestamp w ISO zawsze b\u0119dzie w UTC, ale reprezentuje czas w Warszawie\n \"timestamp\": currentMoment.toISOString(),\n \"Readable date\": readableDateFormatter.format(currentMoment),\n \"Readable time\": readableTimeFormatter.format(currentMoment),\n \"Day of week\": dayOfWeek,\n \"Year\": year,\n \"Month\": polishMonth,\n \"Day of month\": dayOfMonth,\n \"Hour\": hour,\n \"Minute\": minute,\n \"Second\": second,\n \"Timezone\": WARSAW_TIMEZONE + \" (UTC+02:00)\", // Jawnie ustawiamy stref\u0119 czasow\u0105\n \"Month Number\": monthNumber,\n \"aktualna data i godzina\": formatPolishDateTime() // NOWE POLE\n }\n };\n});\n\nreturn outputData;\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-4736,
128
],
"id": "047a41aa-2012-4901-8d4c-cf53e70fc6da",
"name": "Ekstrakcja aktualnej daty"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"leftValue": "={{ $json.categoryType }}",
"rightValue": "prywatny",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "155c39c2-bd94-4403-987b-16d9c8f02682"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "prywatny"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "836da5c1-7eb3-4d6e-bf49-61255c642190",
"leftValue": "={{ $json.categoryType }}",
"rightValue": "MT HUB",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "MT HUB"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "a1920598-dd5b-435f-9472-7af7ec89fbb3",
"leftValue": "={{ $json.categoryType }}",
"rightValue": "FHU",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "FHU"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
-304,
80
],
"id": "3cc217eb-8f93-4a33-8fbc-2e6816a9eaaf",
"name": "Switch1"
},
{
"parameters": {
"jsCode": "const inputText = $input.item.json.output; // TYLKO JEDNA DEKLARACJA\n\nlet category = 'nieznana';\n\n// Szukamy konkretnie: MT HUB, FHU lub prywatny\nconst regex = /Kategoria: (MT HUB|FHU|prywatn(?:y|e)?)/i;\nconst match = inputText.match(regex);\n\nif (match && match[1]) {\n const kat = match[1].toLowerCase();\n if (kat.includes('mt hub')) {\n category = 'MT HUB';\n } else if (kat.includes('fhu')) {\n category = 'FHU';\n } else if (kat.startsWith('prywatn')) {\n category = 'prywatny';\n }\n}\n\n$input.item.json.categoryType = category;\n\nreturn $input.item;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-496,
80
],
"id": "04fba468-f46f-48ea-8cd7-8d5006a1a8c8",
"name": "Code1"
},
{
"parameters": {
"name": "={{ $('Get a file').item.json.result.file_path }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $('Get a row').item.json.month_id }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
-1648,
272
],
"id": "ada3c670-cb3a-47f1-af73-db02b6056a5e",
"name": "Upload file",
"alwaysOutputData": true,
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"operation": "download",
"fileId": {
"__rl": true,
"value": "={{ $json.id }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
-1312,
256
],
"id": "4f302a50-b7d4-4cc0-ab3d-160aae8937c8",
"name": "Download file",
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"operation": "move",
"fileId": {
"__rl": true,
"value": "={{ $('Upload file').item.json.id }}",
"mode": "id"
},
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $('Get a row').item.json.prywatne_id }}",
"mode": "id"
}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
16,
-112
],
"id": "326e2032-6246-445e-aedf-2630a7746f1e",
"name": "Move prywatny",
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "={{ $('AI Agent').item.json.output }}",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
352,
48
],
"id": "b5f13e79-e3a2-4c57-ad7b-854d10dfa39a",
"name": "Send a text message1",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "Zdj\u0119cie zosta\u0142o przes\u0142ane. Trwa analiza. ",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-5008,
112
],
"id": "a2b3edd1-23be-4da8-81c1-c6f77f97897b",
"name": "Send a text message",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "\u274c Wyst\u0105pi\u0142 b\u0142\u0105d autoryzacji Agent AI. Wykonaj autoryzacj\u0119 w n8n. \u274c ",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-416,
464
],
"id": "ba8fc34f-c302-4143-8fde-36bb480d8318",
"name": "Error autoryzacja agenta Ai",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "\u274c Wyst\u0105pi\u0142 problem z przes\u0142aniem pliku na google drive. Sprawd\u017a automatyzacj\u0119 w n8n. \u274c ",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-1344,
512
],
"id": "d30431f4-a8ea-4937-836f-9981eacbdf4f",
"name": "Error upload file",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "move",
"fileId": {
"__rl": true,
"value": "={{ $('Download file').item.json.id }}",
"mode": "id"
},
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $('Get a row').item.json.mt_hub_id }}",
"mode": "id"
}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
16,
80
],
"id": "ad682668-a9ce-48f8-8fa9-fe8c7376aee6",
"name": "Move MTHUB",
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"operation": "move",
"fileId": {
"__rl": true,
"value": "={{ $('Download file').item.json.id }}",
"mode": "id"
},
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $('Get a row').item.json.fhu_id}}",
"mode": "id"
}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
16,
288
],
"id": "b705bc4d-0395-4989-a510-f9a572994464",
"name": "Move FHU",
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"operation": "get",
"tableId": "folder_registry",
"filters": {
"conditions": [
{
"keyName": "month",
"keyValue": "={{ $json.invoiceMonth}}"
},
{
"keyName": "year",
"keyValue": "={{ $json.invoiceYear }}"
}
]
}
},
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
-2560,
128
],
"id": "2d055399-558c-46a3-bb91-86d7d1281e35",
"name": "Get a row",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "Wykryto tekst. Wrzu\u0107 zdj\u0119cie.",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-2016,
-64
],
"id": "0a2de4e0-58dc-4a78-8e9e-358520393305",
"name": "Send a text message2",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "Wykryto d\u017awi\u0119k. Wrzu\u0107 zdj\u0119cie.",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-2016,
112
],
"id": "58021996-04b5-4dc3-90b0-a622ff993ab1",
"name": "Send a text message3",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"tableId": "detailed_expenses",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "description",
"fieldValue": "={{ $fromAI('Miejsce-Przedmiot', ``, 'string') }}"
},
{
"fieldId": "expense_date",
"fieldValue": "={{ $fromAI('Data', ``, 'string') }}"
},
{
"fieldId": "amount",
"fieldValue": "={{ $fromAI('Kwota', ``, 'string') }}"
},
{
"fieldId": "category",
"fieldValue": "={{ $fromAI('Kategoria', ``, 'string') }}"
},
{
"fieldId": "folder_id",
"fieldValue": "={{ $fromAI('wydatek_folder_id', ``, 'string') }}"
},
{
"fieldId": "subcategory",
"fieldValue": "={{ $fromAI('SubKategoria', ``, 'string') }}"
},
{
"fieldId": "created_at",
"fieldValue": "={{ $('Ekstrakcja aktualnej daty').item.json['aktualna data i godzina'] }}"
},
{
"fieldId": "receipt_image_url",
"fieldValue": "={{ $('Upload to Cloudinary').item.json.secure_url }}"
}
]
}
},
"type": "n8n-nodes-base.supabaseTool",
"typeVersion": 1,
"position": [
-720,
512
],
"id": "70920b94-8ba7-4eea-83b5-06ea5ad0040e",
"name": "Dodaj wydatek do bazy",
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "file",
"fileId": "={{ $('Telegram Trigger').item.json.message.photo.last().file_id }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-4480,
128
],
"id": "48d4d2cd-c2db-43d7-b324-fb0bc043612f",
"name": "Get a file1",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "image",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "gpt-4o",
"mode": "list",
"cachedResultName": "GPT-4O"
},
"text": "=Twoje zadanie:\n\n1. Przeanalizuj za\u0142\u0105czone zdj\u0119cie lub skan faktury, rachunku lub paragonu.\n2. Znajd\u017a **rzeczywist\u0105 dat\u0119 wystawienia dokumentu** \u2013 tylko t\u0119, kt\u00f3ra jest widoczna w tre\u015bci dokumentu.\n3. Nie zgaduj \u2013 nie u\u017cywaj dzisiejszej daty ani roku domy\u015blnego. Je\u015bli nie masz pewno\u015bci, zwr\u00f3\u0107 `null`.\n\n\ud83d\udd01 Zwr\u00f3\u0107 wynik jako **czysta tablica JSON**, dok\u0142adnie w tym formacie:\n[\n {\n \"invoiceDay\": \"DD\",\n \"invoiceMonth\": \"MM\",\n \"invoiceYear\": \"YYYY\"\n }\n]\n\n\ud83d\udccc Je\u015bli dokument nie zawiera jednoznacznej daty, zwr\u00f3\u0107:\n[\n {\n \"invoiceDay\": null,\n \"invoiceMonth\": null,\n \"invoiceYear\": null\n }\n]\n\n\ud83e\udde0 Aktualny data i godzina to {{ $('Ekstrakcja aktualnej daty').item.json['aktualna data i godzina'] }} \u2013 nie zak\u0142adaj automatycznie 2023 ani 2024. Upewnij si\u0119, \u017ce data pochodzi z dokumentu, a nie z domys\u0142u.\n\n\u2757Odpowied\u017a ma zawiera\u0107 **tylko czysty JSON**, bez znacznik\u00f3w kodu (np. ```json), komentarzy ani dodatkowego opisu.",
"inputType": "base64",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
-3456,
128
],
"id": "ea86b0c9-a298-4b29-9f8c-427ce5e047a4",
"name": "Analyze image",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "for (const item of items) {\n // Sprawdzamy, czy element ma dane binarne i czy te dane istniej\u0105\n if (item.binary && item.binary.data) {\n const binaryData = item.binary.data; // Odwo\u0142anie do obiektu z danymi binarnymi\n\n const ext = binaryData.fileExtension?.toLowerCase(); // Pobieramy rozszerzenie pliku\n let newMimeType = binaryData.mimeType; // Domy\u015blnie zachowujemy istniej\u0105cy mimeType\n\n // Logika zmiany mimeType na podstawie rozszerzenia\n if (ext === 'jpg' || ext === 'jpeg') {\n newMimeType = 'image/jpeg';\n } else if (ext === 'png') {\n newMimeType = 'image/png';\n } else if (ext === 'webp') {\n newMimeType = 'image/webp';\n }\n\n // WA\u017bNA ZMIANA: Zapisujemy now\u0105 warto\u015b\u0107 z powrotem do obiektu\n binaryData.mimeType = newMimeType;\n }\n}\nreturn items;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-3648,
128
],
"id": "57428f89-fbb5-4423-9c41-faa7c0858de1",
"name": "change mimetype",
"alwaysOutputData": true,
"retryOnFail": true
},
{
"parameters": {
"jsCode": "for (const item of items) {\n // Sprawdzamy, czy element ma dane binarne i czy te dane istniej\u0105\n if (item.binary && item.binary.data) {\n const binaryData = item.binary.data; // Odwo\u0142anie do obiektu z danymi binarnymi\n\n const ext = binaryData.fileExtension?.toLowerCase(); // Pobieramy rozszerzenie pliku\n let newMimeType = binaryData.mimeType; // Domy\u015blnie zachowujemy istniej\u0105cy mimeType\n\n // Logika zmiany mimeType na podstawie rozszerzenia\n if (ext === 'jpg' || ext === 'jpeg') {\n newMimeType = 'image/jpeg';\n } else if (ext === 'png') {\n newMimeType = 'image/png';\n } else if (ext === 'webp') {\n newMimeType = 'image/webp';\n }\n\n // WA\u017bNA ZMIANA: Zapisujemy now\u0105 warto\u015b\u0107 z powrotem do obiektu\n binaryData.mimeType = newMimeType;\n }\n}\nreturn items;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1872,
272
],
"id": "4597f2cb-005a-4a77-9161-d31fc51f61e1",
"name": "change mimetype1",
"alwaysOutputData": true,
"retryOnFail": true
},
{
"parameters": {
"jsCode": "const raw = $json[\"content\"];\n\ntry {\n // Parsujemy string JSON do tablicy obiekt\u00f3w\n const parsedArray = JSON.parse(raw);\n\n const data = parsedArray[0];\n\n return [\n {\n json: {\n invoiceDay: parseInt(data.invoiceDay, 10),\n invoiceMonth: parseInt(data.invoiceMonth, 10),\n invoiceYear: parseInt(data.invoiceYear, 10)\n }\n }\n ];\n} catch (error) {\n return [\n {\n json: {\n invoiceDay: null,\n invoiceMonth: null,\n invoiceYear: null,\n error: \"Nieprawid\u0142owy format JSON w polu content\"\n }\n }\n ];\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-3248,
128
],
"id": "00dac3f1-bd4a-4aa4-8cd4-441f1b88f06d",
"name": "Code"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "cebfda1b-23fc-4d9d-96ca-64fe9b992e3d",
"leftValue": "={{ $json.invoiceDay }}",
"rightValue": "null",
"operator": {
"type": "string",
"operation": "contains"
}
},
{
"id": "eb260322-ccc3-4bab-a8c6-821273468bae",
"leftValue": "={{ $json.invoiceMonth }}",
"rightValue": "null",
"operator": {
"type": "string",
"operation": "contains"
}
},
{
"id": "3f8e72be-95cb-4cc8-b0e0-37ad266ea19e",
"leftValue": "={{ $json.invoiceYear }}",
"rightValue": "null",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "or"
},
"looseTypeValidation": true,
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-3040,
128
],
"id": "b93fff91-65f0-401b-a269-de1e67456659",
"name": "If"
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "\u274c Wyst\u0105pi\u0142 problem z odczytaniem daty rachunku. \u274c",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-2784,
48
],
"id": "97f2a155-6e5a-4149-b24b-a3a0a7f3fd77",
"name": "error date",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://api.cloudinary.com/v1_1/ds597bwxi/image/upload",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"parameterType": "formBinaryData",
"name": "file",
"inputDataFieldName": "data"
},
{
"name": "upload_preset",
"value": "expense_receipts"
},
{
"name": "folder",
"value": "receipts"
},
{
"name": "tags",
"value": "expense,receipt,telegram"
},
{
"name": "public_id",
"value": "={{ 'receipt_' + $json.result.file_id.substring(0, 10) }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-4192,
128
],
"id": "e4e0fc81-8ce0-409f-9023-a0ab78b68c61",
"name": "Upload to Cloudinary",
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "file",
"fileId": "={{ $('Telegram Trigger').item.json.message.photo.last().file_id }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-4016,
128
],
"id": "7b5fc949-555c-43b0-85e2-da5cb44094c5",
"name": "Get a file2",
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Telegram Trigger": {
"main": [
[
{
"node": "Send a text message",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Code1",
"type": "main",
"index": 0
}
],
[
{
"node": "Error autoryzacja agenta Ai",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Send a text message2",
"type": "main",
"index": 0
}
],
[
{
"node": "Send a text message3",
"type": "main",
"index": 0
}
],
[
{
"node": "Get a file",
"type": "main",
"index": 0
}
]
]
},
"Get a file": {
"main": [
[
{
"node": "change mimetype1",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Ekstrakcja aktualnej daty": {
"main": [
[
{
"node": "Get a file1",
"type": "main",
"index": 0
}
]
]
},
"Switch1": {
"main": [
[
{
"node": "Move prywatny",
"type": "main",
"index": 0
}
],
[
{
"node": "Move MTHUB",
"type": "main",
"index": 0
}
],
[
{
"node": "Move FHU",
"type": "main",
"index": 0
}
]
]
},
"Code1": {
"main": [
[
{
"node": "Switch1",
"type": "main",
"index": 0
}
]
]
},
"Upload file": {
"main": [
[
{
"node": "Download file",
"type": "main",
"index": 0
}
],
[
{
"node": "Error upload file",
"type": "main",
"index": 0
}
]
]
},
"Download file": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Move prywatny": {
"main": [
[
{
"node": "Send a text message1",
"type": "main",
"index": 0
}
]
]
},
"Send a text message": {
"main": [
[
{
"node": "Ekstrakcja aktualnej daty",
"type": "main",
"index": 0
}
]
]
},
"Move MTHUB": {
"main": [
[
{
"node": "Send a text message1",
"type": "main",
"index": 0
}
]
]
},
"Move FHU": {
"main": [
[
{
"node": "Send a text message1",
"type": "main",
"index": 0
}
]
]
},
"Get a row": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"Dodaj wydatek do bazy": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get a file1": {
"main": [
[
{
"node": "Upload to Cloudinary",
"type": "main",
"index": 0
}
]
]
},
"change mimetype": {
"main": [
[
{
"node": "Analyze image",
"type": "main",
"index": 0
}
]
]
},
"change mimetype1": {
"main": [
[
{
"node": "Upload file",
"type": "main",
"index": 0
}
]
]
},
"Analyze image": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"If": {
"main": [
[
{
"node": "error date",
"type": "main",
"index": 0
}
],
[
{
"node": "Get a row",
"type": "main",
"index": 0
}
]
]
},
"Upload to Cloudinary": {
"main": [
[
{
"node": "Get a file2",
"type": "main",
"index": 0
}
]
]
},
"Get a file2": {
"main": [
[
{
"node": "change mimetype",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "0443c4e7-7248-4aed-b8f9-c4f71ada0c11",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "W0tNDxvVikyoDVxM",
"tags": []
}
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.
googleDriveOAuth2ApihttpBasicAuthopenAiApisupabaseApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
optimize-added-expenses. Uses telegramTrigger, agent, telegram, lmChatOpenAi. Event-driven trigger; 30 nodes.
Source: https://github.com/endurance71/expenses/blob/4c48135c5b8b9f67c0843b05a48667672d3b28ac/n8n/optimize-added-expenses.json — 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.
Generate AI viral videos with NanoBanana & VEO3, shared on socials via Blotato 2. Uses @blotato/n8n-nodes-blotato, googleSheets, lmChatOpenAi, toolThink. Event-driven trigger; 94 nodes.
Arvifund - Supabase (Fixed v6). Uses httpRequest, telegram, supabase, telegramTrigger. Event-driven trigger; 92 nodes.
Arvifund - Supabase (Fixed v5). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 91 nodes.
Arvifund - Supabase (Fixed v2). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.
Arvifund - Supabase (Fixed v4). Uses httpRequest, telegram, googleSheets, telegramTrigger. Event-driven trigger; 90 nodes.