ai-finance-platform: Welth: The Finance App That Behaves Like a Workflow Engine
Inside a Next.js 15 starter that turns recurring payments, receipt scanning, AI reporting, and abuse protection into one production-minded stack.
- Welth is built around workflows, not screens, so its main innovation is the machinery that keeps finance data moving after the request ends.
- Recurring transactions are handled as fan-out jobs with throttling and resumability, which makes the app feel more like infrastructure than a CRUD starter.
- Gemini is used as a structured extraction layer for receipts and reports, not as a novelty chat panel.
- Prisma transactions, serialized decimals, and layered security controls keep the money model consistent under real-world abuse and concurrency.
Most finance projects are dashboards with a chatbot attached. Welth goes after a harder problem: how do you keep financial state correct when work happens after the user leaves the page? The answer is a stack that treats scheduling, extraction, validation, and abuse protection as first-class product features.
That is why this repo feels less like a template and more like a blueprint. It combines piyush-eon/ai-finance-platform with Next.js App Router, Server Actions, Prisma, Clerk, ArcJet, Inngest, and Gemini in a way that mirrors a real SaaS backend. The visible app is finance. The hidden app is orchestration.
A finance app that does its best work after you leave the page
The project's real thesis is simple: finance software is a sequence of promises. A receipt gets scanned into structured data. A recurring bill gets detected on time. A balance update lands atomically. A monthly report arrives without manual prompting.
AI Finance Platform is a next-generation financial manager and analysing system designed to help users track transactions, manage budgets, and generate AI-powered insights.
That framing matters because the repo is not trying to win on novelty. It is trying to show how a modern finance product survives the boring parts: retries, rate limits, bot traffic, decimal precision, and background work that must not collapse if one step fails.
The hidden engine: recurring transactions as a fan-out problem
This is the most revealing part of the repo. A daily cron does not mutate every due transaction inline. It first discovers what is due, then emits separate work items, then lets a throttled worker path process them one by one. That design is what turns a finance feature into a durable workflow.
The important detail is not just that the system uses background jobs. It uses them in a way that preserves isolation. One failed recurring item does not poison the whole batch. That is the difference between a demo and a production-shaped workflow.
Why Gemini matters more here than in a chatbot
Welth uses Gemini where it has actual leverage. Receipt scanning becomes structured extraction from an image into transaction fields. Monthly reporting becomes a summarization pass over user data, not a general-purpose chat bubble. AI is the translation layer, not the product surface.
That distinction is easy to miss, but it is the reason the feature set feels credible. The model is not asked to be clever in the abstract. It is asked to return usable finance data, and then the app decides what to do with it.
// Conceptual flow inside the receipt scanning path
const imageBase64 = await readReceiptFile();
const result = await gemini.generateContent({
prompt: 'Extract merchant, date, amount, category, and payment method as structured JSON.',
image: imageBase64,
});
const parsed = validateReceiptSchema(result);
await db.transaction.create({ data: parsed });
That is a better pattern than bolting a chat assistant onto the side of a finance app. It converts messy inputs into structured records, then routes those records into the same integrity checks as manual entries.
Money must stay consistent: transactions, decimals, and atomic updates
Finance data has no room for loose writes. The repo handles this with Prisma transactions, atomic account balance updates, and careful decimal serialization so money values do not degrade when they cross server and client boundaries.
The bulk-delete path is especially telling. It does not just remove rows. It computes the balance effect, updates the affected account state, and commits the whole operation together. That is the right instinct for any system where a half-finished write would create nonsense.
await db.$transaction(async (tx) => {
const totalImpact = transactions.reduce((sum, item) => sum.add(item.amount), new Decimal(0));
await tx.transaction.deleteMany({ where: { id: { in: ids } } });
await tx.account.update({
where: { id: accountId },
data: { balance: { increment: totalImpact.neg() } },
});
});
The serialization layer matters too. Prisma decimals are great in the database, but they need careful handling when they move through React and Server Actions. The repo treats that mismatch as an engineering concern, not an afterthought.
Security is not an afterthought
Welth layers defense in two places. First, middleware chains ArcJet before Clerk, so bot and abuse checks happen early. Then sensitive actions add their own rate limiting, which means a user still has to behave well even after they authenticate.
That layered model is worth copying. Auth answers who you are. Rate limiting answers how you are behaving. A finance app needs both because the failure mode is not just a bad login. It is an account flooded with scripted writes.
| Layer | What it does | Why it matters |
|---|---|---|
| Middleware | ArcJet runs before Clerk | Stops obvious bot traffic before auth work starts |
| Action layer | Token-bucket protection on createTransaction | Limits spam even from valid sessions |
| Database writes | Prisma transactions | Prevents partial money updates from corrupting balances |
The design says something important about the repo's priorities. It is not trying to appear secure. It is trying to behave securely at multiple layers, which is a much stronger signal for a production-minded starter.
How it compares to other finance projects
A lot of finance projects solve one slice of the problem well. Some are document Q&A tools. Some are budgeting helpers for an existing system like YNAB. Some are market terminals. Welth aims at a different target: a reusable architecture for a modern finance SaaS.
| Project type | Primary purpose | AI role | Background jobs | Best for |
|---|---|---|---|---|
| Generic finance dashboard | Track balances and transactions | Usually none or a chatbot | Often minimal | Simple CRUD products |
| AI finance copilot | Explain spending or answer questions | Analysis and chat | Sometimes limited | Personal guidance tools |
| Welth | Orchestrate finance workflows | Extraction and reporting | Core to the design | Production-shaped SaaS starters |
That is the strongest differentiator. It is not only that Welth includes more features. It uses a more realistic product shape. It shows how finance software becomes trustworthy once the invisible systems are treated as product, not plumbing.
Who this repo is really for
This is for builders who want a reference architecture, not a toy demo. It is useful for founders who need a credible MVP stack, for developers who want to study modern server-side orchestration, and for anyone trying to understand how a Next.js app can behave like a small backend platform.
Piyush Agarwal frames it as resume-grade, and that is fair. But the stronger claim is broader: it is a compact lesson in how to design a SaaS system where AI, jobs, auth, rate limits, and money integrity all fit together without feeling bolted on.
Why Welth feels like the shape of the next wave of SaaS
The lesson here is bigger than personal finance. More and more web apps will need to do four things at once: accept input, transform it with AI, process it asynchronously, and protect themselves against misuse. Welth stitches those concerns together in one clean package.
That is why the repo stands out. It is not the prettiest dashboard, and it is not the flashiest AI demo. It is a working answer to a much more useful question: what does a modern app look like when it has to keep its promises after the request ends?