AutomationFlowsAI & RAG › Pay

Pay

PAY. Uses agent, lmChatGoogleGemini, httpRequest, googleSheets. Webhook trigger; 35 nodes.

Webhook trigger★★★★★ complexityAI-powered35 nodesAgentGoogle Gemini ChatHTTP RequestGoogle Sheets
AI & RAG Trigger: Webhook Nodes: 35 Complexity: ★★★★★ AI nodes: yes Added:
Pay — n8n workflow card showing Agent, Google Gemini Chat, HTTP Request integration

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
{
  "createdAt": "2025-11-22T02:45:09.052Z",
  "updatedAt": "2026-01-30T06:41:50.000Z",
  "id": "rZjkLEVp0IM9xKfh",
  "name": "PAY",
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "PAY-HISTORY",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1504,
        -272
      ],
      "id": "8be8aa6e-80cf-4217-bbee-92e7783dc2ee",
      "name": "Webhook"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "Analyze this image and extract data",
        "options": {
          "systemMessage": "You are an expert Thai Accountant and OCR Specialist AI. Your task is to extract data from Thai bank transfer slips (GH Bank, KPlus, SCB, etc.) with 100% precision and classify the transaction.\n\n### CRITICAL INSTRUCTION: REFERENCE ID (ref_id)\n1. **L vs 1 Ambiguity:** The font used in bank slips often makes the lowercase letter \"l\" (India-Lima) and the number \"1\" look identical. You must analyze the visual context carefully.\n2. **Format:** The `ref_id` must be a single continuous string. **REMOVE all spaces and line breaks** immediately.\n\n### USER IDENTITY (The Owner)\nThe user is **\"Teerayut Yeerahem\"** (Thai: \u0e19\u0e32\u0e22 \u0e18\u0e35\u0e23\u0e22\u0e38\u0e17\u0e18 \u0e2b\u0e22\u0e35\u0e23\u0e32\u0e40\u0e2b\u0e21).\nMatches include variations: \"Teerayut\", \"Theerayut\", \"\u0e18\u0e35\u0e23\u0e22\u0e38\u0e17\u0e18\", \"Mr. Teerayut\", \"Teerayut Y\".\n\n### CLASSIFICATION LOGIC (Strict Order)\nAnalyze `Sender` and `Receiver` names to determine `transaction_type`:\n\n1. **\"transfer\" (Internal Move):**\n   - IF `Sender` matches User Identity AND `Receiver` matches User Identity.\n   - (Meaning: Moving money between own accounts).\n\n2. **\"expense\" (Spending):**\n   - IF `Sender` matches User Identity AND `Receiver` is SOMEONE ELSE.\n\n3. **\"income\" (Receiving):**\n   - IF `Sender` is SOMEONE ELSE AND `Receiver` matches User Identity.\n\n4. **\"unknown\":** If neither matches or names are illegible.\n\n### DATA EXTRACTION RULES\n1. **Date:** Convert to ISO format **YYYY-MM-DD**.\n   - **Crucial:** Convert Buddhist Year to AD correctly (e.g., BE 2568 = AD 2025, BE 2569 = AD 2026).\n2. **Time:** Format as **HH:mm** (24-hour format).\n3. **Amount:** Numeric value only (remove commas, remove \"THB\").\n4. **Category:**\n   - If type is \"transfer\" -> \"Internal Move\".\n   - If type is \"expense\" -> Guess category based on Receiver (e.g., \"Food\", \"Shopping\", \"Utility\"). Default to \"Uncategorized\" if unsure.\n   - If type is \"income\" -> \"Revenue\".\n\n### OUTPUT FORMAT\nReturn **ONLY** a raw JSON object (no markdown code blocks, no explanation).\n\n{\n  \"transaction_data\": {\n    \"date\": \"YYYY-MM-DD\",\n    \"time\": \"HH:mm\",\n    \"amount\": Number,\n    \"sender_name\": \"String\",\n    \"sender_bank\": \"String\",\n    \"receiver_name\": \"String\",\n    \"receiver_bank\": \"String\",\n    \"ref_id\": \"String\",\n    \"transaction_type\": \"String\",\n    \"category\": \"String\"\n  }\n}",
          "passthroughBinaryImages": true
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        624,
        400
      ],
      "id": "6fe5f860-b3b7-4766-9b9c-160013a772a3",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1,
      "position": [
        592,
        624
      ],
      "id": "61267499-1863-47ee-a357-825f737580c7",
      "name": "Google Gemini Chat Model",
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "faa0762e-6483-4684-9754-e84ce1bb0bf2",
              "leftValue": "={{ $json.body.events[0].message.type }}",
              "rightValue": "image",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        96,
        400
      ],
      "id": "283b34b1-4389-4ad1-828c-0b2c8b9fda6b",
      "name": "Filter"
    },
    {
      "parameters": {
        "url": "=https://api-data.line.me/v2/bot/message/{{ $json.body.events[0].message.id }}/content",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        352,
        400
      ],
      "id": "bca842c1-f508-419e-86ac-e5b0fb1e3b4d",
      "name": "HTTP Request",
      "retryOnFail": true,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const aiResponse = $input.item.json.output || $input.item.json.text;\nconst cleanJson = aiResponse.replace(/```json/g, '').replace(/```/g, '').trim();\n\ntry {\n  const data = JSON.parse(cleanJson);\n  const tx = data.transaction_data;\n\n  // normalize\n  tx.ref_id = String(tx.ref_id || '').trim();   // \u274c \u0e44\u0e21\u0e48\u0e15\u0e49\u0e2d\u0e07 toUpperCase\n\n  return tx;\n} catch (e) {\n  return { error: true, message: \"\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a JSON \u0e1c\u0e34\u0e14\u0e1e\u0e25\u0e32\u0e14\" };\n}\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        976,
        400
      ],
      "id": "cf6b8e37-aef3-41fe-9adf-6f9eb0327aa1",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds",
          "mode": "list",
          "cachedResultName": "Pay History",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit#gid=0"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "date": "={{ $('Code in JavaScript').item.json.date }}",
            "time": "={{ $('Code in JavaScript').item.json.time }}",
            "amount": "={{ $('Code in JavaScript').item.json.amount }}",
            "category": "={{ $('Code in JavaScript').item.json.category }}",
            "sender_name": "={{ $('Code in JavaScript').item.json.sender_name }}",
            "sender_bank": "={{ $('Code in JavaScript').item.json.sender_bank }}",
            "receiver_name": "={{ $('Code in JavaScript').item.json.receiver_name }}",
            "receiver_bank": "={{ $('Code in JavaScript').item.json.receiver_bank }}",
            "ref_id": "={{ $('Code in JavaScript').item.json.ref_id }}",
            "transaction_type (income/expense/transfer)": "={{ $('Code in JavaScript').item.json.transaction_type }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "date",
              "displayName": "date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "time",
              "displayName": "time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "transaction_type (income/expense/transfer)",
              "displayName": "transaction_type (income/expense/transfer)",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "amount",
              "displayName": "amount",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "category",
              "displayName": "category",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "sender_name",
              "displayName": "sender_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "sender_bank",
              "displayName": "sender_bank",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "receiver_name",
              "displayName": "receiver_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "receiver_bank",
              "displayName": "receiver_bank",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "ref_id",
              "displayName": "ref_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2080,
        576
      ],
      "id": "78b9b698-7ca6-4960-b6b2-1cbab2f49cfe",
      "name": "Append row in sheet",
      "retryOnFail": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.line.me/v2/bot/message/reply",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"flex\",\n      \"altText\": \"\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e2a\u0e33\u0e40\u0e23\u0e47\u0e08\",\n      \"contents\": {\n        \"type\": \"bubble\",\n        \"body\": {\n          \"type\": \"box\",\n          \"layout\": \"vertical\",\n          \"contents\": [\n            {\n              \"type\": \"text\",\n              \"text\": \"\u2705 \u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01\u0e2a\u0e33\u0e40\u0e23\u0e47\u0e08\",\n              \"weight\": \"bold\",\n              \"size\": \"xl\",\n              \"color\": \"#1DB446\"\n            },\n            {\n              \"type\": \"separator\",\n              \"margin\": \"md\"\n            },\n            {\n              \"type\": \"box\",\n              \"layout\": \"vertical\",\n              \"margin\": \"md\",\n              \"spacing\": \"sm\",\n              \"contents\": [\n                {\n                  \"type\": \"box\",\n                  \"layout\": \"baseline\",\n                  \"contents\": [\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\",\n                      \"color\": \"#aaaaaa\",\n                      \"size\": \"sm\",\n                      \"flex\": 1\n                    },\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"{{ $('Append row in sheet').item.json.transaction_type || '-' }}\",\n                      \"wrap\": true,\n                      \"color\": \"#666666\",\n                      \"size\": \"sm\",\n                      \"flex\": 2\n                    }\n                  ]\n                },\n                {\n                  \"type\": \"box\",\n                  \"layout\": \"baseline\",\n                  \"contents\": [\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\",\n                      \"color\": \"#aaaaaa\",\n                      \"size\": \"sm\",\n                      \"flex\": 1\n                    },\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"{{ $('Append row in sheet').item.json.receiver_name || '\u0e44\u0e21\u0e48\u0e23\u0e30\u0e1a\u0e38' }}\",\n                      \"wrap\": true,\n                      \"color\": \"#666666\",\n                      \"size\": \"sm\",\n                      \"flex\": 2\n                    }\n                  ]\n                },\n                {\n                  \"type\": \"box\",\n                  \"layout\": \"baseline\",\n                  \"contents\": [\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"\u0e22\u0e2d\u0e14\u0e40\u0e07\u0e34\u0e19\",\n                      \"color\": \"#aaaaaa\",\n                      \"size\": \"sm\",\n                      \"flex\": 1\n                    },\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"{{ $('Append row in sheet').item.json.amount }} \u0e1a\u0e32\u0e17\",\n                      \"wrap\": true,\n                      \"color\": \"#333333\",\n                      \"size\": \"lg\",\n                      \"weight\": \"bold\",\n                      \"flex\": 2\n                    }\n                  ]\n                }\n              ]\n            }\n          ]\n        }\n      }\n    }\n  ]\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2336,
        576
      ],
      "id": "04d4a598-9f51-4cd2-babd-3fafb571dad6",
      "name": "HTTP Request1",
      "retryOnFail": true,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds",
          "mode": "list",
          "cachedResultName": "Pay History",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit#gid=0"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "date",
              "lookupValue": "={{ $json.date }}"
            },
            {
              "lookupColumn": "time",
              "lookupValue": "={{ $json.time }}"
            },
            {
              "lookupColumn": "amount",
              "lookupValue": "={{ $json.amount }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1248,
        400
      ],
      "id": "3f42e9e8-8255-4359-9ebe-598d5ae2a448",
      "name": "Get row(s) in sheet",
      "alwaysOutputData": true,
      "retryOnFail": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "d68ced39-8a95-437f-aa9e-6811ca74d120",
              "leftValue": "={{ $json.ref_id }}",
              "rightValue": 0,
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1552,
        400
      ],
      "id": "650fef6d-e18d-49d0-9277-907a32ca62ba",
      "name": "If"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.line.me/v2/bot/message/reply",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').first().json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"\u26a0\ufe0f \u0e2a\u0e25\u0e34\u0e1b\u0e19\u0e35\u0e49\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01\u0e44\u0e1b\u0e41\u0e25\u0e49\u0e27\u0e04\u0e23\u0e31\u0e1a (Ref: {{ $('Code in JavaScript').first().json.transaction_data.ref_id }})\"\n    }\n  ]\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1824,
        384
      ],
      "id": "d9177044-d72b-409c-b5a8-52c974af2c08",
      "name": "HTTP Request2",
      "executeOnce": true,
      "retryOnFail": true,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": 10
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1840,
        576
      ],
      "id": "4bb65bd4-b9d3-4f7d-9474-afb848d6f6e4",
      "name": "Wait"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtHour": 23
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -1120,
        768
      ],
      "id": "dae57c55-8523-4495-b77f-506376b199c9",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds",
          "mode": "list",
          "cachedResultName": "Pay History",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit#gid=0"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -816,
        768
      ],
      "id": "9ca290b6-efa1-4e2e-9e14-629d7581067b",
      "name": "Get row(s) in sheet1",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// 1. \u0e23\u0e31\u0e1a\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\nconst rows = $input.all();\n\nlet totalMonthlyIncome = 0;  // \u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a\u0e17\u0e31\u0e49\u0e07\u0e40\u0e14\u0e37\u0e2d\u0e19\nlet totalMonthlyExpense = 0; // [\u0e40\u0e1e\u0e34\u0e48\u0e21] \u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22\u0e17\u0e31\u0e49\u0e07\u0e40\u0e14\u0e37\u0e2d\u0e19\nlet totalWeeklyExpense = 0;  // \u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22 7 \u0e27\u0e31\u0e19\nlet countWeekly = 0;         // \u0e19\u0e31\u0e1a\u0e08\u0e33\u0e19\u0e27\u0e19\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23 7 \u0e27\u0e31\u0e19\nlet catMap = {};             // \u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22 7 \u0e27\u0e31\u0e19\n\n// 2. \u0e01\u0e33\u0e2b\u0e19\u0e14\u0e40\u0e27\u0e25\u0e32\u0e1b\u0e31\u0e08\u0e08\u0e38\u0e1a\u0e31\u0e19\nconst today = new Date();\ntoday.setHours(23, 59, 59, 999);\n\n// 2.1 \u0e01\u0e33\u0e2b\u0e19\u0e14\u0e0a\u0e48\u0e27\u0e07\u0e40\u0e27\u0e25\u0e32 \"\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\u0e19\u0e35\u0e49\" (7 \u0e27\u0e31\u0e19\u0e22\u0e49\u0e2d\u0e19\u0e2b\u0e25\u0e31\u0e07)\nconst sevenDaysAgo = new Date();\nsevenDaysAgo.setDate(today.getDate() - 7);\nsevenDaysAgo.setHours(0, 0, 0, 0);\n\n// 2.2 \u0e01\u0e33\u0e2b\u0e19\u0e14\u0e0a\u0e48\u0e27\u0e07\u0e40\u0e27\u0e25\u0e32 \"\u0e40\u0e14\u0e37\u0e2d\u0e19\u0e19\u0e35\u0e49\" (\u0e27\u0e31\u0e19\u0e17\u0e35\u0e48 1 \u0e02\u0e2d\u0e07\u0e40\u0e14\u0e37\u0e2d\u0e19\u0e1b\u0e31\u0e08\u0e08\u0e38\u0e1a\u0e31\u0e19)\nconst startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);\nstartOfMonth.setHours(0, 0, 0, 0);\n\n// 3. \u0e27\u0e19\u0e25\u0e39\u0e1b\u0e40\u0e0a\u0e47\u0e04\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\nfor (const item of rows) {\n  const row = item.json;\n  if (row.date) {\n    const rowDate = new Date(row.date);\n    const amount = parseFloat(row.amount) || 0;\n    \n    // \u0e40\u0e0a\u0e47\u0e04\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17 (Income/Expense)\n    const typeRaw = row['transaction_type (income/expense/transfer)'] || row['type'] || row['transaction_type'] || \"\";\n    const type = typeRaw.toString().toLowerCase();\n    const category = row['category'] || '\u0e2d\u0e37\u0e48\u0e19\u0e46';\n\n    // --- Logic 1: \u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e23\u0e30\u0e14\u0e31\u0e1a \"\u0e40\u0e14\u0e37\u0e2d\u0e19\u0e19\u0e35\u0e49\" (\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a + \u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22) ---\n    if (rowDate >= startOfMonth && rowDate <= today) {\n      // 1.1 \u0e04\u0e34\u0e14\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a\n      if (type.includes('income') || type.includes('\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a')) {\n        totalMonthlyIncome += amount;\n      }\n      // 1.2 [\u0e40\u0e1e\u0e34\u0e48\u0e21] \u0e04\u0e34\u0e14\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22\u0e17\u0e31\u0e49\u0e07\u0e40\u0e14\u0e37\u0e2d\u0e19\n      if (type.includes('expense') || type.includes('\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22')) {\n        totalMonthlyExpense += amount;\n      }\n    }\n\n    // --- Logic 2: \u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e23\u0e30\u0e14\u0e31\u0e1a \"\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\u0e19\u0e35\u0e49\" (7 \u0e27\u0e31\u0e19\u0e22\u0e49\u0e2d\u0e19\u0e2b\u0e25\u0e31\u0e07) ---\n    if (rowDate >= sevenDaysAgo && rowDate <= today) {\n      if (type.includes('expense') || type.includes('\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22')) {\n        totalWeeklyExpense += amount;\n        \n        // \u0e40\u0e01\u0e47\u0e1a\u0e22\u0e2d\u0e14\u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48 (\u0e40\u0e09\u0e1e\u0e32\u0e30\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22 week \u0e19\u0e35\u0e49)\n        if (catMap[category]) {\n          catMap[category] += amount;\n        } else {\n          catMap[category] = amount;\n        }\n        countWeekly++;\n      }\n    }\n  }\n}\n\n// 4. \u0e08\u0e31\u0e14\u0e2d\u0e31\u0e19\u0e14\u0e31\u0e1a\u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48 (Top 5 \u0e02\u0e2d\u0e07 Week \u0e19\u0e35\u0e49)\nconst sortedCats = Object.entries(catMap)\n  .sort((a, b) => b[1] - a[1])\n  .slice(0, 5);\n\n// \u0e2a\u0e23\u0e49\u0e32\u0e07\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e2a\u0e23\u0e38\u0e1b Top 5 (\u0e43\u0e0a\u0e49 \\\\n \u0e41\u0e17\u0e19 \\n \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e44\u0e21\u0e48\u0e43\u0e2b\u0e49 JSON \u0e1e\u0e31\u0e07)\nlet topCatText = \"\";\nif (sortedCats.length > 0) {\n    topCatText = sortedCats.map((item, index) => {\n        const safeName = item[0].replace(/\"/g, '\\\\\"');\n        return `${index + 1}. ${safeName}: -${item[1].toLocaleString()} \u0e3f`;\n    }).join(\"\\\\n\");\n} else {\n    topCatText = \"- \u0e44\u0e21\u0e48\u0e21\u0e35\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\u0e19\u0e35\u0e49 -\";\n}\n\n// [\u0e41\u0e01\u0e49] \u0e04\u0e33\u0e19\u0e27\u0e13\u0e04\u0e07\u0e40\u0e2b\u0e25\u0e37\u0e2d\u0e08\u0e23\u0e34\u0e07 (\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a\u0e17\u0e31\u0e49\u0e07\u0e40\u0e14\u0e37\u0e2d\u0e19 - \u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22\u0e17\u0e31\u0e49\u0e07\u0e40\u0e14\u0e37\u0e2d\u0e19)\nconst netBalance = totalMonthlyIncome - totalMonthlyExpense;\n\n// 5. \u0e2a\u0e48\u0e07\u0e1c\u0e25\u0e25\u0e31\u0e1e\u0e18\u0e4c\nreturn {\n  week_range: sevenDaysAgo.toLocaleDateString('th-TH') + ' - ' + today.toLocaleDateString('th-TH'),\n  month_name: startOfMonth.toLocaleDateString('th-TH', { month: 'long', year: 'numeric' }),\n  \n  total_income: totalMonthlyIncome,\n  total_expense: totalWeeklyExpense,\n  net_balance: netBalance,\n  tx_count: countWeekly,\n\n  weekly_income_formatted: \"0\", \n  weekly_expense_formatted: totalWeeklyExpense.toLocaleString(),\n  \n  monthly_income_formatted: totalMonthlyIncome.toLocaleString(),\n  monthly_expense_formatted: totalMonthlyExpense.toLocaleString(), // [\u0e41\u0e01\u0e49] \u0e43\u0e0a\u0e49\u0e15\u0e31\u0e27\u0e40\u0e25\u0e02\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22\u0e17\u0e31\u0e49\u0e07\u0e40\u0e14\u0e37\u0e2d\u0e19\u0e08\u0e23\u0e34\u0e07\u0e46 \u0e41\u0e25\u0e49\u0e27\n  \n  balance_formatted: netBalance.toLocaleString(),\n  top_categories_text: topCatText\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -608,
        768
      ],
      "id": "5c6578db-6fb4-403e-9d8f-cd4abbd56986",
      "name": "Code in JavaScript1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.line.me/v2/bot/message/push",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"to\": \"U7eb0969b9040dc2ec9d4a19e89e347f4\",\n  \"messages\": [\n    {\n      \"type\": \"flex\",\n      \"altText\": \"\ud83d\udcca \u0e2a\u0e23\u0e38\u0e1b\u0e23\u0e32\u0e22\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\",\n      \"contents\": {\n        \"type\": \"bubble\",\n        \"hero\": {\n          \"type\": \"box\",\n          \"layout\": \"vertical\",\n          \"contents\": [\n            {\n              \"type\": \"text\",\n              \"text\": \"\ud83d\udcca \u0e2a\u0e23\u0e38\u0e1b\u0e23\u0e32\u0e22\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\",\n              \"weight\": \"bold\",\n              \"size\": \"xl\",\n              \"color\": \"#ffffff\"\n            },\n            {\n              \"type\": \"text\",\n              \"text\": \"{{ $json.week_range }}\",\n              \"size\": \"sm\",\n              \"color\": \"#ffffff\",\n              \"margin\": \"md\"\n            }\n          ],\n          \"backgroundColor\": \"#8B5CF6\",\n          \"paddingAll\": \"20px\"\n        },\n        \"body\": {\n          \"type\": \"box\",\n          \"layout\": \"vertical\",\n          \"contents\": [\n            {\n              \"type\": \"text\",\n              \"text\": \"\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\u0e19\u0e35\u0e49 (7 \u0e27\u0e31\u0e19)\",\n              \"weight\": \"bold\",\n              \"size\": \"sm\",\n              \"color\": \"#8B5CF6\"\n            },\n            {\n              \"type\": \"box\",\n              \"layout\": \"horizontal\",\n              \"contents\": [\n                {\n                  \"type\": \"text\",\n                  \"text\": \"\ud83d\udcb0 \u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a\",\n                  \"size\": \"sm\",\n                  \"color\": \"#555555\",\n                  \"flex\": 0\n                },\n                {\n                  \"type\": \"text\",\n                  \"text\": \"\u0e3f{{ $json.weekly_income_formatted }}\",\n                  \"size\": \"sm\",\n                  \"color\": \"#10b981\",\n                  \"align\": \"end\",\n                  \"weight\": \"bold\"\n                }\n              ],\n              \"margin\": \"sm\"\n            },\n            {\n              \"type\": \"box\",\n              \"layout\": \"horizontal\",\n              \"contents\": [\n                {\n                  \"type\": \"text\",\n                  \"text\": \"\ud83d\udcb8 \u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22\",\n                  \"size\": \"sm\",\n                  \"color\": \"#555555\",\n                  \"flex\": 0\n                },\n                {\n                  \"type\": \"text\",\n                  \"text\": \"\u0e3f{{ $json.weekly_expense_formatted }}\",\n                  \"size\": \"sm\",\n                  \"color\": \"#ef4444\",\n                  \"align\": \"end\",\n                  \"weight\": \"bold\"\n                }\n              ],\n              \"margin\": \"sm\"\n            },\n            {\n              \"type\": \"separator\",\n              \"margin\": \"lg\"\n            },\n            {\n              \"type\": \"text\",\n              \"text\": \"\u0e22\u0e2d\u0e14\u0e04\u0e07\u0e40\u0e2b\u0e25\u0e37\u0e2d\u0e08\u0e23\u0e34\u0e07 ({{ $json.month_name }})\",\n              \"weight\": \"bold\",\n              \"size\": \"sm\",\n              \"color\": \"#8B5CF6\",\n              \"margin\": \"lg\"\n            },\n            {\n              \"type\": \"box\",\n              \"layout\": \"horizontal\",\n              \"contents\": [\n                {\n                  \"type\": \"text\",\n                  \"text\": \"\ud83d\udcb5 \u0e04\u0e07\u0e40\u0e2b\u0e25\u0e37\u0e2d\",\n                  \"size\": \"md\",\n                  \"color\": \"#111111\",\n                  \"flex\": 0,\n                  \"weight\": \"bold\"\n                },\n                {\n                  \"type\": \"text\",\n                  \"text\": \"\u0e3f{{ $json.balance_formatted }}\",\n                  \"size\": \"md\",\n                  \"color\": \"#8B5CF6\",\n                  \"align\": \"end\",\n                  \"weight\": \"bold\"\n                }\n              ],\n              \"margin\": \"sm\"\n            },\n            {\n              \"type\": \"text\",\n              \"text\": \"\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a \u0e3f{{ $json.monthly_income_formatted }} - \u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22 \u0e3f{{ $json.monthly_expense_formatted }}\",\n              \"size\": \"xxs\",\n              \"color\": \"#aaaaaa\",\n              \"align\": \"end\",\n              \"margin\": \"xs\"\n            },\n            {\n              \"type\": \"separator\",\n              \"margin\": \"lg\"\n            },\n            {\n              \"type\": \"box\",\n              \"layout\": \"vertical\",\n              \"contents\": [\n                {\n                  \"type\": \"text\",\n                  \"text\": \"\ud83c\udfc6 Top 3 \u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48 (\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\u0e19\u0e35\u0e49)\",\n                  \"weight\": \"bold\",\n                  \"size\": \"sm\",\n                  \"color\": \"#8B5CF6\"\n                },\n                {\n                  \"type\": \"text\",\n                  \"text\": \"{{ $json.top_categories_text }}\",\n                  \"size\": \"xs\",\n                  \"color\": \"#555555\",\n                  \"margin\": \"sm\",\n                  \"wrap\": true\n                }\n              ],\n              \"margin\": \"lg\"\n            }\n          ]\n        },\n        \"footer\": {\n          \"type\": \"box\",\n          \"layout\": \"vertical\",\n          \"contents\": [\n            {\n              \"type\": \"text\",\n              \"text\": \"\ud83d\udcdd \u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\u0e19\u0e35\u0e49: {{ $json.tx_count }} \u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\",\n              \"size\": \"xs\",\n              \"color\": \"#aaaaaa\",\n              \"align\": \"center\"\n            },\n            {\n              \"type\": \"text\",\n              \"text\": \"\u2728 \u0e21\u0e35\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\u0e17\u0e35\u0e48\u0e14\u0e35!\",\n              \"size\": \"xs\",\n              \"color\": \"#aaaaaa\",\n              \"align\": \"center\",\n              \"margin\": \"sm\"\n            }\n          ]\n        }\n      },\n      \"quickReply\": {\n        \"items\": [\n          {\n            \"type\": \"action\",\n            \"action\": {\n              \"type\": \"message\",\n              \"label\": \"\ud83d\udcc5 \u0e2a\u0e23\u0e38\u0e1b\u0e27\u0e31\u0e19\u0e19\u0e35\u0e49\",\n              \"text\": \"\u0e2a\u0e23\u0e38\u0e1b\u0e27\u0e31\u0e19\u0e19\u0e35\u0e49\"\n            }\n          },\n          {\n            \"type\": \"action\",\n            \"action\": {\n              \"type\": \"message\",\n              \"label\": \"\ud83d\udcc5 \u0e2a\u0e23\u0e38\u0e1b\u0e40\u0e21\u0e37\u0e48\u0e2d\u0e27\u0e32\u0e19\",\n              \"text\": \"\u0e2a\u0e23\u0e38\u0e1b\u0e40\u0e21\u0e37\u0e48\u0e2d\u0e27\u0e32\u0e19\"\n            }\n          },\n          {\n            \"type\": \"action\",\n            \"action\": {\n              \"type\": \"message\",\n              \"label\": \"\ud83d\udcca \u0e2a\u0e23\u0e38\u0e1b 7 \u0e27\u0e31\u0e19\",\n              \"text\": \"\u0e2a\u0e23\u0e38\u0e1b 7 \u0e27\u0e31\u0e19\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -400,
        768
      ],
      "id": "516a7d7c-72c1-4ebb-8371-503f8bfd4da9",
      "name": "HTTP Request3",
      "retryOnFail": true,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "742fca07-264f-4c67-af8d-1cfccdf5a0a4",
              "leftValue": "={{ $json.body.events[0].message.text }}",
              "rightValue": "\u0e2a\u0e23\u0e38\u0e1b\u0e27\u0e31\u0e19\u0e19\u0e35\u0e49",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -944,
        -384
      ],
      "id": "509c80fb-7bea-449a-94bf-a9b8d32b7ba0",
      "name": "If1"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds",
          "mode": "list",
          "cachedResultName": "Pay History",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit#gid=0"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -656,
        -624
      ],
      "id": "02b4c7eb-90d5-4014-ae8a-a000d1d00cf0",
      "name": "Get row(s) in sheet2",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// \u0e23\u0e31\u0e1a\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\nconst rows = $input.all();\n\nlet todayIncome = 0;\nlet todayExpense = 0;\nlet todayCount = 0;\nlet catMap = {};\n\n// \u0e01\u0e33\u0e2b\u0e19\u0e14\u0e40\u0e27\u0e25\u0e32\u0e27\u0e31\u0e19\u0e19\u0e35\u0e49\nconst today = new Date();\ntoday.setHours(0, 0, 0, 0);\nconst endOfDay = new Date();\nendOfDay.setHours(23, 59, 59, 999);\n\n// \u0e27\u0e19\u0e25\u0e39\u0e1b\u0e40\u0e0a\u0e47\u0e04\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\nfor (const item of rows) {\n  const row = item.json;\n  if (row.date) {\n    const rowDate = new Date(row.date);\n    const amount = parseFloat(row.amount) || 0;\n    \n    const typeRaw = row['transaction_type (income/expense/transfer)'] || row['type'] || row['transaction_type'] || \"\";\n    const type = typeRaw.toString().toLowerCase();\n    const category = row['category'] || '\u0e2d\u0e37\u0e48\u0e19\u0e46';\n\n    // \u0e40\u0e09\u0e1e\u0e32\u0e30\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e27\u0e31\u0e19\u0e19\u0e35\u0e49\n    if (rowDate >= today && rowDate <= endOfDay) {\n      todayCount++;\n      \n      if (type.includes('income') || type.includes('\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a')) {\n        todayIncome += amount;\n      }\n      if (type.includes('expense') || type.includes('\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22')) {\n        todayExpense += amount;\n        \n        if (catMap[category]) {\n          catMap[category] += amount;\n        } else {\n          catMap[category] = amount;\n        }\n      }\n    }\n  }\n}\n\n// \u0e08\u0e31\u0e14\u0e2d\u0e31\u0e19\u0e14\u0e31\u0e1a\u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48\nconst sortedCats = Object.entries(catMap)\n  .sort((a, b) => b[1] - a[1])\n  .slice(0, 3);\n\nlet topCatText = '';\nif (sortedCats.length > 0) {\n  sortedCats.forEach((item, index) => {\n    topCatText += `${index + 1}. ${item[0]}: \u0e3f${item[1].toLocaleString('th-TH')}\\n`;\n  });\n} else {\n  topCatText = '- \u0e22\u0e31\u0e07\u0e44\u0e21\u0e48\u0e21\u0e35\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e27\u0e31\u0e19\u0e19\u0e35\u0e49 -';\n}\n\nconst balance = todayIncome - todayExpense;\n\nconst formatNum = (num) => {\n  return num.toLocaleString('th-TH', {minimumFractionDigits: 0, maximumFractionDigits: 2});\n};\n\nconst formatDate = (date) => {\n  return date.toLocaleDateString('th-TH', { day: '2-digit', month: 'long', year: 'numeric' });\n};\n\n// \u2b50 \u0e2a\u0e33\u0e04\u0e31\u0e0d: \u0e04\u0e37\u0e19\u0e04\u0e48\u0e32\u0e41\u0e1a\u0e1a\u0e2d\u0e32\u0e23\u0e4c\u0e40\u0e23\u0e22\u0e4c\u0e1e\u0e23\u0e49\u0e2d\u0e21 pairedItem\nreturn [\n  {\n    json: {\n      date: formatDate(today),\n      income: todayIncome,\n      expense: todayExpense,\n      balance: balance,\n      count: todayCount,\n      top_categories: topCatText.trim(),\n      income_formatted: formatNum(todayIncome),\n      expense_formatted: formatNum(todayExpense),\n      balance_formatted: formatNum(balance)\n    },\n    pairedItem: 0\n  }\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -416,
        -624
      ],
      "id": "d1a8a5ad-e499-4998-92ad-556f65aacb70",
      "name": "Code in JavaScript2"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.line.me/v2/bot/message/reply",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "=application/json; charset=UTF-8"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "=json",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "jsonBody": "={\n  \"replyToken\": \"{{ $('Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"flex\",\n      \"altText\": \"\ud83d\udcca \u0e2a\u0e23\u0e38\u0e1b\u0e23\u0e32\u0e22\u0e07\u0e32\u0e19\u0e01\u0e32\u0e23\u0e40\u0e07\u0e34\u0e19\u0e1b\u0e23\u0e30\u0e08\u0e33\u0e27\u0e31\u0e19\",\n      \"contents\": {\n        \"type\": \"bubble\",\n        \"size\": \"mega\",\n        \"header\": {\n          \"type\": \"box\",\n          \"layout\": \"vertical\",\n          \"contents\": [\n            {\n              \"type\": \"text\",\n              \"text\": \"FINANCIAL REPORT\",\n              \"color\": \"#ffffffaa\",\n              \"weight\": \"bold\",\n              \"size\": \"xxs\"\n            },\n            {\n              \"type\": \"text\",\n              \"text\": \"{{ $('set1').item.json.date }}\",\n              \"color\": \"#ffffff\",\n              \"weight\": \"bold\",\n              \"size\": \"xl\",\n              \"flex\": 1\n            }\n          ],\n          \"backgroundColor\": \"#27AE60\",\n          \"paddingAll\": \"20px\"\n        },\n        \"body\": {\n          \"type\": \"box\",\n          \"layout\": \"vertical\",\n          \"contents\": [\n            {\n              \"type\": \"text\",\n              \"text\": \"\u0e22\u0e2d\u0e14\u0e04\u0e07\u0e40\u0e2b\u0e25\u0e37\u0e2d\u0e2a\u0e38\u0e17\u0e18\u0e34 (Net Balance)\",\n              \"color\": \"#8C8C8C\",\n              \"size\": \"xs\",\n              \"align\": \"center\",\n              \"weight\": \"bold\"\n            },\n            {\n              \"type\": \"text\",\n              \"text\": \"{{ $('set1').item.json.balance }}\",\n              \"weight\": \"bold\",\n              \"size\": \"4xl\",\n              \"align\": \"center\",\n              \"color\": \"#2c3e50\",\n              \"margin\": \"sm\"\n            },\n            {\n              \"type\": \"separator\",\n              \"margin\": \"xl\"\n            },\n            {\n              \"type\": \"box\",\n              \"layout\": \"horizontal\",\n              \"margin\": \"xl\",\n              \"contents\": [\n                {\n                  \"type\": \"box\",\n                  \"layout\": \"vertical\",\n                  \"contents\": [\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a\",\n                      \"size\": \"xs\",\n                      \"color\": \"#8C8C8C\"\n                    },\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"{{ $('set1').item.json.income }}\",\n                      \"size\": \"lg\",\n                      \"weight\": \"bold\",\n                      \"color\": \"#27AE60\"\n                    }\n                  ],\n                  \"alignItems\": \"center\"\n                },\n                {\n                  \"type\": \"separator\",\n                  \"color\": \"#E0E0E0\"\n                },\n                {\n                  \"type\": \"box\",\n                  \"layout\": \"vertical\",\n                  \"contents\": [\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22\",\n                      \"size\": \"xs\",\n                      \"color\": \"#8C8C8C\"\n                    },\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"{{ $('set1').item.json.expense }}\",\n                      \"size\": \"lg\",\n                      \"weight\": \"bold\",\n                      \"color\": \"#EB5757\"\n                    }\n                  ],\n                  \"alignItems\": \"center\"\n                }\n              ]\n            },\n            {\n              \"type\": \"separator\",\n              \"margin\": \"xl\"\n            },\n            {\n              \"type\": \"box\",\n              \"layout\": \"vertical\",\n              \"margin\": \"lg\",\n              \"contents\": [\n                {\n                  \"type\": \"text\",\n                  \"text\": \"\ud83d\udccc \u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48\u0e22\u0e2d\u0e14\u0e19\u0e34\u0e22\u0e21\",\n                  \"size\": \"sm\",\n                  \"weight\": \"bold\",\n                  \"color\": \"#333333\",\n                  \"margin\": \"sm\"\n                },\n                {\n                  \"type\": \"box\",\n                  \"layout\": \"vertical\",\n                  \"margin\": \"md\",\n                  \"backgroundColor\": \"#F8F9FA\",\n                  \"cornerRadius\": \"md\",\n                  \"paddingAll\": \"md\",\n                  \"contents\": [\n                    {\n                      \"type\": \"text\",\n                      \"text\": \"{{ JSON.stringify($('set1').item.json.top_categories).slice(1, -1) }}\",\n                      \"size\": \"xs\",\n                      \"color\": \"#555555\",\n                      \"wrap\": true,\n                      \"lineSpacing\": \"4px\"\n                    }\n                  ]\n                }\n              ]\n            }\n          ]\n        }\n      }\n    }\n  ]\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        80,
        -624
      ],
      "id": "28c33c96-438d-42ef-b867-1a8da77771ac",
      "name": "HTTP Request4",
      "retryOnFail": true,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "0136b485-4002-4737-b823-022ebbe03aaa",
              "leftValue": "={{ $json.body.events[0].message.text }}",
              "rightValue": "\u0e2a\u0e23\u0e38\u0e1b\u0e40\u0e21\u0e37\u0e48\u0e2d\u0e27\u0e32\u0e19",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -720,
        -368
      ],
      "id": "9903d0f9-016c-4a46-b9b8-fd179b60f5e3",
      "name": "If2"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds",
          "mode": "list",
          "cachedResultName": "Pay History",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit#gid=0"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -416,
        -384
      ],
      "id": "4883e67c-660b-4a4f-9b00-d27abcd07ff1",
      "name": "Get row(s) in sheet3",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// \u0e23\u0e31\u0e1a\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\nconst rows = $input.all();\n\nlet yesterdayIncome = 0;\nlet yesterdayExpense = 0;\nlet yesterdayCount = 0;\nlet catMap = {};\n\n// \u0e01\u0e33\u0e2b\u0e19\u0e14\u0e40\u0e27\u0e25\u0e32\u0e40\u0e21\u0e37\u0e48\u0e2d\u0e27\u0e32\u0e19\nconst yesterday = new Date();\nyesterday.setDate(yesterday.getDate() - 1);\nyesterday.setHours(0, 0, 0, 0);\nconst endOfYesterday = new Date(yesterday);\nendOfYesterday.setHours(23, 59, 59, 999);\n\n// \u0e27\u0e19\u0e25\u0e39\u0e1b\u0e40\u0e0a\u0e47\u0e04\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\nfor (const item of rows) {\n  const row = item.json;\n  if (row.date) {\n    const rowDate = new Date(row.date);\n    const amount = parseFloat(row.amount) || 0;\n    \n    const typeRaw = row['transaction_type (income/expense/transfer)'] || row['type'] || row['transaction_type'] || \"\";\n    const type = typeRaw.toString().toLowerCase();\n    const category = row['category'] || '\u0e2d\u0e37\u0e48\u0e19\u0e46';\n\n    // \u0e40\u0e09\u0e1e\u0e32\u0e30\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e40\u0e21\u0e37\u0e48\u0e2d\u0e27\u0e32\u0e19\n    if (rowDate >= yesterday && rowDate <= endOfYesterday) {\n      yesterdayCount++;\n      \n      if (type.includes('income') || type.includes('\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a')) {\n        yesterdayIncome += amount;\n      }\n      if (type.includes('expense') || type.includes('\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22')) {\n        yesterdayExpense += amount;\n        \n        if (catMap[category]) {\n          catMap[category] += amount;\n        } else {\n          catMap[category] = amount;\n        }\n      }\n    }\n  }\n}\n\n// \u0e08\u0e31\u0e14\u0e2d\u0e31\u0e19\u0e14\u0e31\u0e1a\u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48\nconst sortedCats = Object.entries(catMap)\n  .sort((a, b) => b[1] - a[1])\n  .slice(0, 3);\n\nlet topCatText = '';\nif (sortedCats.length > 0) {\n  sortedCats.forEach((item, index) => {\n    topCatText += `${index + 1}. ${item[0]}: \u0e3f${item[1].toLocaleString('th-TH')}\\n`;\n  });\n} else {\n  topCatText = '- \u0e44\u0e21\u0e48\u0e21\u0e35\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e40\u0e21\u0e37\u0e48\u0e2d\u0e27\u0e32\u0e19 -';\n}\n\nconst balance = yesterdayIncome - yesterdayExpense;\n\nconst formatNum = (num) => {\n  return num.toLocaleString('th-TH', {minimumFractionDigits: 0, maximumFractionDigits: 2});\n};\n\nconst formatDate = (date) => {\n  return date.toLocaleDateString('th-TH', { day: '2-digit', month: 'long', year: 'numeric' });\n};\n\nreturn {\n  date: formatDate(yesterday),\n  income: yesterdayIncome,\n  expense: yesterdayExpense,\n  balance: balance,\n  count: yesterdayCount,\n  top_categories: topCatText.trim(),\n  income_formatted: formatNum(yesterdayIncome),\n  expense_formatted: formatNum(yesterdayExpense),\n  balance_formatted: formatNum(balance)\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -192,
        -384
      ],
      "id": "5790f64b-f6c7-4eb3-83eb-fa9f0e540bae",
      "name": "Code in JavaScript3"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "432a2823-a559-4a44-a3c8-7bf02ada8583",
              "leftValue": "={{ $json.body.events[0].message.text }}",
              "rightValue": "\u0e2a\u0e23\u0e38\u0e1b 7 \u0e27\u0e31\u0e19",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -544,
        -32
      ],
      "id": "7fa979e9-9e58-45e9-8332-6f3d89558016",
      "name": "If3"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds",
          "mode": "list",
          "cachedResultName": "Pay History",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ptHPEg2d_19vbecMjzZga2ETJu3ARfuS6LLomYhyIds/edit#gid=0"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -304,
        -144
      ],
      "id": "12f73473-99df-44d2-84aa-d59399661d1d",
      "name": "Get row(s) in sheet4",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// \u0e23\u0e31\u0e1a\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e08\u0e32\u0e01 Sheet\nconst rows = $input.all();\n\nlet weeklyIncome = 0;\nlet weeklyExpense = 0;\nlet catMap = {};\nlet count = 0;\n\n// \u0e01\u0e33\u0e2b\u0e19\u0e14\u0e0a\u0e48\u0e27\u0e07\u0e40\u0e27\u0e25\u0e32 (7 \u0e27\u0e31\u0e19\u0e22\u0e49\u0e2d\u0e19\u0e2b\u0e25\u0e31\u0e07)\nconst today = new Date();\nconst sevenDaysAgo = new Date();\nsevenDaysAgo.setDate(today.getDate() - 7);\nsevenDaysAgo.setHours(0, 0, 0, 0);\n\n// \u0e27\u0e19\u0e25\u0e39\u0e1b\u0e04\u0e33\u0e19\u0e27\u0e13\nfor (const item of rows) {\n  const row = item.json;\n  if (row.date) {\n    const rowDate = new Date(row.date);\n    // \u0e41\u0e1b\u0e25\u0e07\u0e04\u0e48\u0e32\u0e40\u0e07\u0e34\u0e19\u0e40\u0e1b\u0e47\u0e19\u0e15\u0e31\u0e27\u0e40\u0e25\u0e02\n    const amount = parseFloat(row.amount) || 0;\n    \n    // \u0e15\u0e23\u0e27\u0e08\u0e2a\u0e2d\u0e1a\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\n    const typeRaw = row['transaction_type (income/expense/transfer)'] || row['type'] || \"\";\n    const type = typeRaw.toString().toLowerCase();\n    const category = row['category'] || '\u0e2d\u0e37\u0e48\u0e19\u0e46';\n\n    // \u0e40\u0e07\u0e37\u0e48\u0e2d\u0e19\u0e44\u0e02: \u0e15\u0e49\u0e2d\u0e07\u0e40\u0e1b\u0e47\u0e19\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e43\u0e19 7 \u0e27\u0e31\u0e19\u0e25\u0e48\u0e32\u0e2a\u0e38\u0e14\n    if (rowDate >= sevenDaysAgo && rowDate <= today) {\n      count++;\n      \n      if (type.includes('income') || type.includes('\u0e23\u0e32\u0e22\u0e23\u0e31\u0e1a')) {\n        weeklyIncome += amount;\n      }\n      else if (type.includes('expense') || type.includes('\u0e23\u0e32\u0e22\u0e08\u0e48\u0e32\u0e22')) {\n        weeklyExpense += amount;\n        // \u0e40\u0e01\u0e47\u0e1a\u0e2a\u0e16\u0e34\u0e15\u0e34\u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48\n        if (catMap[category]) {\n          catMap[category] += amount;\n        } else {\n          catMap[category] = amount;\n        }\n      }\n    }\n  }\n}\n\n// \u0e08\u0e31\u0e14\u0e2d\u0e31\u0e19\u0e14\u0e31\u0e1a Top 3 \u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48\nconst sortedCats = Object.entries(catMap)\n  .sort((a, b) => b[1] - a[1])\n  .slice(0, 3);\n\nlet topCatText = '';\nif (sortedCats.length > 0) {\n  sortedCats.forEach((item, index) => {\n    topCatText += `${index + 1}. ${item[0]}: \u0e3f${item[1].toLocaleString('th-TH')}\\n`;\n  });\n} else {\n  topCatText = '- \u0e44\u0e21\u0e48\u0e21\u0e35\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23 -';\n}\n\n// \u0e04\u0e33\u0e19\u0e27\u0e13\u0e04\u0e07\u0e40\u0e2b\u0e25\u0e37\u0e2d\nconst balance = weeklyIncome - weeklyExpense;\n\n// \u0e1f\u0e2d\u0e23\u0e4c\u0e41\u0e21\u0e15\u0e15\u0e31\u0e27\u0e40\u0e25\u0e02\u0e41\u0e25\u0e30\u0e27\u0e31\u0e19\u0e17\u0e35\u0e48\nconst formatNum = (num) => num.toLocaleString('th-TH', {minimumFractionDigits: 0, maximumFractionDigits: 2});\nconst formatDate = (d) => d.toLocaleDateString('th-TH', { day: '2-digit', month: 'short' });\nconst weekRange = `${formatDate(sevenDaysAgo)} - ${formatDate(today)}`;\n\n// \u0e2a\u0e48\u0e07\u0e04\u0e48\u0e32\u0e2d\u0e2d\u0e01\u0e44\u0e1b\nreturn {\n  week_range: weekRange,\n  weekly_income_formatted: formatNum(weeklyIncome),\n  weekly_expense_formatted: formatNum(weeklyExpense),\n  balance_formatted: formatNum(balance),\n  top_categories: topCatText.trim(),\n  count: count,\n  month_name: \"7 \u0e27\u0e31\u0e19\u0e25\u0e48\u0e32\u0e2a\u0e38\u0e14\" // \u0e43\u0e0a\u0e49\u0e41\u0e17\u0e19\u0e0a\u0e37\u0e48\u0e2d\u0e40\u0e14\u0e37\u0e2d\u0e19\u0e43\u0e19 Template \u0e40\u0e14\u0e34\u0e21\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -80,
        -144
      ],
      "id": "2744d8b9-1fb3-40e1-9305-f50e2fa22543",
      "name": "Code in JavaScript4"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "69eafb91-cc9a-41c3-88c0-6de07fd0700f",
              "name": "webhookUserId",
              "value": "={{ $('Webhook').item.json.body.events[0].source.userId }}",
              "type": "string"
            },
            {
              "id": "84a6483b-ee6c-4f54-ae8a-9fe03f8cf02f",
              "name": "date",
              "value": "={{ $('Code in JavaScript2').first().json.date }}",
              "type": "string"
            },
            {
              "id": "a4780242-a652-463d-a381-7275e9525d18",
              "name": "income",
              "value": "={{ $('Code in JavaScript2').first().json.income_formatted }}",
              "type": "string"
            },
            {
              "id": "d14aa5d8-532b-405f-8232-0387e5dc9e69",
              "name": "expense",
              "value": "={{ $('Code in JavaScript2').first().json.expense_formatted }}",
              "type": "string"
            },
            {
              "id": "726ea174-c217-4efe-9946-b6022bde8ced",
              "name": "balance",
              "value": "={{ $('Code in JavaScript2').first().json.balance_formatted }}",
              "type": "string"
            },
            {
              "id": "210653d7-1e7f-4f3c-b4c8-a9800b6e1079",
              "name": "count",
              "value": "={{ $('Code in JavaScript2').first().json.count }}",
              "type": "string"
            },
            {
              "id": "c9471938-cf99-4328-a4f7-ef18e93c7732",
              "name": "top_categories",
              "value": "={{ $('Code in JavaScript2').first().json.top_categories }}\n",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -208,
        -624
      ],
      "id": "ff85a5ed-41db-4064-9118-5a826ce0b001",
      "name": "set1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.line.me/v2/bot/message/reply",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "=application/json; charset=UTF-8"
            }
          ]
    

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

PAY. Uses agent, lmChatGoogleGemini, httpRequest, googleSheets. Webhook trigger; 35 nodes.

Source: https://github.com/Yterayut/N8N-Project/blob/86c7ac7b91736b76c6cf8611b38704cb6c29e0c6/workflow_patches/server-export2/workflows/rZjkLEVp0IM9xKfh.json — 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

LineOA. Uses httpRequest, agent, lmChatGoogleGemini, outputParserStructured. Webhook trigger; 69 nodes.

HTTP Request, Agent, Google Gemini Chat +3
AI & RAG

Resume Screening & Behavioral Interviews with Gemini, Elevenlabs, & Notion ATS copy. Uses outputParserStructured, chainLlm, googleDrive, stickyNote. Webhook trigger; 67 nodes.

Output Parser Structured, Chain Llm, Google Drive +9
AI & RAG

Candidate Engagement | Resume Screening | AI Voice Interviews | Applicant Insights

Output Parser Structured, Chain Llm, Google Drive +9
AI & RAG

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

Supabase, Gmail, Form Trigger +13