A complete walkthrough of the architecture โ from a WhatsApp message to a saved transaction.
User sends coffee 80 upi on Telegram or WhatsApp. Webhook fires to the Railway backend instantly.
The Telegram ID or WhatsApp phone number is passed through HMAC-SHA256 with a secret key. Only the hash is stored โ the real identifier never touches the database.
intent.js runs a pre-LLM regex classifier. ~70% of messages (greetings, commands, clear expenses) are handled without calling Claude at all โ reducing cost and latency.
Claude Haiku extracts amount, category, description, date, payment method, and expense type. Returns a structured JSON array โ even for multi-entry messages.
Transaction saved under the hashed user ID. If a default payment method is set, it's applied before insert. A batch_id groups multi-entry messages for atomic undo.
User sees: ๐ธ Expense saved ยท โน80 ยท Food ยท Coffee ยท UPI. Budget alert fires asynchronously if they're approaching a limit.
Telegram and WhatsApp are separate adapters over a shared core. Adding a new platform means writing one adapter โ all business logic stays unchanged.
Haiku handles fast, cheap parsing. Sonnet handles smarter conversational queries. Right model for the right job โ ~10x cost difference between the two.
A regex-based classifier runs before Claude. Greetings, slash commands, and bare numbers are handled instantly โ no API call, no latency, no cost.
Last 3 exchanges stored in memory with a 30-minute TTL per user. Enables natural follow-ups like "how about entertainment?" without re-stating context.
Real Telegram IDs and phone numbers are never stored. HMAC-SHA256 with a server secret produces an irreversible hash โ we literally cannot identify users from our own DB.
No passwords. A one-time token (15 min TTL) is issued via the bot. Clicking it creates a 7-day session. Identity bridges seamlessly from bot to web dashboard.
The parser always returns an array. Multiple expenses in one message get a shared batch_id โ so /undo removes the entire batch atomically.
The AI receives a suggestion list but isn't constrained by it. Users can specify any category โ "laptop under Electronics" โ and the AI honours it exactly.
Natural language reminder setting โ "electricity bill due 10th remind me" โ parsed by Claude, stored separately from transactions. One-time and recurring reminders supported. Freemium gate built in from day one via a plan column on the users table.
Your Telegram ID and WhatsApp phone number are passed through HMAC-SHA256(id, SECRET) before storage. The hash is one-way and irreversible โ even Spendly's own database cannot be used to identify you.
First name is used only during the session for personalization ("Hey Kiran!") โ it is never stored in the database. Every message from Telegram or WhatsApp includes the user's name in the payload, so it's always available in memory without persisting it.
All transactions are stored under the hashed ID. You can export your data anytime with /export and permanently delete everything with /deleteaccount.