← Back to blog
April 15, 20262 min read

Reading Bank SMS in Real Time with Flutter

FlutterDartNode.js

Reading Bank SMS in Real Time with Flutter

The best expense tracker is the one you don't have to think about. Every manual-entry app I tried lasted two days before I stopped logging things.

So I built one that does the logging for me by reading my bank's transaction SMS messages.

How Indian Bank SMS Works

Indian banks send a standardized SMS for every debit/credit transaction. The format varies by bank, but they all contain:

  • Transaction type (debited/credited)
  • Amount
  • Account last 4 digits
  • Available balance

A regex that covers the common patterns catches ~90% of real-world messages.

The Flutter Side

Flutter's telephony package (now flutter_telephony) provides both one-time SMS inbox access and a background listener for incoming messages.

final telephony = Telephony.instance;

// Listen for new messages in the background
telephony.listenIncomingSms(
  onNewMessage: (SmsMessage message) {
    final transaction = SmsParser.parse(message.body ?? '');
    if (transaction != null) {
      expenseRepo.save(transaction);
    }
  },
  listenInBackground: true,
);

The parser runs a series of regexes against the message body. If a match is found, it extracts the amount, type, and merchant hint (often included in UPI messages).

The Node.js Backend

The backend is intentionally thin — it receives parsed transactions from the app, stores them in a simple SQLite database, and exposes a REST API for querying history and aggregates (daily/weekly/monthly totals).

Keeping parsing on-device means the raw SMS content never leaves the phone. Only the structured transaction data hits the server.

Permissions and Trust

This requires READ_SMS and RECEIVE_SMS permissions — high-trust permissions that users are (rightly) cautious about. The app explains exactly why they're needed on first launch, and the privacy policy is explicit: no SMS body is transmitted, only parsed transaction data.

What's Left

The regex coverage isn't 100%. Edge cases like international transfers, EMI messages, and bank-specific formats occasionally slip through. A small corrections UI (where the user can fix a misparse) would make the data more reliable.

Still, for the common case — UPI, debit card, net banking — it works automatically and silently. Exactly what I wanted.