Inspiration
Most people only open their banking app after something has already gone wrong: an overdraft fee, a missed bill, a card balance that crept up. And their money is spread across several accounts, so nothing shows them the whole picture in one place. Budgeting apps wait for you to come to them. We wanted the opposite: money that speaks up at the right moment, in the places you already are (your texts and your phone), in plain words, and that never pressures you or moves money behind your back.
So we asked: what if your accounts were people in a group chat? Huddlr turns Checking 🏦, Savings 🐷, Card 💳, Loan 🧾, IRA 🌱 and a Coach 🧭 into characters who text you over iMessage when something happens: a big purchase, a paycheck landing, a bill coming due, or a dip in your forecast. Each account gives its side, and the Coach gives the bottom line: every move to make, which you do yourself in your bank app. You can @mention one account, text a photo of a price tag to ask "can I afford this?", or call the same crew on the phone. And the Huddle page seats all your accounts at one round table: what you have and what you owe in the middle, each account's health at a glance, and a "what if" box where you type "what if I buy a $300 TV?" and every account shows what it would do to them before you spend.
What we learned
- The model should phrase things, and the code should decide them. Every number comes from our finance engine. Gemini only writes the sentences around it. That split made the characters trustworthy and testable.
- Plain language beats precision. "0.55 months of essentials" confused our own testers. "About 2 weeks of must-pay bills" didn't. Now every savings cushion is described as time you're covered.
- "Quiet by default" matters more than clever messages. A finance assistant that texts too often gets muted. "hey" deserves a hello, not four accounts and a Coach.
- Seeing everything at once helps people understand it. The chat explains one moment. The round table shows the whole picture at a glance.
- Test against the real API early. Every Nessie and SIP surprise came from reading real data back, not from the docs.
- Agreeing on contracts in hour one let four people build in parallel without blocking each other.
How we built it
- Bank data: Capital One's Nessie API, seeded with a demo customer and 60 days of purchases, paychecks and bills. A sync detects new transactions by ID. Nessie supports checking, savings and credit cards, so our engine simulates the IRA and loan.
- Finance engine (no AI): plain TypeScript computes a 45-day forecast (lowest balance, first negative day, safe to spend until payday), five health indicators, what-ifs, and plans. It's the only source of numbers. For example, it works out that a $2,400 card balance at 24% costs $48 a month in interest. Any loan the user tells us about gets a full breakdown: "$18,000 at 6% for 24 months" becomes $797.77 a month, with how much goes to interest and when it's paid off. A longer projection covers every account over a window you choose, with a likely range based on a spending model learned from past transactions, and compares it with following the Coach's plan.
- Characters: Gemini (3.5 Flash-Lite) writes every line. Code checks each one before it's sent:
- a number checker rejects any figure the finance engine didn't produce;
- tone rules catch pressure ("put it in me"), slang, "be careful" anxiety, and cut-off sentences;
- Coach rules require a "Bottom line:" that lists every move, and forbid asking for a YES.
If a line fails, Gemini is told exactly what was wrong and tries again, up to four times. A pre-written template is used only if the rate limit is hit or time runs out.
- Conversation routing:
@savingsgets one voice.- A general question gets a roundtable: each account answers in turn, threaded under the message it replies to, and then the Coach.
- A text with two questions gets both answered.
- "Can I afford $40?" is answered directly by the engine.
- "Can I take out a loan for a house?" gets a lender's-eye check computed in code: income, the share of it already going to debt payments, savings for a down payment, and card use.
A moderator caps speakers per event, holds messages during quiet hours, and limits proactive texts per day.
- Messaging: Photon handles iMessage, with a signed webhook and an outbox sender that paces messages and retries.
- Voice: an ElevenLabs agent on a Photon phone number over SIP (TLS). It calls the same finance tools the text agents use, so phone and text always agree. A post-call webhook saves the call and texts a recap. If checking will go negative within 48 hours and the texts go unanswered, Huddlr calls you, and texts if you don't pick up.
- The Huddle page: the welcome text links to it, and "huddle link" gets a fresh one anytime. Opening it takes a 6-digit code texted to the same iMessage thread, so you need both the link and the phone, and a new link signs out old devices. Each account sits at the table with a face that reflects its health, and a tap shows its details. In the "what if" box, Gemini only reads the question and picks the kind of decision (buy now, new monthly bill, future expense, move money, lose income). The engine and simple math work out what it does to each account.
- Infrastructure: shared zod contracts and fixtures every service builds against, a simulated clock so the demo can fast-forward days, a password-protected team dashboard with a demo controller, Hono services on a Vultr VM behind Caddy, Postgres, and GitHub Actions CI on every merge.
Challenges we faced
- Nessie surprises.
- Balances never change on writes, so we keep our own ledger.
- A purchase sent without a status breaks that account's purchase list for good, and a bill without a
payment_datedoes the same to the bill list. - Amounts are stored as whole dollars, so cents silently disappear.
We found each of these by seeding real data and reading it back, then added guards so they can't happen again.
- Keeping the AI honest. The model loves to round, estimate and invent numbers. It once told a user that spending 11% of their income on debt "does not meet guidelines," when the usual limit is 36%. We moved every calculation and comparison into code. The model now gets the verdict, not the raw math.
- Making it sound human, not robotic. Early versions answered "hey" with a full debate, had characters pitch for money instead of explaining, opened with "Adding to Checking" when Checking never spoke, and wrote dates like 2026-12-01. Each one became a code-enforced rule with a test. Across our six services there are now 370+ tests, plus an end-to-end gate run on every change.
- Understanding real texts. People write "can i check with @loan if i can get a house," "18000 for 24. months," or two questions in one message. Parsing mid-sentence mentions, loan terms, and multi-part questions took many rounds of phone testing.
- Getting phone calls working. Photon's actual SIP server and credentials differed from its docs, and shared iMessage lines can't take calls, so we needed a dedicated number. It took several rounds before inbound and outbound calls both worked.
- Showing everything without overwhelming. A big negative net worth in the middle of the table looked alarming. We led with what you have and what you owe instead, and kept net worth as a quieter line beneath.
- Restraint. Deciding when the characters should stay quiet was as hard as deciding what they say.
Built With
- gemini
- nessie
- node.js
- photon
- postgresql
- sql
- typescript
