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": [
-4592,
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\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": [
-1088,
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": [
-1104,
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": [
-4128,
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": [
-512,
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": [
-704,
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": [
-1376,
272
],
"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": [
-192,
-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": [
144,
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": [
-4400,
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": [
-624,
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": [
-1552,
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": [
-192,
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": [
-192,
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'] }}"
}
]
}
},
"type": "n8n-nodes-base.supabaseTool",
"typeVersion": 1,
"position": [
-928,
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": [
-3872,
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>"
}
}
}
],
"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": "change mimetype",
"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
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "cd854273-c672-48a9-a987-0f1e39e470fe",
"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.
googleDriveOAuth2ApiopenAiApisupabaseApitelegramApi
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; 28 nodes.
Source: https://github.com/endurance71/expenses/blob/4c48135c5b8b9f67c0843b05a48667672d3b28ac/Backup/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.
This template is designed for marketers, content creators, and e-commerce brands who want to automate the creation of professional ad videos at scale. It’s ideal for teams looking to generate consiste
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.
Generate AI viral videos with NanoBanana & VEO3, shared on socials via Blotato. Uses @blotato/n8n-nodes-blotato, googleSheets, lmChatOpenAi, toolThink. Event-driven trigger; 48 nodes.
Create Video Ia. Uses @blotato/n8n-nodes-blotato, googleSheets, lmChatOpenAi, toolThink. Event-driven trigger; 47 nodes.