{
  "name": "AI expense tracker: Telegram voice/photo/text to sheets",
  "nodes": [
    {
      "id": "789a3e35-547d-47ff-9fce-4287f1a51ee0",
      "name": "Google Sheets: Get Rows (Dedup lookup)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        560,
        -64
      ]
    },
    {
      "id": "81ffc0d2-926c-4ac4-ba23-04c81652b99e",
      "name": "IF (Is Duplicate?)",
      "type": "n8n-nodes-base.if",
      "position": [
        736,
        -64
      ]
    },
    {
      "id": "e0461045-0fc1-4a8f-a8f2-81189e44917f",
      "name": "Switch (Voice/Photo/Text)",
      "type": "n8n-nodes-base.switch",
      "position": [
        1104,
        -288
      ]
    },
    {
      "id": "2881f5cc-61ba-4204-acaa-f0cfb7615a1b",
      "name": "Code (Restore Telegram Payload)",
      "type": "n8n-nodes-base.code",
      "position": [
        944,
        -48
      ]
    },
    {
      "id": "6408da1b-ca88-48ad-a351-ea89b565d527",
      "name": "Set (Text Context)",
      "type": "n8n-nodes-base.set",
      "position": [
        1408,
        336
      ]
    },
    {
      "id": "10079662-d912-4409-91e5-09fa347111ce",
      "name": "Google Gemini Chat (Text → JSON)",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        1648,
        336
      ]
    },
    {
      "id": "b0accb8d-9078-4361-bc9b-609e39fc6fd1",
      "name": "Code (Parse Gemini JSON)",
      "type": "n8n-nodes-base.code",
      "position": [
        2496,
        -64
      ]
    },
    {
      "id": "c47bab6f-6c9f-412f-a717-262277657d14",
      "name": "Code (Split expenses to items)",
      "type": "n8n-nodes-base.code",
      "position": [
        2880,
        -80
      ]
    },
    {
      "id": "1cd58e0a-904a-4ac6-88dc-e35b6f3cbfc2",
      "name": "Google Sheets → Append row(s)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3072,
        -80
      ]
    },
    {
      "id": "d57bc019-f640-48d0-9628-4e3c9c1f5d57",
      "name": "IF (Has expenses?)",
      "type": "n8n-nodes-base.if",
      "position": [
        2672,
        -64
      ]
    },
    {
      "id": "ee625a24-d767-4ffe-88ab-0b174e656a3c",
      "name": "Set (Photo Context)",
      "type": "n8n-nodes-base.set",
      "position": [
        1408,
        -64
      ]
    },
    {
      "id": "4da8ecb9-2306-457f-97d2-de9a6200bd5a",
      "name": "Code (Pick Best Photo)",
      "type": "n8n-nodes-base.code",
      "position": [
        1616,
        -64
      ]
    },
    {
      "id": "9318dbfe-62f6-4136-a880-05a6e8b32f52",
      "name": "Code (Normalize Gemini Image Output)",
      "type": "n8n-nodes-base.code",
      "position": [
        2224,
        -64
      ]
    },
    {
      "id": "ea8bb0d4-9ea0-482c-b2d2-3e035f810993",
      "name": "Telegram → Send Error Message and wait for response",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2880,
        128
      ]
    },
    {
      "id": "dd199ba1-0778-49f7-9fb6-a932092fd5f4",
      "name": "Telegram → Send Final Message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2880,
        -272
      ]
    },
    {
      "id": "64d0da46-7a55-460e-914a-f3f89fc53d78",
      "name": "Google Gemini (Analyze Image)",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        2016,
        -64
      ]
    },
    {
      "id": "7af32a67-6212-439d-8189-b27b94a8261f",
      "name": "Set (Voice Context)",
      "type": "n8n-nodes-base.set",
      "position": [
        1408,
        -464
      ]
    },
    {
      "id": "fc1d557e-30b4-4f28-852a-f19af6ddcb76",
      "name": "Telegram → Get Voice File",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1600,
        -464
      ]
    },
    {
      "id": "022846d3-718f-41ad-b6c2-89d990e95629",
      "name": "Telegram → Get Image File",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1808,
        -64
      ]
    },
    {
      "id": "3c664eb1-856f-4b6b-8408-351c5f2653dc",
      "name": "Google Gemini (Analyze Audio)",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        1808,
        -464
      ]
    },
    {
      "id": "5a718e98-34de-407f-ab47-e975d3249ebe",
      "name": "Code (Normalize Gemini Audio Output)",
      "type": "n8n-nodes-base.code",
      "position": [
        2016,
        -464
      ]
    },
    {
      "id": "0f04fa94-5c7b-48ec-9ad3-558a7cb24316",
      "name": "Code (Normalize Gemini Text Output)",
      "type": "n8n-nodes-base.code",
      "position": [
        2016,
        336
      ]
    },
    {
      "id": "e8c72bf3-485e-4a10-bfb2-2c98a966e9bb",
      "name": "Switch (Command Router)",
      "type": "n8n-nodes-base.switch",
      "position": [
        416,
        -272
      ]
    },
    {
      "id": "bed08bc1-2a8d-4e18-90c0-3a9a145169bc",
      "name": "Code (Parse Budget Amount)",
      "type": "n8n-nodes-base.code",
      "position": [
        752,
        -928
      ]
    },
    {
      "id": "bef10b08-afb6-492e-8581-f859ee5084b7",
      "name": "IF (Budget ok?)",
      "type": "n8n-nodes-base.if",
      "position": [
        944,
        -928
      ]
    },
    {
      "id": "d7a70e37-8fab-4c8e-b040-22897d66b88a",
      "name": "Telegram → Budget Error",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1184,
        -736
      ]
    },
    {
      "id": "9551f85a-3dd3-413c-a5b9-155a3065611e",
      "name": "Google Sheets → Append or update row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1168,
        -944
      ]
    },
    {
      "id": "af27370d-b54a-4b35-b31d-679a1889ebe8",
      "name": "Telegram → Budget Updated",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1360,
        -944
      ]
    },
    {
      "id": "179743fe-7387-48b4-a2e0-50a5b9479ad4",
      "name": "GS - Get Daily Report Range",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        4512,
        -80
      ]
    },
    {
      "id": "9852ebb9-18c4-4420-9281-048c03995433",
      "name": "Code - Build Daily Report",
      "type": "n8n-nodes-base.code",
      "position": [
        4704,
        -80
      ]
    },
    {
      "id": "8a1698e4-c3df-4ca8-be1a-c0c595d70058",
      "name": "TG - Send Daily Report",
      "type": "n8n-nodes-base.telegram",
      "position": [
        4912,
        -80
      ]
    },
    {
      "id": "aeaf6ba9-378d-4065-b020-72d63f68bf34",
      "name": "Code (Schedule Report Token)",
      "type": "n8n-nodes-base.code",
      "position": [
        3248,
        -80
      ]
    },
    {
      "id": "d8055f37-ddb8-4b09-97c9-733fd69d5259",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        0,
        -272
      ]
    },
    {
      "id": "a6ee6c21-fa8e-44eb-b200-d42766bcb7ad",
      "name": "Code - Check Latest Token",
      "type": "n8n-nodes-base.code",
      "position": [
        3920,
        -80
      ]
    },
    {
      "id": "f5ab1340-25b8-4573-9225-8aa67e1e434a",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        4096,
        -64
      ]
    },
    {
      "id": "b4027f12-8ec6-4f80-83a5-678229609c19",
      "name": "ReportTokens",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3424,
        -80
      ]
    },
    {
      "id": "9e10464d-c414-4091-a991-089403a7ae39",
      "name": "Data table → Get row(s)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3744,
        -80
      ]
    },
    {
      "id": "fa1ae335-6574-45fc-aa5a-a84fdfd1ba2f",
      "name": "Data table → Delete row(s)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        4304,
        -80
      ]
    },
    {
      "id": "fa2618f9-6914-41ab-a274-0f90dbebb441",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        3584,
        -80
      ]
    },
    {
      "id": "52f3a9a9-5cc4-4c5a-9cf1-fdb42663c306",
      "name": "CONFIG - User Settings",
      "type": "n8n-nodes-base.set",
      "position": [
        208,
        -272
      ]
    },
    {
      "id": "6bb87bf8-368b-4e9a-8ef2-77e0e610d5ae",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -32,
        -1024
      ],
      "parameters": {
        "width": 592,
        "height": 576,
        "content": "# AI Multimodal Expense Tracker\n**Global, Multi-Currency Expense Logger via Telegram (Text, Voice, Photo).**\n\nThis workflow turns Telegram into a frictionless finance assistant using Gemini AI. It sup"
      }
    },
    {
      "id": "6d5e677d-c3f8-4e4d-932b-f03ff87e6b22",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -32,
        -368
      ],
      "parameters": {
        "width": 1280,
        "height": 480,
        "content": "## 1. Input & Routing\nInitializes configuration, handles credentials, and routes inputs to the appropriate processing chain (Command, Voice, Photo, or Text)."
      }
    },
    {
      "id": "4489dbec-b060-4b53-a509-1dd6f053cf02",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        -544
      ],
      "parameters": {
        "width": 1104,
        "height": 1072,
        "content": "## 2. AI Extraction & Normalization\nGemini AI analyzes multimodal inputs to extract structured JSON. Code nodes normalize currency formats and parse the AI response."
      }
    },
    {
      "id": "5836aead-4611-4f97-9066-84e1b4531ce1",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3216,
        -176
      ],
      "parameters": {
        "width": 1856,
        "height": 288,
        "content": "## 5. Smart Debounced Reporting (optional)\nUses n8n Data Table to wait for 30 minutes of inactivity before generating and sending a daily financial summary."
      }
    },
    {
      "id": "7d325593-4132-4125-8f8b-fd167afaecf6",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        704,
        -1024
      ],
      "parameters": {
        "width": 784,
        "height": 432,
        "content": "## 4. Logging & Budget Logic\nSplits multiple items into separate rows, handles \"/add budget\" commands, and writes clean data to Google Sheets."
      }
    },
    {
      "id": "f9de232a-e13f-423d-9907-0f5516b0a9a9",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        -112
      ],
      "parameters": {
        "width": 304,
        "height": 176,
        "content": "### ⚠️ CRITICAL SETUP REQUIRED\n1. **Google Sheet:** You must use the Template provided in the description.\n2. **Data Table (optional):** You must create a `ReportTokens` table in n8n for the reporting"
      }
    },
    {
      "id": "2dc28159-a189-4b28-8982-44cd7558d18b",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2416,
        -384
      ],
      "parameters": {
        "width": 784,
        "height": 688,
        "content": "## 3. Logging & Feedback\nValidates AI output, splits multiple items, logs to Google Sheets, and sends success/error feedback to Telegram."
      }
    }
  ],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Data table → Delete row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Data table → Get row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ReportTokens": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF (Budget ok?)": {
      "main": [
        [
          {
            "node": "Google Sheets → Append or update row in sheet",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Telegram → Budget Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "CONFIG - User Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF (Has expenses?)": {
      "main": [
        [
          {
            "node": "Code (Split expenses to items)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Telegram → Send Final Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Telegram → Send Error Message and wait for response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF (Is Duplicate?)": {
      "main": [
        [],
        [
          {
            "node": "Code (Restore Telegram Payload)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set (Text Context)": {
      "main": [
        [
          {
            "node": "Google Gemini Chat (Text → JSON)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set (Photo Context)": {
      "main": [
        [
          {
            "node": "Code (Pick Best Photo)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set (Voice Context)": {
      "main": [
        [
          {
            "node": "Telegram → Get Voice File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CONFIG - User Settings": {
      "main": [
        [
          {
            "node": "Switch (Command Router)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Pick Best Photo)": {
      "main": [
        [
          {
            "node": "Telegram → Get Image File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch (Command Router)": {
      "main": [
        [
          {
            "node": "Code (Parse Budget Amount)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Google Sheets: Get Rows (Dedup lookup)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Parse Gemini JSON)": {
      "main": [
        [
          {
            "node": "IF (Has expenses?)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Build Daily Report": {
      "main": [
        [
          {
            "node": "TG - Send Daily Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Check Latest Token": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data table → Get row(s)": {
      "main": [
        [
          {
            "node": "Code - Check Latest Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch (Voice/Photo/Text)": {
      "main": [
        [
          {
            "node": "Set (Voice Context)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set (Photo Context)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set (Text Context)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Parse Budget Amount)": {
      "main": [
        [
          {
            "node": "IF (Budget ok?)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GS - Get Daily Report Range": {
      "main": [
        [
          {
            "node": "Code - Build Daily Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram → Get Image File": {
      "main": [
        [
          {
            "node": "Google Gemini (Analyze Image)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram → Get Voice File": {
      "main": [
        [
          {
            "node": "Google Gemini (Analyze Audio)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Schedule Report Token)": {
      "main": [
        [
          {
            "node": "ReportTokens",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data table → Delete row(s)": {
      "main": [
        [
          {
            "node": "GS - Get Daily Report Range",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini (Analyze Audio)": {
      "main": [
        [
          {
            "node": "Code (Normalize Gemini Audio Output)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini (Analyze Image)": {
      "main": [
        [
          {
            "node": "Code (Normalize Gemini Image Output)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Split expenses to items)": {
      "main": [
        [
          {
            "node": "Google Sheets → Append row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Restore Telegram Payload)": {
      "main": [
        [
          {
            "node": "Switch (Voice/Photo/Text)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets → Append row(s)": {
      "main": [
        [
          {
            "node": "Code (Schedule Report Token)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat (Text → JSON)": {
      "main": [
        [
          {
            "node": "Code (Normalize Gemini Text Output)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Normalize Gemini Text Output)": {
      "main": [
        [
          {
            "node": "Code (Parse Gemini JSON)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Normalize Gemini Audio Output)": {
      "main": [
        [
          {
            "node": "Code (Parse Gemini JSON)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Normalize Gemini Image Output)": {
      "main": [
        [
          {
            "node": "Code (Parse Gemini JSON)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets: Get Rows (Dedup lookup)": {
      "main": [
        [
          {
            "node": "IF (Is Duplicate?)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets → Append or update row in sheet": {
      "main": [
        [
          {
            "node": "Telegram → Budget Updated",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}