I'm a solo developer. Two months ago I launched Tally Assistant — an AI-powered bookkeeping tool. Here's why I built it, what I learned, and the tech stack.
The problem: CSVs from hell
Every month I'd download bank statements from PayPal, Stripe, Wise, and my traditional bank. Four completely different CSV formats:
Stripe: "2026-07-11","Payment from acct_123",1500.00,usd,John Doe
PayPal: "7/11/2026","14:30:00","PDT","Alice Johnson","Payment","Completed","USD","850.00","-24.65","825.35"
German bank: "11.07.2026";"Cloudflare Inc";"-10,46";"EUR"
UK bank: "11/07/2026","NOTION LABS","10.00","","£1,234.56"
Different date formats. Different delimiters. Different decimal separators. Different currency columns. Manual reconciliation took 2-3 hours every month. I tried QuickBooks (US-centric, no AI), Wave (free but multi-currency is broken), and FreshBooks (decent invoicing, no AI). Nothing fit.
So I built what I needed.
What it does
- Upload any bank CSV → AI auto-detects delimiter, date format, decimal separator, currency column. 200+ bank formats supported. No reformatting, no template setup.
- Snap a payment screenshot → AI OCR extracts merchant, amount, currency, and date in 50+ languages. Original image saved as tax documentation.
- Generate an invoice from one sentence → "Logo design $300, landing page $700 for Acme Corp, 6% VAT" → AI builds the invoice with line items, tax, PDF, and PayPal link. 5 seconds.
- Auto payment reminders → Escalating: friendly nudge Day 1, professional follow-up Day 7, firm final notice Day 21. AI-written, auto-sent.
- Multi-currency → 30+ currencies with daily exchange rates. Each transaction keeps its original currency.
- AI Finance Chat → "How much profit did I make in Q2?" — gets answered from your actual transaction data.
Tech stack
- Next.js (App Router) on Vercel
- PostgreSQL on Supabase + Prisma ORM
- Clerk for auth
- OpenAI (GPT-5.4-mini) for CSV parsing, receipt OCR, invoice generation
- Resend for email delivery and payment reminders
- Tailwind + shadcn/ui for the frontend
Total hosting cost: $0/month. Every service is on a free tier.
The CSV parsing: how it actually works
The CSV import is the feature I'm most proud of. Here's the architecture:
-
Programmatic delimiter detection — scans the first 5 rows, counts field consistency for each candidate delimiter (
,,;,\t,|), picks the most consistent one. - Date format detection — if any date has a value > 12 in position 1 → DD/MM/YYYY. If any has >12 in position 2 → MM/DD/YYYY. If all ≤ 12 in both → check currency/language heuristics.
- AI column mapping — the raw CSV (with detected delimiter + date format passed as hints) goes to GPT-5.4-mini. The AI maps columns by semantic meaning, not position. It handles headers in any language.
- Client auto-matching — the AI compares transaction descriptions against existing client names. New clients are auto-created.
I wrote more about this in Why Every Bank's CSV Is Different.
What I'd do differently
- Launch earlier. I spent 3 weeks on features before showing anyone. Should have launched after week 1 with just CSV import + invoicing.
- Distribution is harder than building. I spent 3 weeks coding, 5 weeks trying to get anyone to see it. The ratio should be closer to 1:1.
- "Free" positioning is a double-edged sword. It removes friction for users, but some people assume "free = low quality." I'm still figuring out how to communicate "it's free during launch because I want feedback, not because it's bad."
- Reddit > HN for my niche. My HN posts got zero traction. My Reddit replies to people actively complaining about bookkeeping got clicks and signups. Go where the pain is being discussed.
What's next
Recurring invoices, Stripe payment links, a mobile app. And getting to the first 100 users. Currently at single digits.
Try it, roast it
tallyassistant.com — completely free through September 2026. No credit card. If something about the CSV import or invoicing flow breaks on your specific use case, tell me. I fix bugs faster than big companies do.
Top comments (3)
what happens in the client auto-matching when a transaction description fuzzily resembles two existing clients? that choice looks harder than delimiter detection to me.
Thanks for the nudge — you were right, the original did exact string match only. "Acme Corp" vs "Acme Corporation" →duplicate client. We just fixed it three ways:
passing the existing client names into the prompt is what i'd have reached for first too. how do you keep that list bounded as a user's clients pile up — cap it to recent ones, or send the whole set each import? i hit token and cost walls fast once a couple accounts had long histories.