Back to work
FinTech·2026

CashFlow

Split bills, track balances and settle up with friends — plus a shopkeeper khata ledger and an AI collection agent that chases overdue debts across WhatsApp, SMS and email.

Role
Founder & Solo Developer
Year
2026
Category
FinTech
Platform
Mobile App
Stack
React NativeExpoFastAPIMongoDBRedisRazorpayGeminiTwilio
CashFlow mobile app
01

Why I built this

The 'you owe me' conversation is awkward, and paper khata books get lost. CashFlow stops both: friends and roommates split rent and dinners with running per-member balances, and shopkeepers replace their credit ledger with a digital khata that tracks overdue accounts and drafts gentle-but-firm collection messages. A real subscription model — free, Pro ₹149/mo, ₹699/yr, Founder's Pass — makes it a genuinely monetizable product.

02

How it works

Friends, groups and invites

Create typed groups (Trip, Couple, Home, Office, Roommates), import contacts with dedup, or invite via QR code and links. Every group tracks per-member running balances.

Five split methods with server-side validation

Equal (with penny-remainder handling), exact, percentage, shares and personal — validated server-side so amounts always sum to the total. Every change lands in an edit-history log.

One-tap settle-up

Balances are computed across expenses and settlements; settle-up generates a Razorpay UPI payment link with webhook confirmation. Leaving a group is guarded — the app refuses if any balance is non-zero.

Khata ledger with an AI collection agent

Credit/debit entries with running balance, PDF statement export, and a >30-day overdue aging banner. The AI drafts WhatsApp/email reminder messages for human approval before sending.

Monetized subscriptions

A real paywall (free / Pro / Founder's Pass) gated server-side through a single entitlements registry, with a daily lifecycle job that downgrades on expiry and sends D-7/3/1 renewal emails.

03

Key decisions

Every feature check routes through entitlements.py — plans, meters and paywall logic in one registry, with no scattered 'if user.is_pro' checks anywhere else. It made the subscription rollout clean.

Gemini auto-categorizes expenses and drafts collection nudges, with human approval before anything is sent. The AI is an assistant, never an autonomous actor.

Subscriptions use Razorpay orders, settle-ups use UPI payment links — both verified via HMAC webhooks so the ledger only moves on confirmed payments.

04

Backend architecture

FastAPI monolith on MongoDB

Models cover users, friends, groups, expenses (with embedded splits), settlements, khata customers and entries, monthly insights, OTP codes, notifications, billing orders, webhook events and agent logs — all with compound owner_id + created_at indexes and TTL expiry on OTPs.

Webhook-driven money movement

Razorpay orders, subscriptions and UPI payment links all verify HMAC signatures; a webhook_events collection dedupes deliveries so callbacks are at-least-once safe.

AI ops and insight jobs

A monthly-insights job summarizes spending with Gemini, an agent log records every AI draft, and Redis caches /dashboard and /fx/rates responses.

Subscription lifecycle worker

A daily job scans entitlements, downgrades expired plans, and queues D-7/3/1 renewal emails through the notifications pipeline (WhatsApp/SMS/email/push).

05

Challenges

High

Secrets committed in render.yaml

The MongoDB URI with password and gateway keys were hardcoded in a committed file. Tracked as a P0, with rotation left as an explicit user action — a hard lesson in repo hygiene.

High

WhatsApp and OTP failures in production

100% WhatsApp delivery failure because Twilio vars were missing in prod and the sandbox required per-recipient opt-in. Fixed with honest 502s, proper env vars, and a migration to Meta WhatsApp Cloud API (WABA).

Medium

Google Sign-In broken on Play builds

A SHA-1 mismatch between the EAS build key and Play App Signing key silently broke Google login on store builds — caught during release verification.

06

What I learned

  • Centralize entitlements: one source of truth for plans, meters and paywalls makes monetization clean.

  • Never commit secrets, and verify production env parity — both the audit and the stabilization tracker centered on leaked keys and missing prod vars.

  • React Query caching plus AbortController timeouts replaced expensive refetch-on-focus patterns and killed infinite spinners.

Code snippets

backend/routers/expenses.pypython
if split_type == "equal":
    n = len(splits)
    per = round(total / n, 2)
    remainder = round(total - per * n, 2)
    result = []
    for i, s in enumerate(splits):
        amt = per + (remainder if i == 0 else 0)
        result.append({"user_id": s.user_id, "amount": abs(amt) if amt < 0.01 else amt})
    return result