Inspiration

Every trip, house, and dinner group has the same problem: someone pays, someone keeps a spreadsheet, and three weeks later nobody remembers who owes what. Splitting apps exist, but they make everyone download something and type every expense in twice. The money talk already happens in the group chat. We wanted the tab to live there too.

What it does

Tab is a member of your iMessage group chat.

  • Text expenses: "got groceries, $63" → Tab replies "Groceries, $63.00. Split 4 ways, that's $15.75 each." If something's off ("jake only had a $3 diet coke"), it re-splits.
  • Receipts: send a photo. Tab reads the items, tax, tip, and fees, checks that the math adds up, and lets people claim what they had ("1 and 4", "same as Kian", "we all split the fries").
  • Asks instead of guessing: missing amount? "How much was the Uber?" Unknown name? "Who's Dhabush?" Tab never invents a number or a person.
  • Settling: by default Tab keeps a running tab. Say "settle up" and it posts one message with who owes whom. Each person taps šŸ‘ to pay their part. A typed "yes" never moves money.
  • Questions: "what do i owe" gets just the amount. "why" gets a one-line breakdown. "@tab ledger" posts a private link to a live web ledger with balances and a money-flow graph.
  • Corrections and disputes: "actually it was 44" updates the expense; šŸ‘Ž on a settle request opens a dispute that changes only that person's share.

How we built it

  • iMessage client: a Mac bridge that reads enabled group chats and sends Tab's replies, threaded replies, and tapbacks.
  • SpacetimeDB (Maincloud) is the single source of truth. Every write goes through typed reducers; the backend, client, and web ledger read role-gated views. The split math (an even, custom, or itemized split, with tax and tip spread proportionally and exact cent rounding) lives in the module. Settlement is a scheduled reducer that completes simulated transfers.
  • A two-stage AI pipeline:
    1. A fast classifier gate (TypeSafe's Jev) sees every message and decides whether it's about money.
    2. Only messages that pass go to Grok (xAI) for structured extraction: expenses, claims, corrections, and receipt reading with Grok vision. Chatter never reaches Grok, and its text is cleared from the database.
  • Numbers come from code, never from the model. Every amount Grok extracts must appear in the original message ("thirty two fifty" counts). Receipts must add up, or Tab asks the payer. Even Tab's free-form money answers are checked in code for who owes whom before they're sent.
  • Capital One Nessie: demo accounts are seeded with Nessie, and completed settlements are mirrored there.
  • Web ledger: React + Vite + React Flow, live-subscribed to SpacetimeDB, opened by an unguessable per-group link.
  • Tests: a simulated group chat runs the real backend against an in-memory copy of the database module, with scripted Grok responses. That's 250+ backend tests, plus a browser playground for driving Tab with fake phones.

Challenges we ran into

  • Real receipts are messy. One of ours printed the card payment as "Credit āˆ’$227.66" and "Amount Due $0.00". The first version read the payment as a discount and the total as $0, and the math still "added up". We added code-level guards (payments aren't discounts, a $0 total is never accepted) instead of trusting the prompt.
  • Checking an LLM's numbers isn't enough. Our first check confirmed every dollar amount existed in the data, but "you owe Jake $26 and Joe $6" passed when it was the other way around. Balances are now answered from templates, and the AI path checks direction too.
  • Group chats are chaotic. Several people answer at once, someone's "ok" is to a different conversation, and iPhones send curly apostrophes ("I’m Tanuj") that broke name parsing. We built a tracker for Tab's open questions so the right person's answer goes to the right question.
  • Timing for a live demo: real objection windows are hours long, so a demo mode compresses them, without shrinking how long Tab waits for people to type.

Accomplishments that we're proud of

  • Money only ever moves on an explicit šŸ‘ from the person paying.
  • Tab feels like a friend in the chat: short, lowercase when the group is lowercase, and quiet unless it's about money.
  • Three people and their coding agents shipped 40+ reviewed pull requests in a weekend, with the agents reviewing each other's PRs on GitHub.

What we learned

  • Put a cheap, privacy-preserving gate in front of the expensive model.
  • Let the LLM read language, but let code own every number.
  • "Ask, never guess" is a better product than a smarter guess.

What's next for Tab

Real payment rails (Venmo/Zelle-style), netting debts across expenses into fewer transfers, multi-currency, and running Tab on its own number instead of a team member's account.

Built With

  • capital-one-nessie-api
  • github
  • github-actions
  • grok-(xai-api)
  • grok-vision
  • imessage
  • node.js
  • react
  • react-flow
  • spacetimedb
  • typesafe-jev
  • typescript
  • vercel
  • vite
  • vitest
  • zod
Share this project:

Updates

Submission history