AI Credit Card Statement Copilot
A statement tells you what you spent and nothing about what it means, which is how people earn a cashback and lose it to a category cap they never read. Credrax computes every number in deterministic TypeScript and lets the model narrate what the code already worked out.
The bank sends a PDF. You open it, see a number, and have no idea.
Every month the same ritual. The bank sends a PDF. You open it, see a number, and have no idea whether it is good or bad, where the money went, or whether the cashback you earned was worth the category you spent in. The card's app shows the total. The statement shows the rows. Neither connects them.
The interesting failure is not that the information is missing. It is all there, in sixty-odd rows, and reading it is technically possible. The failure is that nobody has forty minutes a month to reconcile a reward structure against their own spending, so nobody does, and the consequences are invisible by design. A cardholder can hit the spend that earns a cashback and still not receive it, because the reward was capped per category, or excluded a merchant type, or required the transaction to land before a cycle date. None of those conditions announce themselves. The money simply does not arrive, and there is nothing on the statement that says why.
Which makes this a comprehension problem rather than a data problem. The statement is a record of what happened, written for a system that needs to be auditable. What a person needs is an account of what it meant: where the money went, what the card gave back, and what it was supposed to give back and didn't.
Hence the shape of the product. Upload one statement, one card, one month, and get back something that can be questioned rather than filed.
Fluent and correct are not the same skill.
A finance explainer has to be two things that come from different places. It has to read like a person talking, because a table nobody understands is the problem being solved, not the solution. And every number in it has to be right, because a wrong balance is not a rough draft, it is a lie about someone's money.
Language models are excellent at the first and structurally unreliable at the second. Not because they are always wrong at arithmetic, but because they are wrong unpredictably, and a confident wrong number is indistinguishable from a confident right one at the point where a reader is deciding whether to believe it. Fluency is the exact quality that makes an error hard to catch.
That tension governs the entire architecture. Every subsequent decision is an answer to the same question: which half of this does the model get to own, and which half does it get locked out of.
One rule encoded into every file.
The rule
Compute first. Narrate second.
That is not a guideline. It is a constraint encoded in every file. The insight engine, the health score, the cashback calculator, the reward rule evaluator. All deterministic TypeScript, no model involved. GPT-4o only enters the picture after every number exists.
The model's job is to translate already-computed facts into plain English. It is a writer, not a calculator. Its system prompt says so explicitly:
"Use ONLY the data provided. Never invent, estimate, or recompute numbers. If a number is not in the input, you may not state it."
A ten-step pipeline, not a black box.
Uploading a PDF starts a job. The job runs ten steps in sequence, reported in real time over SSE so the frontend can show exactly which step is running. Only step 1, the parse, can abort the job. Every other step either returns data or attaches a warning and lets the pipeline continue.
| # | Step | What it does | Model |
|---|---|---|---|
| 1 | Parse | PDF → raw text, transactions, cycle dates | — |
| 2 | Normalize | Clean merchants, amounts, dates, categories | — |
| 3 | Card match | Recognize card from catalog | — |
| 4 | Rule engine | Apply card-specific reward rules | — |
| 5 | Cashback | Aggregate rewards, flag missed optimisations | — |
| 6 | Insights | Category breakdown, top merchants, spend patterns | — |
| 7 | Health score | 0–10 composite across five weighted factors | — |
| 8 | Narrative | Turn computed data into readable English | GPT-4o |
| 9 | Glossary | Filter static glossary to terms in this statement | — |
| 10 | QA validate | Reconciliation checks, attach warnings | — |
Eight of ten steps never touch a model. GPT-4o only sees step 8, after every number is locked.
The score is not a guess. It is arithmetic.
The financial health score is a 0–10 composite built from five weighted factors: utilization (30%), payment behavior (25%), interest charges (20%), spending stability (15%), cash advance (10%). Each factor returns a verdict — excellent, healthy, normal, watch, concern — and a weighted contribution. The composite maps to a band: excellent (8.5–10), good (7–8.5), caution (5–7), concern (below 5). None of it is an opinion. The model can see the score and explain it. It did not make it.
The LLM narrates. It never computes.
The narrative agent receives a structured object containing every computed number, every verdict, and every insight, and its only job is to write a human-facing summary. Strict rules: if a number is not in the input, the model may not state it. It can format numbers (add ₹, Indian digit grouping). It cannot change them.
The result sounds like it understands your finances because it is backed by arithmetic that actually does. "You spent 47% of this month's total on dining and groceries, which pushed utilization to 68%, inside the watch band and above the threshold where revolving interest becomes a real risk" is what happens when the numbers are handed to the model first.
The alternative, handing the model the raw PDF and asking it to work out the finances, is a different product with a different failure mode. It would demo well. It would be materially easier to build, since most of the parsing and reward logic simply disappears into the prompt. And it would produce numbers no one could defend, in a category where being unable to explain where a figure came from is disqualifying.
The cost of the choice is real and worth naming. Deterministic computation means every card's reward structure has to be encoded by hand before the product can say anything useful about it, which is why the catalog holds two cards rather than fifty. The easier architecture scales instantly and cannot be trusted. This one earns trust and scales slowly. For money, that is the right side of the trade, but it is a trade and not a free win.
The safety rail is not trust in the model. It is that the model receives a read-only data object and returns a text string. There is no pathway for it to modify a transaction, recalculate a balance, or update a score.
No database in v1. That is deliberate.
A statement is one session: you upload, you read, you close the tab. Persistence means storing financial documents, which is a different product with different obligations.
| Next.js 15 App Router + TypeScript | 12 routes, CSS custom properties — no Tailwind |
| PDF parsing | pdf-parse, runs server-side on upload |
| Financial computation | Deterministic TypeScript — 16 lib modules, no model |
| Job state | In-memory Map — no database in v1 |
| AI narration + chat | OpenAI gpt-4o, structured JSON output |
| Card catalog | JSON files — Axis Airtel RuPay, HDFC Regalia |
The demo at /statement/demo runs without an API key — a mock June 2026 Axis Airtel statement, full pipeline, narration step skipped gracefully.
Two things that cost more than expected.
The PDF format problem. Indian credit card statements have no standard. Each issuer has their own column order, date encoding, and PDF export quirks. The QA validator catches reconciliation mismatches and attaches warnings rather than failing silently, because silent failure in a finance tool is a different class of problem.
The card catalog problem. Reward rules are genuinely complex. The Axis Airtel card gives 25% cashback on Airtel recharges, 10% on utility bills, 1% on everything else, with a monthly cap per category. Encoding that correctly so the cashback calculation is deterministic took longer than the insight engine. Two cards in the catalog now. Adding more is research and data entry, not a coding problem, and that's the real ceiling for making this broadly useful.
Do not let a language model compute financial numbers. Not because it will always get them wrong, since it often gets them right, but because you cannot tell when it does not, and a wrong balance in a finance app is a different class of error than a wrong synonym in a thesaurus.
The pipeline pattern — compute everything first, feed results to the model, lock the model out of the calculation, cost nothing to implement and means every number in the UI has a traceable source in TypeScript. The model writes. That separation is the only architectural decision in this project I would not revisit.
The card catalog is the real ceiling. Two cards is a demo. Making this useful for most Indian cardholders means encoding the reward structures for twenty or thirty cards. That is the next thing.