AutomationFlowsData & Sheets › AI Money Tracker Chatbot

AI Money Tracker Chatbot

AI Money Tracker Chatbot. Uses telegramTrigger, postgres, googleSheets, telegram. Event-driven trigger; 24 nodes.

Event trigger★★★★☆ complexity24 nodesTelegram TriggerPostgresGoogle SheetsTelegramHTTP Request
Data & Sheets Trigger: Event Nodes: 24 Complexity: ★★★★☆ Added:

This workflow follows the Google Sheets → HTTP Request 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 →

Download .json
{
  "name": "AI Money Tracker Chatbot",
  "nodes": [
    {
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "id": "node-tg-trigger",
      "name": "Telegram Message Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.1,
      "position": [
        240,
        400
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const msg = $input.first().json;\nconst text = (msg.message?.text || '').trim();\nconst chatId = msg.message?.chat?.id || '';\nconst from = msg.message?.from?.first_name || 'User';\nconst isCommand = text.startsWith('/');\nlet command = '';\nlet args = [];\nif (isCommand) {\n  const parts = text.split(' ');\n  command = parts[0].toLowerCase();\n  args = parts.slice(1);\n}\nreturn [{ json: { userMessage: text, chatId, from, isCommand, command, args } }];"
      },
      "id": "node-extract-msg",
      "name": "Extract Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "is-command-check",
              "leftValue": "={{ $json.userMessage.charAt(0) }}",
              "rightValue": "/",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        }
      },
      "id": "node-if-command",
      "name": "Is Command?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        680,
        400
      ]
    },
    {
      "parameters": {
        "jsCode": "const { command, args, chatId } = $input.first().json;\n\nlet sql = '';\nlet replyType = 'text';\nlet staticReply = '';\nlet responseTemplate = '';\n\nif (command === '/saldo') {\n  sql = \"SELECT saldo_sekarang, updated_at FROM saldo_rekening ORDER BY id DESC LIMIT 1\";\n  responseTemplate = 'SALDO';\n  replyType = 'query';\n\n} else if (command === '/pengeluaran') {\n  sql = \"SELECT TO_CHAR(bulan, 'Month YYYY') AS bulan_label, total_pengeluaran, total_pemasukan, target_saving, sisa_budget, pct_saving_risk, CASE WHEN pct_saving_risk >= 100 THEN 'DANGER \ud83d\udd34' WHEN pct_saving_risk >= 80 THEN 'WARNING \ud83d\udfe1' ELSE 'AMAN \ud83d\udfe2' END AS status FROM monthly_budget WHERE bulan = DATE_TRUNC('month', NOW())::DATE\";\n  responseTemplate = 'PENGELUARAN';\n  replyType = 'query';\n\n} else if (command === '/export') {\n  sql = \"SELECT TO_CHAR(tanggal_waktu AT TIME ZONE 'Asia/Jakarta', 'DD Mon YYYY HH24:MI') AS waktu, tipe, merchant_deskripsi AS merchant, kategori_otomatis AS kategori, nominal, saldo_akhir FROM jago_transactions WHERE DATE_TRUNC('month', tanggal_waktu) = DATE_TRUNC('month', NOW()) ORDER BY tanggal_waktu DESC LIMIT 200\";\n  responseTemplate = 'EXPORT';\n  replyType = 'export';\n\n} else if (command === '/budget') {\n  const newBudget = parseFloat(args[0]);\n  if (!newBudget || isNaN(newBudget)) {\n    staticReply = '\u274c Format salah. Gunakan: /budget <nominal>\\nContoh: /budget 4000000';\n  } else {\n    sql = `UPDATE monthly_budget SET target_saving = ${newBudget} WHERE bulan = DATE_TRUNC('month', NOW())::DATE`;\n    responseTemplate = `\u2705 Target saving diubah menjadi *Rp ${newBudget.toLocaleString('id-ID')}*`;\n    replyType = 'update';\n  }\n\n} else if (command === '/reset_saldo') {\n  const newSaldo = parseFloat(args[0]);\n  if (!newSaldo || isNaN(newSaldo)) {\n    staticReply = '\u274c Format salah. Gunakan: /reset\\_saldo <nominal>\\nContoh: /reset\\_saldo 3500000';\n  } else {\n    sql = `UPDATE saldo_rekening SET saldo_sekarang = ${newSaldo}, updated_at = NOW(), keterangan = 'Reset manual' WHERE id = (SELECT id FROM saldo_rekening ORDER BY id DESC LIMIT 1)`;\n    responseTemplate = `\u2705 Saldo direset menjadi *Rp ${newSaldo.toLocaleString('id-ID')}*`;\n    replyType = 'update';\n  }\n\n} else {\n  staticReply = '\ud83d\udcb0 *Money Tracker Bot \u2014 Daftar Command*\\n\\n' +\n    '\ud83d\udcca *Info Keuangan*\\n' +\n    '/saldo \u2014 Cek saldo rekening\\n' +\n    '/pengeluaran \u2014 Ringkasan budget bulan ini\\n' +\n    '/export \u2014 Export ke Google Sheets\\n\\n' +\n    '\u2699\ufe0f *Pengaturan*\\n' +\n    '/budget <nominal> \u2014 Set target saving\\n' +\n    'Contoh: /budget 4000000\\n\\n' +\n    '/reset\\_saldo <nominal> \u2014 Reset saldo manual\\n' +\n    'Contoh: /reset\\_saldo 3500000\\n\\n' +\n    '\ud83e\udd16 *AI Chat* (bahasa natural)\\n' +\n    'Contoh: \"Berapa saldo saya?\"\\n' +\n    'Contoh: \"Tampilkan transaksi kemarin\"\\n\\n' +\n    '/help \u2014 Tampilkan pesan ini';\n}\n\nconst needsDb = (replyType !== 'text');\nreturn [{ json: { sql, replyType, staticReply, responseTemplate, chatId, needsDb } }];"
      },
      "id": "node-build-cmd",
      "name": "Build Command",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        220
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "needs-db-check",
              "leftValue": "={{ $json.replyType }}",
              "rightValue": "text",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ]
        }
      },
      "id": "node-if-needs-db",
      "name": "Needs DB?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1120,
        220
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "={{ $json.sql }}",
        "options": {}
      },
      "id": "node-exec-cmd-sql",
      "name": "Execute Command SQL",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        1340,
        120
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "is-export-check",
              "leftValue": "={{ $('Build Command').first().json.replyType }}",
              "rightValue": "export",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        }
      },
      "id": "node-if-export",
      "name": "Is Export?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1560,
        120
      ]
    },
    {
      "parameters": {
        "operation": "clear",
        "documentId": {
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "value": "Transaksi",
          "mode": "name"
        }
      },
      "id": "node-clear-transaksi",
      "name": "Clear Transaksi Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1780,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "value": "Transaksi",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "node-write-transaksi",
      "name": "Write Transaksi Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2000,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT TO_CHAR(bulan, 'Month YYYY') AS bulan, total_pengeluaran, total_pemasukan, target_saving, sisa_budget, pct_saving_risk, CASE WHEN pct_saving_risk >= 100 THEN 'DANGER' WHEN pct_saving_risk >= 80 THEN 'WARNING' ELSE 'AMAN' END AS status FROM monthly_budget ORDER BY bulan DESC LIMIT 12",
        "options": {}
      },
      "id": "node-get-budget-export",
      "name": "Get Budget for Export",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        2220,
        40
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "clear",
        "documentId": {
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "value": "Budget",
          "mode": "name"
        }
      },
      "id": "node-clear-budget",
      "name": "Clear Budget Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2440,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "value": "Budget",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "node-write-budget",
      "name": "Write Budget Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2660,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const chatId = $('Build Command').first().json.chatId;\nconst spreadsheetLink = 'https://docs.google.com/spreadsheets/d/YOUR_SPREADSHEET_ID/edit';\nconst finalMessage = '\u2705 *Export Selesai!*\\n\\n' +\n  '\ud83d\udccb *Sheet Transaksi* \u2014 Transaksi bulan ini\\n' +\n  '\ud83d\udcca *Sheet Budget* \u2014 Ringkasan 12 bulan\\n\\n' +\n  `\ud83d\udc49 [Buka Spreadsheet](${spreadsheetLink})`;\nreturn [{ json: { finalMessage, chatId } }];"
      },
      "id": "node-export-reply-msg",
      "name": "Export Reply Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2880,
        40
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $json.chatId }}",
        "text": "={{ $json.finalMessage }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "node-tg-reply-export",
      "name": "Telegram Reply (Export)",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        3100,
        40
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all().map(i => i.json);\nconst chatId = $('Build Command').first().json.chatId;\nconst template = $('Build Command').first().json.responseTemplate;\nconst replyType = $('Build Command').first().json.replyType;\nconst rp = (n) => { const num = parseFloat(String(n).replace(/[^0-9.-]/g, '')); return isNaN(num) ? n : 'Rp ' + num.toLocaleString('id-ID'); };\nconst uangFields = ['nominal', 'saldo_sekarang', 'saldo_akhir', 'total_pengeluaran', 'total_pemasukan', 'target_saving', 'sisa_budget'];\nlet finalMessage = '';\n\nif (replyType === 'update') {\n  finalMessage = template;\n} else if (template === 'SALDO') {\n  const r = rows[0] || {};\n  const updatedAt = r.updated_at ? new Date(r.updated_at).toLocaleString('id-ID', {timeZone:'Asia/Jakarta'}) : '-';\n  finalMessage = `\ud83d\udcb0 *Saldo Rekening*\\n${rp(r.saldo_sekarang)}\\n\\n\ud83d\udd50 Update: ${updatedAt}`;\n} else if (template === 'PENGELUARAN') {\n  if (rows.length === 0) { finalMessage = '\ud83d\udced Belum ada data budget bulan ini.'; }\n  else {\n    const r = rows[0];\n    finalMessage = `\ud83d\udcca *Budget ${r.bulan_label}*\\n\\n\ud83d\udcb8 Pengeluaran: ${rp(r.total_pengeluaran)}\\n\ud83d\udcb5 Pemasukan: ${rp(r.total_pemasukan)}\\n\ud83c\udfaf Target Saving: ${rp(r.target_saving)}\\n\ud83c\udfe6 Sisa Budget: ${rp(r.sisa_budget)}\\n\ud83d\udcc8 Risiko: ${r.pct_saving_risk}%\\n\\nStatus: ${r.status}`;\n  }\n} else {\n  finalMessage = '\u2705 Selesai!';\n}\nreturn [{ json: { finalMessage, chatId } }];"
      },
      "id": "node-format-cmd",
      "name": "Format Command Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1780,
        220
      ]
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first().json;\nreturn [{ json: { finalMessage: item.staticReply, chatId: item.chatId } }];"
      },
      "id": "node-static-reply",
      "name": "Static Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1340,
        320
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $json.chatId }}",
        "text": "={{ $json.finalMessage }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "node-tg-reply-cmd",
      "name": "Telegram Reply (Command)",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2000,
        260
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={{ $env.GEMINI_API_KEY }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={\"contents\":[{\"parts\":[{\"text\":\"Kamu adalah asisten keuangan Money Tracker AI untuk Bank Jago.\\n\\nStruktur database PostgreSQL:\\n- jago_transactions: id, tanggal_waktu, tipe (debit/kredit/transfer_pocket), nominal, merchant_deskripsi, kategori_otomatis, saldo_akhir\\n- monthly_budget: bulan, target_saving, total_pengeluaran, total_pemasukan, sisa_budget, pct_saving_risk\\n- saldo_rekening: id, saldo_sekarang, updated_at\\n\\nPerintah pengguna: {{ $json.userMessage }}\\n\\nBerikan HANYA respons JSON berikut (tanpa markdown, tanpa komentar):\\n{\\\"action\\\":\\\"query atau update atau info\\\",\\\"sql\\\":\\\"SQL query PostgreSQL jika perlu, atau string kosong\\\",\\\"responseTemplate\\\":\\\"Pesan Bahasa Indonesia dengan emoji. Jika butuh data DB tulis {DATA} sebagai placeholder\\\",\\\"message\\\":\\\"Jika action=info isi pesan di sini, jika tidak biarkan kosong\\\"}\\n\\nAturan:\\n- action=query: untuk membaca data (SELECT)\\n- action=update: untuk mengubah/edit data (UPDATE/INSERT/DELETE)\\\\n- Jika user minta edit/ubah/hapus transaksi, buat query UPDATE/DELETE pada jago_transactions\\\\n- action=info: untuk percakapan biasa tanpa perlu DB\\n- Untuk bulan ini gunakan: DATE_TRUNC('month', NOW())::DATE\\n- responseTemplate diisi selalu kecuali action=info\\n\\nINGAT: HANYA output JSON!\"}]}]}",
        "options": {}
      },
      "id": "node-gemini-parse",
      "name": "Gemini: Parse Intent",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        900,
        580
      ]
    },
    {
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst rawText = response.candidates?.[0]?.content?.parts?.[0]?.text || '{}';\nconst cleaned = rawText.replace(/```json/gi, '').replace(/```/g, '').trim();\nlet parsed;\ntry { parsed = JSON.parse(cleaned); } catch(e) {\n  parsed = { action: 'info', sql: '', responseTemplate: '', message: 'Maaf, saya tidak mengerti. Ketik /help untuk daftar command.' };\n}\nconst chatId = $('Extract Message').first().json.chatId;\nconst hasSql = parsed.action === 'query' || parsed.action === 'update';\nreturn [{ json: { ...parsed, chatId, hasSql } }];"
      },
      "id": "node-parse-response",
      "name": "Parse Gemini Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        580
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "has-sql-check",
              "leftValue": "={{ $json.action }}",
              "rightValue": "info",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ]
        }
      },
      "id": "node-if-sql",
      "name": "Has SQL?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1340,
        580
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "={{ $json.sql }}",
        "options": {}
      },
      "id": "node-postgres-exec",
      "name": "Execute SQL",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        1560,
        480
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all().map(i => i.json);\nconst chatId = $('Parse Gemini Response').first().json.chatId;\nconst template = $('Parse Gemini Response').first().json.responseTemplate || '\u2705 Selesai!';\nconst rp = (n) => { const num = parseFloat(String(n).replace(/[^0-9.-]/g, '')); return isNaN(num) ? n : 'Rp ' + num.toLocaleString('id-ID'); };\nconst uangFields = ['nominal', 'saldo_sekarang', 'saldo_akhir', 'total_pengeluaran', 'total_pemasukan', 'target_saving', 'sisa_budget'];\nlet dataText = '';\nif (rows.length === 0) { dataText = '(tidak ada data)'; }\nelse if (rows.length === 1) {\n  dataText = Object.entries(rows[0]).map(([k, v]) => `${k.replace(/_/g, ' ')}: ${uangFields.includes(k) ? rp(v) : v}`).join('\\n');\n} else {\n  dataText = rows.map((r, i) => `${i+1}. ${Object.entries(r).map(([k,v]) => uangFields.includes(k) ? rp(v) : v).join(' | ')}`).join('\\n');\n}\nconst finalMessage = template.replace('{DATA}', dataText);\nreturn [{ json: { finalMessage, chatId } }];"
      },
      "id": "node-format-result",
      "name": "Format SQL Result",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1780,
        480
      ]
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first().json;\nreturn [{ json: { finalMessage: item.message, chatId: item.chatId } }];"
      },
      "id": "node-direct-reply",
      "name": "Direct Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1560,
        680
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $json.chatId }}",
        "text": "={{ $json.finalMessage }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "node-tg-reply-ai",
      "name": "Telegram Reply (AI)",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2000,
        560
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Telegram Message Trigger": {
      "main": [
        [
          {
            "node": "Extract Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Message": {
      "main": [
        [
          {
            "node": "Is Command?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Command?": {
      "main": [
        [
          {
            "node": "Build Command",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gemini: Parse Intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Command": {
      "main": [
        [
          {
            "node": "Needs DB?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Needs DB?": {
      "main": [
        [
          {
            "node": "Execute Command SQL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Static Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Command SQL": {
      "main": [
        [
          {
            "node": "Is Export?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Export?": {
      "main": [
        [
          {
            "node": "Clear Transaksi Sheet",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Command Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clear Transaksi Sheet": {
      "main": [
        [
          {
            "node": "Write Transaksi Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Transaksi Sheet": {
      "main": [
        [
          {
            "node": "Get Budget for Export",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Budget for Export": {
      "main": [
        [
          {
            "node": "Clear Budget Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clear Budget Sheet": {
      "main": [
        [
          {
            "node": "Write Budget Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Budget Sheet": {
      "main": [
        [
          {
            "node": "Export Reply Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Export Reply Message": {
      "main": [
        [
          {
            "node": "Telegram Reply (Export)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Command Reply": {
      "main": [
        [
          {
            "node": "Telegram Reply (Command)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Static Reply": {
      "main": [
        [
          {
            "node": "Telegram Reply (Command)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini: Parse Intent": {
      "main": [
        [
          {
            "node": "Parse Gemini Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Gemini Response": {
      "main": [
        [
          {
            "node": "Has SQL?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has SQL?": {
      "main": [
        [
          {
            "node": "Execute SQL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Direct Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute SQL": {
      "main": [
        [
          {
            "node": "Format SQL Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format SQL Result": {
      "main": [
        [
          {
            "node": "Telegram Reply (AI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Direct Reply": {
      "main": [
        [
          {
            "node": "Telegram Reply (AI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner"
  },
  "staticData": null,
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": [
    {
      "name": "expense-tracker",
      "createdAt": "2026-02-26"
    }
  ]
}

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

AI Money Tracker Chatbot. Uses telegramTrigger, postgres, googleSheets, telegram. Event-driven trigger; 24 nodes.

Source: https://github.com/yusdar31/money-tracking-n8n/blob/f0913b610d44a184dd4a76f17fcc86eddbcb771d/n8n/ai_chatbot_workflow.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

This workflow is perfect for productivity-focused teams, remote workers, virtual assistants, and digital knowledge managers who receive documents, images, or notes through Telegram and want to automat

Telegram Trigger, Telegram, HTTP Request +3
Data & Sheets

This workflow acts as a junior finance research analyst for a UK boutique M&A or corporate finance team. It listens for Slack messages, classifies the request, gathers company or market data, and prod

HTTP Request, Google Drive, Google Docs +5
Data & Sheets

This n8n workflow retrieves AI agent chat memory logs stored in Postgres and pushes them to Google Sheets, creating one sheet per session. It’s useful for teams building chat-based products or agents

Postgres, HTTP Request, Google Sheets
Data & Sheets

This n8n workflow automates the transformation of spreadsheet data into professional charts and graphs using AI-driven analysis. Triggered via Slack, it processes uploaded files (Excel, CSV, Google Sh

Agent, Postgres, HTTP Request +8
Data & Sheets

Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.

N8N Nodes Evolution Api, Redis, HTTP Request +3