The Friction Problem
Expense tracking apps fail at a predictable point: manual entry. The friction of logging every transaction is low enough to tolerate on easy days and high enough to skip on hard ones — and skipping twice becomes quitting. Apps that address this with receipt scanning either lock it behind a paywall or produce output inaccurate enough that correction overhead exceeds the original entry cost.
The engineering problem is that receipts are a noisy, inconsistent input: thermal paper, variable lighting, skewed angles, and retailer names that don't map cleanly to spending categories.
Data Architecture
Three core Prisma models: Account (type, balance, isDefault, isBusiness), Transaction (type, amount, category, date, receiptUrl, isRecurring, recurringInterval), and Budget (amount, lastAlertSent per user). All data mutations run as Next.js server actions — createTransaction, updateBudget, deleteAccount — giving end-to-end type safety from React component to database without an intermediate REST layer.
Clerk handles auth with Svix webhook delivery to sync user records to PostgreSQL at registration. Arcjet enforces rate limits across API routes.
Receipt Pipeline
The receipt pipeline accepts an image upload, sends it to an AI vision model with a typed output schema, and returns structured JSON: vendor, line items, subtotal, tax, total, date, and inferred category. Category inference required iterative prompt engineering — a receipt from a major retailer is genuinely ambiguous across groceries, household supplies, and electronics, and zero-shot defaults don't match how users mentally categorize their spending.
Budget Intelligence
Budget evaluation is synchronous and pre-confirmation: the alert fires inside the transaction flow before the record commits, not as an asynchronous notification afterward. Inngest runs two durable background jobs — a monthly budget summary and a threshold alert at 80% category usage — both survive serverless function timeouts and retry on failure.
Key Technical Decisions
- —Server actions throughout — end-to-end type-safe mutations via Prisma, no REST API layer
- —AI vision receipt parser with typed JSON output and prompt-engineered category inference
- —Pre-confirmation budget evaluation — alert fires synchronously before the record commits
- —Inngest durable jobs — budget alerts and monthly reports that retry on timeout or failure
What's Next
Xpenso is live and in active use. The most demanding part was the receipt parser's failure path — bad angles, low contrast, faded thermal paper — which required as much design work as the successful parse. A parser that fails loudly is worse than no parser. Planned additions include multi-currency account support and ML-based spending pattern detection for proactive recommendations.
