AutomationFlowsAI & RAG › Auto Expense Tracker From Line Messages with Gpt-4 and Google Sheets

Auto Expense Tracker From Line Messages with Gpt-4 and Google Sheets

ByCatalina Kuo @catalinakuowork on n8n.io

Do you often forget to record expenses?

Webhook trigger★★★★☆ complexityAI-powered25 nodesHTTP RequestAgentOpenAI ChatGoogle SheetsOutput Parser Structured
AI & RAG Trigger: Webhook Nodes: 25 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #6264 — we link there as the canonical source.

This workflow follows the Agent → Google Sheets 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
{
  "id": "lqR2ivTOUP1U8YWq",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Special Tracker Share",
  "tags": [],
  "nodes": [
    {
      "id": "a9abe724-ec70-4aa9-9579-39968d9f624c",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -3000,
        40
      ],
      "parameters": {
        "path": "your-webhook-path",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "559432eb-1739-49e7-9fae-9337ef843239",
      "name": "message",
      "type": "n8n-nodes-base.set",
      "position": [
        -2540,
        20
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "18602838-3e42-4804-b809-938dea492e38",
              "name": "body.events[0].message.text",
              "type": "string",
              "value": "={{ $json.body.events[0].message.text }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1161ff82-4ceb-435a-a4ed-c2ca4f618627",
      "name": "image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2540,
        200
      ],
      "parameters": {
        "url": "=https://api-data.line.me/v2/bot/message/{{ $json.body.events[0].message.id }}/content ",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <Line Channel access token>"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "afb32020-22d2-420c-9567-b3b81fda1075",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -2200,
        20
      ],
      "parameters": {
        "text": "=Please analyze {{ $json.body.events[0].message.text }} or the image",
        "options": {
          "systemMessage": "=Check relevance: If the text is not an expense record or invoice, immediately stop all processing. If it is bookkeeping-related, extract the six data fields below.\nExtract these six pieces of information: \n1. Date (If the input only says \u201ctoday\u201d, use {{ $now.format('YYYY-MM-DD') }}) 2. Channel (Free text from the input) 3. Channel Type (Must be exactly one of: Convenience Store, Personal Care Store, Hypermarket / Supermarket, Traditional Market, Online Shopping, Pharmacy, Hardware Store, Restaurant / Food, Stall, Medical Clinic / Hospital, 3C / Electronics Mall, Airline / Passenger Transport, Software Top-Up, Gas / Transit Top-Up, Online Course, Telecom Company) 4. Expense Description (Free text from the input) 5. Amount 6. Category (Must be exactly one of: Household, Main Meals, Drinks & Desserts, Household Items, Beauty, Clothing & Accessories, Transport, Entertainment, Telecom, Medical, 3C, Software, Learning, Travel)\nOutput format: JSON \uff1a{\\\"Date\\\": \\\"...\\\", \\\"Channel\\\": \\\"...\\\", \\\"Channel Type\\\": \\\"...\\\", \\\"Expense Description\\\": \\\"...\\\", \\\"Amount\\\": \\\"...\\\", \\\"Category\\\": \\\"...\\\"}\"\n\n",
          "passthroughBinaryImages": true
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2
    },
    {
      "id": "a4fe0a3b-40e0-4cf0-9d44-86abadca9fe4",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -2200,
        340
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini",
          "cachedResultName": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "516361f7-21a9-4d9a-9f44-2bab05b9bfcb",
      "name": "reply_to_line",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        60
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"\u2705 Expense recorded successfully: {{ $('Merge_all').item.json['for_duplication'] }}\"\n    }\n  ]\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <Line Channel access token>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "ab9fbffc-cd1e-4a2c-8f5e-23ee0f844c77",
      "name": "Get row(s) in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1540,
        260
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 372483996,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk/edit#gid=372483996",
          "cachedResultName": "2025en"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk/edit?usp=drivesdk",
          "cachedResultName": "Spending Tracker"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6,
      "alwaysOutputData": true
    },
    {
      "id": "88bd5d9f-bcaa-4191-bcb6-1e3a0b138879",
      "name": "Merge_all",
      "type": "n8n-nodes-base.merge",
      "position": [
        -700,
        40
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2,
      "alwaysOutputData": false
    },
    {
      "id": "5d153710-d3a2-42dc-a090-c4faa3390210",
      "name": "reply_to_line_duplicated",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -200,
        280
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"\u26a0\ufe0f This entry has already been logged and will not be duplicated\"\n    }\n  ]\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <Line Channel access token>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "570de74c-b410-40ff-b7ac-781625c912a7",
      "name": "reply_to_line_no_spending",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -200,
        -120
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Irrelevant details or images will not be logged.\"\n    }\n  ]\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <Line Channel access token>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "05789d97-3ebb-4e9f-951d-ecd54c4da404",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -2000,
        180
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"Date\": \"...\",\n  \"Channel\": \"...\",\n  \"Channel Type\": \"...\",\n  \"Expense Description\": \"...\",\n  \"Amount\": \"...\",\n  \"Category\": \"...\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "cbadff1d-15f5-4282-a62f-b42c25b41661",
      "name": "append_to_sheet1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -200,
        60
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $json.output.Date }}",
            "Amount": "={{ $json.output.Amount }}",
            "Channel": "={{ $json.output.Channel }}",
            "Category": "={{ $json.output.Category }}",
            "Channel Type": "={{ $json.output['Channel Type'] }}",
            "for_duplication": "={{ $json.for_duplication }}",
            "Expense Description": "={{ $json.output['Expense Description'] }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Channel",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Channel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Channel Type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Channel Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Expense Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Expense Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Amount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "for_duplication",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "for_duplication",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "for_duplication"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 372483996,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk/edit#gid=372483996",
          "cachedResultName": "2025en"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_JK8t0LdX-RF3oBwDTJ30mpkkw8vs02xR0folLryebk/edit?usp=drivesdk",
          "cachedResultName": "Spending Tracker"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "aee3b5eb-ff29-4847-ab57-8cbb0cdba7a6",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        -940,
        260
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "renameField": true,
              "outputFieldName": "dedupeList",
              "fieldToAggregate": "for_duplication"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8da264bd-5b13-4912-ae8e-7ded8fc6af17",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2540,
        -640
      ],
      "parameters": {
        "width": 720,
        "height": 540,
        "content": "## Prompt en\nCheck relevance: If the text is not an expense record or invoice, immediately stop all processing. If it is bookkeeping-related, extract the six data fields below.\nExtract these six pieces of information: \n1. Date (All numbers. If the input only says \u201ctoday\u201d, use {{ $now.format('YYYY-MM-DD') }}) 2. Channel (Free text from the input) 3. Channel Type (Must be exactly one of: Convenience Store, Personal Care Store, Hypermarket / Supermarket, Traditional Market, Online Shopping, Pharmacy, Hardware Store, Restaurant / Food, Stall, Medical Clinic / Hospital, 3C / Electronics Mall, Airline / Passenger Transport, Software Top-Up, Gas / Transit Top-Up, Online Course, Telecom Company) 4. Expense Description (Free text from the input) 5. Amount 6. Category (Must be exactly one of: Household, Main Meals, Drinks & Desserts, Household Items, Beauty, Clothing & Accessories, Transport, Entertainment, Telecom, Medical, 3C, Software, Learning, Travel)\nOutput format: JSON \uff1a{\\\"Date\\\": \\\"...\\\", \\\"Channel\\\": \\\"...\\\", \\\"Channel Type\\\": \\\"...\\\", \\\"Expense Description\\\": \\\"...\\\", \\\"Amount\\\": \\\"...\\\", \\\"Category\\\": \\\"...\\\"}\"\n\n## Prompt zh\n\u5206\u6790\u51fa\u8cc7\u8a0a\uff1a\n\u5148\u5224\u65b7\u662f\u5426\u70ba\u8a18\u5e33\u76f8\u95dc\u660e\u7d30\u3001\u767c\u7968\uff0c\u82e5\u4e0d\u662f\u5247\u4e0d\u9700\u8981\u8655\u7406\uff0c\u76f4\u63a5\u505c\u6b62\u6240\u6709\u6d41\u7a0b\u3002\u82e5\u662f\u8a18\u5e33\u76f8\u95dc\uff0c\u5206\u6790\u51fa\u516d\u500b\u8cc7\u8a0a:\n1. \u65e5\u671f\uff08\u5982\u679c\u53ea\u63d0\u5230'\u4eca\u5929'\uff0c\u7528{{ $now.format('YYYY-MM-DD') }} \uff09 2. \u901a\u8def 3. \u901a\u8def\u985e\u578b\uff08\u53ea\u80fd\u70ba\uff1a\u4fbf\u5229\u5546\u5e97\u3001\u500b\u4eba\u7528\u54c1\u5e97\u3001\u91cf\u8ca9\u8d85\u5e02\u3001\u50b3\u7d71\u5e02\u5834\u3001\u7db2\u8def\u8cfc\u7269\u3001\u85e5\u5c40\u3001\u4e94\u91d1\u767e\u8ca8\u3001\u9910\u5ef3\u5c0f\u5403\u5e97\u3001\u91ab\u7642\u9662\u6240\u30013C\u5546\u5834\u3001\u822a\u7a7a\u5ba2\u904b\u3001\u8edf\u9ad4\u5132\u503c\u3001\u52a0\u6cb9\u4ea4\u901a\u5132\u503c\u3001\u7dda\u4e0a\u8ab2\u7a0b\u3001\u96fb\u4fe1\u516c\u53f8\uff094. \u82b1\u8cbb\u660e\u7d30 5. \u91d1\u984d 6. \u985e\u5225\uff08\u53ea\u80fd\u70ba\uff1a\u5bb6\u7528\u3001\u6b63\u9910\u98f2\u98df\u3001\u98f2\u6599\u751c\u9ede\u3001\u751f\u6d3b\u7528\u54c1\u3001\u7f8e\u599d\u3001\u8863\u670d\u98fe\u54c1\u3001\u4ea4\u901a\u3001\u5a1b\u6a02\u3001\u96fb\u4fe1\u3001\u91ab\u7642\u30013C\u3001\u8edf\u9ad4\u3001\u5b78\u7fd2\u3001\u65c5\u904a\uff09\u3002\u82e5\u901a\u8def\u985e\u578b\u5224\u65b7\u70ba\u822a\u7a7a\u5ba2\u904b\uff0c\u985e\u5225\u4e00\u5b9a\u70ba\u65c5\u904a\u3002\u82e5\u7121\u6cd5\u5224\u65b7\u6216\u6c92\u8cc7\u8a0a\uff0c\u8acb\u586b\u5165'DN'\uff0c\u6bcf\u683c\u90fd\u8981\u6709\u8cc7\u6599\u3002\u8acb\u8f38\u51fa\u70ba JSON \u683c\u5f0f\uff0c\u4f8b\u5982\uff1a{\\\"\u65e5\u671f\\\": \\\"...\\\", \\\"\u901a\u8def\\\": \\\"...\\\", \\\"\u901a\u8def\u985e\u578b\\\": \\\"...\\\", \\\"\u82b1\u8cbb\u660e\u7d30\\\": \\\"...\\\", \\\"\u91d1\u984d\\\": \\\"...\\\", \\\"\u985e\u5225\\\": \\\"...\\\"}\"\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4f8f3594-5aa1-4279-b25e-502658e8bedc",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2100,
        440
      ],
      "parameters": {
        "width": 280,
        "height": 440,
        "content": "## Structured Output en\n{\n  \"Date\": \"...\",\n  \"Channel\": \"...\",\n  \"Channel Type\": \"...\",\n  \"Expense Description\": \"...\",\n  \"Amount\": \"...\",\n  \"Category\": \"...\"\n}\n\n## Structured Output zh\n{\n  \"\u65e5\u671f\": \"...\",\n  \"\u901a\u8def\": \"...\",\n  \"\u901a\u8def\u985e\u578b\": \"...\",\n  \"\u82b1\u8cbb\u660e\u7d30\": \"...\",\n  \"\u91d1\u984d\": \"...\",\n  \"\u985e\u5225\": \"...\"\n}"
      },
      "typeVersion": 1
    },
    {
      "id": "efb13bc2-bc7a-4f6a-8ec1-d783ac9c1d41",
      "name": "deduplication",
      "type": "n8n-nodes-base.set",
      "position": [
        -1700,
        20
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2f544a6e-2a51-4979-b0fb-ef9c3dcf54c6",
              "name": "for_duplication",
              "type": "string",
              "value": "={{ $json.output.Date }}-{{ $json.output['Channel Type'] }}-{{ $json.output.Amount }}-{{ $json.output.Category }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "f6f35e3c-0424-4e7c-be0f-33e191dca2b4",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1700,
        -420
      ],
      "parameters": {
        "width": 320,
        "height": 300,
        "content": "## deduplication en\nfor_duplication = {{ $json.output.Date }}-{{ $json.output['Channel Type'] }}-{{ $json.output.Amount }}-{{ $json.output.Category }}\n\n## deduplication zh\n\u53bb\u91cd\u4f7f\u7528 = {{ $json.output['\u65e5\u671f'] }}-{{ $json.output['\u901a\u8def\u985e\u578b'] }}-{{ $json.output['\u91d1\u984d'] }}-{{ $json.output['\u985e\u5225'] }}"
      },
      "typeVersion": 1
    },
    {
      "id": "296b9ff9-4819-49a8-b813-37dc507db57d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1760,
        440
      ],
      "parameters": {
        "color": 4,
        "width": 320,
        "height": 480,
        "content": "## Google Sheet Fields en\nSet up in advance\n1. Date\n2. Channel\n3. Channel Type\n4. Expense Description\n5. Amount\n6. Category\n7. for_duplication\n\n## Google Sheet Fields zh\n\u9810\u5148\u8a2d\u7f6e\u597d\n1. \u65e5\u671f\n2. \u901a\u8def\n3. \u901a\u8def\u985e\u578b\n4. \u82b1\u8cbb\u660e\u7d30\n5. \u91d1\u984d\n6. \u985e\u5225\n7. \u53bb\u91cd\u4f7f\u7528"
      },
      "typeVersion": 1
    },
    {
      "id": "8ff07d6b-6aa1-4a43-ab5a-231a4f668ca3",
      "name": "for_deduplications",
      "type": "n8n-nodes-base.set",
      "position": [
        -1240,
        260
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9b7c37f4-beac-493d-8cec-81d59fe144f7",
              "name": "for_duplication",
              "type": "string",
              "value": "={{ $json.for_duplication }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e1af076e-37f4-4cdf-8a9c-ca5f3cd2e743",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1380,
        440
      ],
      "parameters": {
        "width": 320,
        "height": 240,
        "content": "## for_deduplications en\nManual Mapping\nfor_deduplication = {{ $json['for_duplication'] }}\n\n## for_deduplications zh\nManual Mapping\n\u53bb\u91cd\u4f7f\u7528 = {{ $json['\u53bb\u91cd\u4f7f\u7528'] }}"
      },
      "typeVersion": 1
    },
    {
      "id": "f62cb77b-74e1-4b90-9937-95af55e76497",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -940,
        440
      ],
      "parameters": {
        "width": 220,
        "height": 260,
        "content": "## Aggregrate en\nindividual field\ninput for_deduplication\noutput dedupeList\n\n## Aggregrate zh\nindividual field\ninput \u53bb\u91cd\u4f7f\u7528\noutput dedupeList"
      },
      "typeVersion": 1
    },
    {
      "id": "e2f1f377-4d78-4853-8d4e-037c4db5a483",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -800,
        -640
      ],
      "parameters": {
        "width": 540,
        "height": 540,
        "content": "## Switch en\n### empty\n{{ $('Merge_all').item.json['for_duplication'] }} = DN-DN-DN-DN\n{{ $('Merge_all').item.json['for_duplication'] }} matches regrx ^.*-DN-DN-DN$\n{{ $('Merge_all').item.json['for_duplication'] }} = ---\n### add\n{{ !$json.dedupeList.includes($json['for_duplication']) }} is true\n### duplicate\n{{ !$json.dedupeList.includes($json['for_duplication']) }} is false\n\n## Switch zh\n### empty\n{{ $('Merge_all').item.json['\u53bb\u91cd\u4f7f\u7528'] }} = DN-DN-DN-DN\n{{ $('Merge_all').item.json['\u53bb\u91cd\u4f7f\u7528'] }} matches regrx ^.*-DN-DN-DN$\n{{ $('Merge_all').item.json['\u53bb\u91cd\u4f7f\u7528'] }} = ---\n### add\n{{ !$json.dedupeList.includes($json['\u53bb\u91cd\u4f7f\u7528']) }} is true\n### duplicate\n{{ !$json.dedupeList.includes($json['\u53bb\u91cd\u4f7f\u7528']) }} is false"
      },
      "typeVersion": 1
    },
    {
      "id": "c9ea87a2-0f69-47a7-b409-1ea4ada2a666",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3240,
        -400
      ],
      "parameters": {
        "color": 4,
        "width": 400,
        "height": 300,
        "content": "## Requirements en\n1. Set up GCP OAuth and enable the Google Sheets API\n2. Pre-configure the Google Sheet field names\n3. Obtain the LINE Developer Webhook URL\n4. OpenAI API Key\n\n## \u9700\u6c42 zh\n1. \u8a2d\u7f6e GCP OAuth & \u958b\u555f google sheet API  \n2. \u9810\u5148\u8a2d\u7f6e\u597d Google Sheet field name\n3. \u53d6\u5f97 Line Developer Webhook URL \n4. Open AI API KEY"
      },
      "typeVersion": 1
    },
    {
      "id": "6a01a146-0b13-4ac5-8429-bf8452157587",
      "name": "Switch based on Expense Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        -2780,
        40
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "text",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "45419786-9466-49e9-bfe9-a46923cfe56e",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.events[0].message.type }}",
                    "rightValue": "text"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "image",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "dba6e722-8432-481b-aa6d-9a3fdff4f022",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.events[0].message.type }}",
                    "rightValue": "image"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "12f21b25-8a50-4224-8f3d-cfcba20510b6",
      "name": "Response Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        -460,
        0
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "empty",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "8e802d98-e4ed-4e9b-a383-4e44229bce76",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.for_duplication }}",
                    "rightValue": "DN-DN-DN-DN"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "empty",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "07afa184-938a-45fc-aa3f-69d9ea989058",
                    "operator": {
                      "type": "string",
                      "operation": "regex"
                    },
                    "leftValue": "={{ $json.for_duplication }}",
                    "rightValue": "^.*-DN-DN-DN$"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "empty",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "01611be4-10af-43e2-99a4-00c96ea4f947",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.for_duplication }}",
                    "rightValue": "---"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "add",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "55f50acc-423c-4cf9-844a-a4a0e0c48973",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ !$json.dedupeList.includes($json['for_duplication']) }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "duplicate",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "7cdabbfc-6a66-4f28-a2cf-781fe145faca",
                    "operator": {
                      "type": "boolean",
                      "operation": "false",
                      "singleValue": true
                    },
                    "leftValue": "={{ !$json.dedupeList.includes($json['for_duplication']) }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "none"
        },
        "looseTypeValidation": true
      },
      "typeVersion": 3.2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "99a76ce1-eb41-4cb5-93a9-8bed26cf767d",
  "connections": {
    "image": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Switch based on Expense Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "message": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "deduplication",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Merge_all",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge_all": {
      "main": [
        [
          {
            "node": "Response Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "deduplication": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge_all",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Response Switch": {
      "main": [
        [
          {
            "node": "reply_to_line_no_spending",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "reply_to_line_no_spending",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "reply_to_line_no_spending",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "append_to_sheet1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "reply_to_line_duplicated",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "append_to_sheet1": {
      "main": [
        [
          {
            "node": "reply_to_line",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "for_deduplications": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "for_deduplications",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Switch based on Expense Type": {
      "main": [
        [
          {
            "node": "message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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

Do you often forget to record expenses?

Source: https://n8n.io/workflows/6264/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

⏺ 🚀 How it works

Agent, Anthropic Chat, Output Parser Structured +6
AI & RAG

leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.

Supabase, Gmail, Form Trigger +13
AI & RAG

🧠 Gwen – The AI Voice Marketing Agent Gwen is your intelligent voice-powered marketing assistant built in n8n. She combines the power of OpenAI, ElevenLabs, and automation workflows to handle content

Tool Workflow, Memory Buffer Window, Agent +10
AI & RAG

Tired of grinding out YouTube content? This n8n workflow turns AI into your personal video factory—creating engaging, faceless shorts on autopilot. Perfect for creators, marketers, or side-hustlers lo

HTTP Request, Google Drive, Google Sheets +6
AI & RAG

Faceless YouTube Generator. Uses httpRequest, limit, googleDrive, googleSheets. Webhook trigger; 49 nodes.

HTTP Request, Google Drive, Google Sheets +7