Inspiration

In Pakistan, a khata is the notebook where shopkeepers and families write down who spent what. Most people have stopped keeping one, but the need is still there. And the data already exists: every bank sends an SMS or email for every single transaction.

Every expense tracker we tried made us type those transactions in by hand. That lasts about a week, and then the app gets abandoned. So we asked ourselves: what if the tracker read the alerts our banks already send us, and did all the typing itself?

That idea became KhataBot.

What it does

KhataBot is an automatic expense trackerer that it keeps itself up to date.

  • Bank SMS ingestion: it picks up transa SMS and turns them into categorizedtransactions.
  • Bank email ingestion: every user gets a private forwarding inbox. You set up a filter in Gmail or Outlook so that only bank alerts are forwarded, and those alerts are logged too.
  • AI categorization: an LLM pulls out the amount, merchant, account, and direction (in or out), then picks a category and subcategory.
  • Add with AI: type or say something l 800 for lunch" and it creates both entries.
  • Net worth, not just spending: it tracks accounts, cash, loans, and liabilities:

$$ \text{Net worth} = \sum \text{assets} - \

  • Insights: a spending donut by category and subcategory, stacked trends, and views by day, week, month, quarter, year, or a custom range.
  • Transfer detection: when money moves between two of your own accounts, it's recorded as a transfer, not as spending.
  • CSV import/export, multi-currency,e.

How we built it

  • Android app: Kotlin, Jetpack Compose, Material 3, Koin for dependency injection, Room for local storage.
  • Local-first sync: everything is written to Room first, and a sync engine pushes it to the server in the background. The app works fully offline.
  • Backend: Supabase (Postgres, Auth, row-level security) with Deno/TypeScript Edge Functions. parse-sms and inbound-email share one parsing core, and parse-entry handles text and voice entry.
  • AI: LLMs through OpenRouter, with an ordered fallback chain of models stored in a database config table. We can swap models at runtime without shipping an app update.
  • Parsing ladder: a cheap merchant cache and deterministic rules run first, and the LLM runs only when they can't handle the alert. This keeps cost and latency low.
  • Monetization and analytics: RevenueCat for the subscription (with a free tier), PostHog and Firebase for analytics and crash reporting.

Challenges we faced

  • Every bank formats its alerts differently. The same payment might arrive as an SMS and as an email, so we had to deduplicate carefully to avoid counting it twice.
  • Google Play's SMS policy. Our first READ_SMS permission declaration was rejected because we had picked the wrong permitted use. We had to understand the policy properly and resubmit under SMS-based money management before the app was approved.
  • LLM reliability. A free-tier model hit rate limits and silently dropped a real transaction. We fixed this with fallback chains, and now only use models that support strict JSON output.
  • Keeping secrets off the device. We movunctions, so no API key ever ships inside the app.
  • Money math is unforgiving. Getting the signs right for loans, liabilities, opening balances, and transfers took several redesigns.

What we learned

  • Automation beats motivation: people keep using a tracker when it asks nothing of them.
  • An LLM works best as one step in a pipeline, backed by caches, rules, and fallbacks, not as the whole pipeline.
  • Offline-first design and careful sync pay off when the network is unreliable.
  • Platform policy is part of the product, and needs as much care as the code.

What's next

  • End-to-end encryption, so the server stores only ciphertext.
  • PDF bank statement import.
  • An iOS version.
  • Budgets and smarter spending alerts.

KhataBot is live on Google Play today.

Built With

Share this project:

Updates

Submission history