Bank & wallet SMS → money tracker

Forward bank, card and wallet SMS to a webhook; AI logs each payment and income in a sheet.

Medium Saves ~4 hours/month Finance & AdminBy Flowngn team

Works with n8n · Make · Zapier

How it works

Trigger: Webhook — an SMS forwarded from your phone
  1. An SMS-forwarding app on your phone sends bank and wallet SMS to the webhook.
  2. AI reads the SMS: income or expense, amount, currency, merchant and date.
  3. OTPs, ads and balance messages are ignored; transactions go to your Money sheet.

Diagram

trueBank SMSWebhookRead the SMSBasic LLM ChainClaude modelAnthropic Chat Mo…{ }Read transactionCode⑂Is a transaction?IfGAdd to Money sheetGoogle Sheets
Trigger App Logic Code AI

Code

JavaScript used in the Code step — already included in the template below.

Read transaction · Code node
// Mode: Run Once for Each Item
// The AI was asked for JSON like:
// {"is_transaction": true, "type": "debit", "amount": 450, "currency": "EGP", "merchant": "Carrefour", "date": "2026-10-19"}
const match = $json.text.match(/\{[\s\S]*\}/);
const t = match ? JSON.parse(match[0]) : {};
const sms = $("Bank SMS").item.json.body || {};

return {
  json: {
    is_transaction: t.is_transaction === true && Number(t.amount) > 0,
    date: t.date || new Date().toISOString().slice(0, 10),
    type: t.type === "credit" ? "income" : "expense",
    amount: Number(t.amount) || 0,
    currency: t.currency || "EGP",
    merchant: t.merchant || "",
    sender: sms.from || "",
  },
};

Build it

Pick your tool — same automation, step by step.

Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder. AI steps use a Claude model node — swap it for OpenAI, Gemini or Ollama if you prefer.

Workflow JSON
Show code (208 lines)
{
  "name": "Bank & wallet SMS → money tracker (Flowngn)",
  "nodes": [
    {
      "parameters": {
        "content": "## Bank & wallet SMS → money tracker\nForward bank, card and wallet SMS to a webhook; AI logs each payment and income in a sheet.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
        "height": 260,
        "width": 340
      },
      "id": "2de5ab85-b2af-45d3-a2c6-711a884b6523",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -420,
        -60
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "bank-sms",
        "options": {}
      },
      "id": "2de5ab85-b2af-45d3-a2c6-711a884b6523",
      "name": "Bank SMS",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "2de5ab85-b2af-45d3-a2c6-711a884b6523"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=This is a bank, card or wallet SMS (often Arabic or English). Reply ONLY with JSON: {\"is_transaction\": true|false, \"type\": \"debit\"|\"credit\", \"amount\": number, \"currency\": \"EGP\", \"merchant\": \"...\", \"date\": \"YYYY-MM-DD\"}. OTP codes, ads and balance-only messages are not transactions.\n\nSMS: {{ $json.body.text }}"
      },
      "id": "2de5ab85-b2af-45d3-a2c6-711a884b6523",
      "name": "Read the SMS",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.5,
      "position": [
        300,
        0
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-5",
          "mode": "list"
        },
        "options": {}
      },
      "id": "2de5ab85-b2af-45d3-a2c6-711a884b6523",
      "name": "Claude model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        300,
        220
      ]
    },
    {
      "parameters": {
        "jsCode": "// Mode: Run Once for Each Item\n// The AI was asked for JSON like:\n// {\"is_transaction\": true, \"type\": \"debit\", \"amount\": 450, \"currency\": \"EGP\", \"merchant\": \"Carrefour\", \"date\": \"2026-10-19\"}\nconst match = $json.text.match(/\\{[\\s\\S]*\\}/);\nconst t = match ? JSON.parse(match[0]) : {};\nconst sms = $(\"Bank SMS\").item.json.body || {};\n\nreturn {\n  json: {\n    is_transaction: t.is_transaction === true && Number(t.amount) > 0,\n    date: t.date || new Date().toISOString().slice(0, 10),\n    type: t.type === \"credit\" ? \"income\" : \"expense\",\n    amount: Number(t.amount) || 0,\n    currency: t.currency || \"EGP\",\n    merchant: t.merchant || \"\",\n    sender: sms.from || \"\",\n  },\n};",
        "mode": "runOnceForEachItem"
      },
      "id": "2de5ab85-b2af-45d3-a2c6-711a884b6523",
      "name": "Read transaction",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        600,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.is_transaction }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "2de5ab85-b2af-45d3-a2c6-711a884b6523",
      "name": "Is a transaction?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        900,
        0
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Money",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {}
        },
        "options": {}
      },
      "id": "2de5ab85-b2af-45d3-a2c6-711a884b6523",
      "name": "Add to Money sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1200,
        0
      ]
    }
  ],
  "connections": {
    "Bank SMS": {
      "main": [
        [
          {
            "node": "Read the SMS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read the SMS": {
      "main": [
        [
          {
            "node": "Read transaction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read transaction": {
      "main": [
        [
          {
            "node": "Is a transaction?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is a transaction?": {
      "main": [
        [
          {
            "node": "Add to Money sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude model": {
      "ai_languageModel": [
        [
          {
            "node": "Read the SMS",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Open in the playground to edit →

Tips

  • Use an SMS forwarder app that filters by sender (your bank's name) — never forward OTP codes anywhere.
  • Send only to your own private webhook URL.

Related flows