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.