Case Study
Aegis: An MSME Credit Engine That Won't Let a Score Override a Fraud Flag
Built solo for IDBI Innovate 2026. A deterministic MSME credit-decision layer that separates gradable risk from non-negotiable policy gates, and never lets an LLM touch a number.
Built with:Next.js 16 · React 19 · TypeScript · Zod · Vitest · Groq (gpt-oss-120b) · Vercel

Outcome
Submitted with a working prototype, 21/21 functional tests passing. Deterministic scoring engine, LLM-as-narrator-only architecture, and a live What-If simulator.
The version I didn't want to build
Give a hundred teams the same problem statement, a Financial Health Score for MSME lending, and most will land on the same shape. Pull in GST, UPI, and utility data. Fold it into one score. Optimize that score to approve more of the MSMEs a bureau currently can't see. If there's time left, back it with an ML model, because a model reads as more sophisticated than a weighted sum.
I didn't want to build that version, and not for aesthetic reasons. It doesn't survive contact with a bank.
INSIGHT
An underwriter doesn't get to approve a loan because a score says so. They have to defend the decision to a risk committee, and increasingly, to a regulator. A score with no decomposition is a liability with better branding than most liabilities get. And "approve more" only optimizes in one direction. It says nothing about the bureau-clean businesses that are actually bad risk. No risk officer signs off on a score that only expands the approve set.
There was a third reason, more practical than either of those. ML-derived weights need a labeled dataset: hundreds or thousands of MSMEs with known default or repayment history. I had somewhere between 8 and 13 hand-built synthetic profiles. Training weights on that isn't calibration. It's overfitting with extra steps.
Defensibility, bidirectionality, and data insufficiency. Those three problems shaped the entire build, not just the pitch.
The one rule everything else follows
As of December 2025, India had 8.7 crore registered MSMEs. By March 2026, roughly 3.6 crore had ever accessed formal credit, about 41%. That gap doesn't prove the other 59% are creditworthy. It proves that conventional credit history alone can't give a lender a complete picture of the market.
The interesting part of the build isn't that gap. It's that risk isn't one number. It's two.
The engine computes a graded capability score from six weighted factors, then applies soft penalties for recoverable weaknesses: receivable delays, buyer concentration, a seasonal cash dip, recent late GST filing. Those are the levers the What-If simulator exposes, because a penalty only matters if the borrower can see what happens when it's removed.
Separately, the engine checks for hard policy flags: a GST filing gap of two or more consecutive missed cycles, an active default signal, or a KYC mismatch. The score always gets computed, on every profile, gate or no gate. What the gate controls isn't whether a number exists. It's whether that number is allowed to produce an approval.
DECISION
Why split it instead of running one continuous weighted score: real credit policy isn't continuous. An active fraud signal isn't a factor to weigh a little more heavily. It's a reason a bank won't lend, full stop, no matter how good everything else looks. Collapse that into a single score and you get one of two failures. Either a high enough number papers over the fraud flag, or the flag's weight gets forced so high it distorts every other factor in the model. Splitting it lets each mechanism do the job it's actually good at. The score handles gradation. The gate handles the bright line.
The recommendation bands, when no gate is triggered, are simple: a net score of 60 or above clears for approval, 50 to 59 gets referred with an improvement path, below 50 declines with a path. Any triggered gate overrides all three, regardless of the number underneath it.
Every other decision in Aegis, the simulator, the UI, the narrator prompts, the demo script, follows from this split. I treated it as a frozen invariant once it was in place, and I didn't let myself reopen it, even when deadline pressure made "just a small tweak to the weights" sound reasonable.
Why not just buy Perfios
This was the sharpest question I got asked during the build, and it's the one most teams entering this problem statement won't have an answer for.
Claiming Aegis assesses risk better than an established platform like Perfios, Karza, or Setu is false, and it falls apart the moment someone who knows the space asks a follow-up. These platforms already serve lenders at scale with production underwriting, fraud checks, and risk-decisioning tools. If the pitch is "we score better," that fight is already lost before it starts.
These platforms get paid by the lender. Their assessment logic is typically closed, and there's little commercial reason to build a borrower-facing improvement loop, because the MSME paying for the loan isn't the one paying for the platform.
Aegis sits on top of whatever a bank already has. It's decomposed and simulatable by construction, and the borrower-facing side, showing an MSME exactly what to fix, is a core feature rather than an afterthought, because that's the side a data vendor has less commercial reason to prioritize.
The line I settled on for the pitch: you might still buy Perfios. Aegis can run on top of it. Perfios tells the bank what the numbers are. Aegis helps the underwriter defend a decision, and tells the borrower how to qualify. A data vendor has less incentive to build that second half.
That reframes the fight from "better score," which I'd lose, to "different job," a category the incumbents aren't optimized to occupy.
Testing the decision logic across three synthetic businesses
With no production repayment data, I couldn't validate predictive accuracy. What I could test is whether the architecture behaves consistently across three deliberately different situations, so I built three synthetic profiles, each one designed to answer a specific objection.
Meher Components is a manufacturing business in Pune, established 2018, with an annual turnover around ₹8.4 crore. It's a thin file to a conventional bureau, no meaningful formal credit history, and a bureau-led screen declines it outright. Run through Aegis, the default profile nets a score of 82, comfortably clear of the approval line, and the system approves it with high confidence. Adjusting the operational levers, receivable days, GST compliance, buyer concentration, moves the score a point or two in either direction, but the underlying operating strength holds the recommendation in place either way. That stability is the point: a thin file isn't the same thing as a weak file, and the gap between what the bureau sees and what the business actually is happens to be large enough that the recommendation doesn't wobble.
The Mirage is a synthetic profile built to test the opposite failure mode: a higher-revenue trader with some bureau history, where the revenue number hides real instability. One buyer covers roughly 58% of its business. Receivables run slow. GST filing has missed three cycles. The graded capability score comes out around 51, which soft penalties for concentration and seasonality would already pull toward decline on their own. But the missed GST cycles trip a hard gate, which caps the recommendation at refer-or-decline regardless of what the score underneath it says.
This is the profile I'd pull up if someone asks whether the system just approves everyone. It doesn't. A business that looks approvable on the surface gets declined, because the gate doesn't care what the score says.
The Climber is a borderline food-sector business, synthetic like the other two, improving but not yet bankable as it stands. Capability score around 66, no gates triggered, but soft penalties for slow receivables and a concentrated buyer base pull the net score down to 48. Decline, with a path.
INSIGHT
These three cases show the architecture behaves as intended: the score gets computed independently of the gate check, a thin file doesn't automatically mean decline, and a gate can override a decent score. They don't show that the underlying weights or thresholds predict actual repayment. That needs real outcome data, which this prototype doesn't have.
Being honest about the number that moves
The Climber's simulator is the clearest demo moment, so it's the one I spent the most time shaping, and I want to be straightforward about how.
Reducing receivable days from 60 to 40 clears the invoice penalty and moves the net score from 48 to 61, just over the approve line. I got there by nudging the weights while I was building that demo scenario, not by discovering a value that happened to land there. I'm naming that plainly because it matters: the 48-to-61 jump is a designed illustration of how the simulator responds to a lever, not an empirically derived number.
I still made a deliberate choice inside that nudge. I could have shaped it to land much higher, 48 to 85, for a more dramatic reveal. I didn't, because a jump that big would look gamed, and anyone watching the demo would stop trusting the simulator right there. A small, believable crossing reads like a real underwriting outcome. A dramatic one reads like a trick. That's the trade I made on purpose. The specific threshold value is illustrative, not calibrated.
What I'd defend, and what I'm naming before anyone else finds it
The weights behind the six scoring factors (cash-flow stability, GST consistency, digital receipts, business stability, invoice health, and seasonality) are expert-judgment weights, not empirically calibrated ones, and I'd defend that distinction directly. With roughly a dozen synthetic profiles and no observed repayment outcomes, training weights would have meant overfitting, not calibration. Hand-set weights are also auditable in a way a learned model isn't: "cash-flow stability is weighted highest because it's the most direct proxy for repayment capacity" is a sentence a risk committee can evaluate. "The model learned this weighting" is not.
The RBI released a draft Guidance on Regulatory Principles for Model Risk Management on June 24, 2026, open for public comment through July 24. It's still a draft, not a finalized requirement, so I'm not claiming Aegis satisfies it. What I can say is that keeping the LLM outside the scoring path is directionally aligned with where that draft is heading, and it's worth naming that the draft's definition of a model is broad enough to cover a deterministic rules engine like this one, not just the LLM layer. A production version would still need independent validation, a documented model inventory, monitoring, and human-review paths that this prototype doesn't have.
A few other things I cut on purpose, and would rather name here than have a
judge find first. All the data is synthetic, no live integration exists with
the Account Aggregator framework, GSTN, EPFO, or TReDS, the assessmentAdapter
layer is where those would plug in but nothing is wired yet. The hard gates
are binary, with no graduated severity, a deliberate bright-line choice and a
real limitation a production system would need to fix. Right now, all three
gates route to the identical outcome: a GST filing gap, an active default,
and a KYC mismatch all collapse to the same REFER_OR_DECLINE, with nothing
distinguishing a confirmed default from a mismatch that's more likely bad
data than bad intent. That's a real limitation, not a hypothetical one. One
place the engine does guard against double-counting: a late GST filing only
adds a soft penalty when it hasn't already tripped the GST hard flag, so the
same lapse can't cost a business twice.
None of these were discovered under pressure. They were decisions made with the time I had, and I'd rather write them down than pretend the build was more finished than it was.
The pattern I keep having to catch in myself
The most consistent risk across this build wasn't technical. It was scope discipline. There was a recurring pull to reopen the engine weights, or the onboarding wizard, or the simulator, after they were already tested and working. Every reopening urge got run through the same filter: if removing a feature wouldn't make the demo worse, don't build it. If a change wasn't necessary, it didn't happen, no matter how easy it looked in the moment.
That's the part of this project I'd carry into the next one more than any specific architecture decision. A tested, working, slightly less impressive system beats one I kept improving until it broke two days before submission. I've built the second version before. I know how it ends.