How to read this build sheet
The module explains how underwriting works. This page is the parts list and the assembly instructions.
Every vendor link is there so you can verify what we say. You should not need to visit any of them to understand how the tool works.
Prices carry a Verified May 2026 stamp and sit in marked blocks. Indian bureau and data pricing is negotiated per contract and there is no public rate card — the figures here are the ranges people actually pay, useful for budgeting and for knowing when a quote is out of line.
The one distinction that confuses everyone
There are two different layers and they are routinely conflated.
| Layer | Who | What they hold |
|---|---|---|
| Credit Information Company (bureau) | TransUnion CIBIL, Experian, Equifax, CRIF High Mark | The underlying credit data. Licensed under CICRA 2005. |
| Aggregator / TSP | Perfios (which absorbed Karza), IDfy (which acquired Signzy), Surepass and others | The integration plumbing. They fetch from the bureaus on your behalf and give you one friendly API. |
Direct bureau access requires being an RBI-registered Credit Institution. Everyone else goes through an aggregator and pays a markup for it.
The same split exists on Account Aggregator: licensed NBFC-AAs (Finvu, Setu, OneMoney, CAMSfinserv) move the data, and TSPs (Perfios and similar) consume it on behalf of FIU clients and layer analytics on top. The AA rail crossed roughly 252.9 million linked accounts in 2026.
The obligation people miss
Under the RBI Master Direction on Credit Information Reporting, a regulated lender must submit data to and pull reports from at least one licensed CIC.
Reporting is not optional and it is not a later problem. It is a build item: you need a reporting pipeline producing the bureau’s prescribed format on their cycle, and getting it wrong affects borrowers’ scores, which is a conduct issue as well as a compliance one.
Raw materials — credit data
| Material | What it does | Verify at |
|---|---|---|
| TransUnion CIBIL | Largest Indian bureau. Score, tradelines, DPD history, enquiries. | transunioncibil.com |
| Experian India | Bureau. Same access model. | experian.in |
| Equifax India | Bureau. | equifax.co.in |
| CRIF High Mark | Bureau, historically strong microfinance coverage. | crifhighmark.com |
| Perfios | TSP. Bank statement analysis, bureau plumbing, GST and ITR. Absorbed Karza in 2022. | perfios.com |
| IDfy | Verification and data stack; acquired Signzy. | idfy.com |
| Finvu / Setu / OneMoney / CAMSfinserv | Licensed NBFC-AAs — the consent rail itself. | sahamati.org.in |
| GSTN | GSTR-1 / GSTR-3B filings for MSME income. Consent-based via authorised providers. | gst.gov.in |
| EPFO | Employer-confirmed salary and tenure. | epfindia.gov.in |
Raw materials — modelling and monitoring
| Material | What it does | Verify at |
|---|---|---|
| XGBoost / LightGBM | Gradient boosting. The tabular workhorse for PD models. | github.com/dmlc/xgboost |
| SHAP | Shapley attribution — the basis for adverse action reason codes. | github.com/shap/shap |
| InterpretML (EBM) | Glass-box models. Often within a point of GBM and directly interpretable. | github.com/interpretml |
| OptBinning | Weight-of-evidence binning and scorecard construction. | github.com/guillermo-navas-palencia |
| Evidently / NannyML | Drift and performance monitoring in production. | github.com/evidentlyai |
| Fairlearn | Group fairness metrics and disparity testing. | github.com/fairlearn |
| MLflow | Experiment tracking and model registry — your inventory. | github.com/mlflow |
How to use each one — bureau and AA
Bureau pull, through an aggregator
Every aggregator wraps the same underlying bureau call. The shape barely varies.
POST /credit-report
{
"name": "...", "dob": "1991-04-12",
"pan": "ABCPX1234K", # PAN is the strongest match key
"mobile": "9xxxxxxxxx",
"address": {...},
"consent": {"granted": true, "timestamp": "...", "ip": "...",
"purpose": "loan_underwriting"}, # REQUIRED. Store it.
"bureau": "cibil" # or experian | equifax | crif
}
RESPONSE
{
"score": 742, "score_name": "CIBIL_SCORE_V3", # <- version matters
"enquiries": [ {date, member, purpose, amount} ],
"accounts": [ {type, opened, sanctioned, balance,
dpd_history: "000|000|030|..."} ],
"match_confidence": "exact" | "partial"
}
WHAT TO CHECK, EVERY TIME
[ ] match_confidence. A "partial" match may be a different person
with a similar name. Do NOT decision on a partial match.
[ ] score_name / version. Cut-offs tuned on one score version are
not valid on another. Pin the version in your policy.
[ ] "no hit" is NOT a low score. It is a thin file. Route it to the
alt-data path, do not decline it. (See the module.)
[ ] dpd_history is a STRING of 3-char buckets, most recent first.
Parse it; do not regex for "30" and call it a delinquency.
[ ] enquiries in the last 30-90 days - a burst across lenders is one
of the strongest distress signals you get for free in this payload.
[ ] store the RAW response as evidence, not just your parsed fields.The gotcha: a bureau pull is itself a recorded enquiry. Pulling speculatively on browsing users, or pulling repeatedly while testing against real PANs, leaves marks on real people’s files. Use the sandbox for development, and pull once per application.
Account Aggregator — the consent flow
1. CREATE CONSENT REQUEST (you -> AA, via your TSP)
{customer_mobile, purpose_code, fi_types:["DEPOSIT"],
from_date, to_date, frequency, data_life, consent_expiry}
-> {consent_handle, redirect_url}
2. USER APPROVES (user -> AA app/webview)
They pick which accounts to share. THEY MAY PICK NONE.
3. CONSENT NOTIFICATION (AA -> your webhook)
{consent_handle, status: ACTIVE|REJECTED|EXPIRED}
4. REQUEST DATA (you -> AA)
{consent_id, from_date, to_date} -> {session_id}
5. FETCH (you -> AA)
GET /FI/fetch/{session_id} -> encrypted payload, you decrypt
WHERE IT ACTUALLY BREAKS
[ ] Step 2 drop-off is the single biggest number in this flow.
Instrument it per screen. It is usually 30-50% and it is mostly
a UX problem, not a consent problem.
[ ] The user may share ONE account of five. Check account count
against what your policy assumes before scoring.
[ ] from_date/to_date must be within what the FIP actually retains.
Asking for 24 months when the bank holds 12 returns 12, silently.
[ ] data_life and consent_expiry are DIFFERENT. data_life is how long
you may keep it; expiry is how long you may fetch. Set both
deliberately - DPDP purpose limitation applies.
[ ] Some FIPs are slow or intermittently down. Build for a partial
fetch and a retry, not a single synchronous call.Confirm whether AA connectivity is included in your statement-analysis pricing or billed as an add-on. Several vendors price AA-based fetch as a premium capability rather than a default, and it is discovered at contract stage rather than at demo.
How to use each one — statement analysis and modelling
Bank statement analysis
RESPONSE (shape is broadly common across vendors)
{
"accounts": [{
"bank": "...", "account_type": "SAVINGS",
"period": {"from": "...", "to": "..."},
"summary": {"total_credits":..., "total_debits":...,
"avg_monthly_balance":..., "min_balance":...},
"income": {"salary_credits": [...], "confidence": 0.9},
"obligations": {"emi_debits": [...], "monthly_emi":...},
"flags": {"bounces": n, "circular_transactions": bool,
"tampering_suspected": bool}
}]
}
WHAT TO CHECK
[ ] period actually covers what you asked for. A 4-month statement
scored against a 12-month policy is not a conservative decision,
it is an invalid one.
[ ] the analyser's salary detection is heuristic. Verify against
EPFO where the borrower is salaried - employer-confirmed beats
inferred every time.
[ ] tampering flags apply to UPLOADED PDFs. AA-sourced data comes
structured from the bank and bypasses that risk entirely -
which is the main reason to prefer AA over upload.
[ ] bounce/return counts: confirm the vendor's definition. Some count
inward returns only, some both directions. It changes the feature.
[ ] ask how many of the supported bank formats were UPDATED in the
last twelve months, not how many are "supported".Model tooling
import xgboost as xgb, shap, mlflow
from sklearn.model_selection import TimeSeriesSplit
# 1. SPLIT BY TIME, never randomly. Credit data has a time dimension
# and random splits leak future information into training.
tscv = TimeSeriesSplit(n_splits=5)
# 2. TRAIN
model = xgb.XGBClassifier(
max_depth=4, # shallow. deep trees overfit credit data
n_estimators=300,
learning_rate=0.05,
scale_pos_weight=neg/pos, # defaults are ~2-8% positive
eval_metric="auc")
# 3. REGISTER IT - this is your model inventory entry (Module 09)
mlflow.set_experiment("pd_unsecured")
with mlflow.start_run():
model.fit(X_tr, y_tr)
mlflow.log_params({"population": "salaried_tier1_2",
"approved_use": "personal_loan_unsecured",
"prohibited_use": "any other product"})
mlflow.xgboost.log_model(model, "model")
# 4. EXPLAIN - TreeSHAP exact, not the sampling approximation
explainer = shap.TreeExplainer(model) # exact for trees
sv = explainer.shap_values(X_te) # -> reason codes
# WHAT TO CHECK
# [ ] out-of-TIME performance, not just out-of-sample
# [ ] performance by segment, not just overall
# [ ] benchmark against logistic regression. If the gain is small,
# the explainability burden may not be worth it.
# [ ] every feature mapped to a reason code (CI assertion in the module)The gotcha: scale_pos_weight changes the shape of the predicted probabilities. If you set it and then treat the output as a calibrated probability of default, your cut-offs mean something different from what you think. Calibrate explicitly, or hold the threshold on rank rather than on the raw score.
Cost per unit
Indian credit data — typical negotiated ranges
Verified May 2026There is no public rate card. Bureau pricing is negotiated on signing a member agreement, and the full-stack vendors run enterprise sales only — no self-serve sandbox, no public API key, no per-call price page. Treat these ranges as a negotiating benchmark, not a quote.
The number that actually matters is cost per approval, not per application. At a 20% approval rate, ₹60 of pulls per application becomes ₹300 per approved loan. Ordering your waterfall cheapest-first — score-only before full report, bureau before AA — is a commercial decision, not engineering tidiness.
Best combinations
| Combination | Works because |
|---|---|
| Hard rules → score-only pull → full report only if in contention | Declines the obvious cheaply. The score-only pull is a fraction of a full report and answers most cases. |
| Bureau hit → standard path · bureau no-hit → AA + GST/EPFO path | Thin file is an unknown borrower, not a bad one. Two paths, not one decline. |
| AA-sourced statements + EPFO cross-check | AA removes tampering risk; EPFO confirms employer income rather than inferring it. |
| GBM score + explicit policy layer | Model gives probability, policy gives the decision. Defensible as policy rather than "the model said no". |
| EBM challenger alongside the GBM | Tells you continuously whether the opacity is buying you anything. |
Combinations that conflict
- Pulling every source for every applicant. Multiplies cost per approval and adds nothing to obvious declines.
- Uploaded PDF statements for a high-risk product. Tampering risk that AA removes entirely.
- A model decisioning directly, with no policy layer. Harder to explain and harder to change.
- Random train/test split on credit data. Leaks the future; the model looks better than it is.
- Cut-offs carried across a bureau score version change. Silent recalibration of your whole book.
Three recommended builds
Strong and expensive
Build: Full-stack lending platform — LOS, BRE, LMS, bureau and AA plumbing, statement analysis and decisioning from one vendor.
Use when: no data science team, launching fast, partnering with an NBFC that expects a known stack.
Cost shape: enterprise contract plus per-pull pass-through with markup.
Trade: the credit model is theirs. Your repayment data accumulates inside someone else’s system, which is the exact asset that was meant to become your moat.
Strong and reasonable — the default
Build: Aggregator for bureau and AA plumbing → vendor statement analysis → your own features, your own model, your own policy layer → MLflow as the inventory.
Use when: you have one or two people who can own a model and you intend to lend at scale.
Cost shape: per-pull data costs plus a small platform cost; the model is yours.
Trade: you own model risk, validation and monitoring — which is what Module 09 is for.
This is the recommended build for almost every lender that intends to survive. Buy the plumbing, own the model. The plumbing is commodity; the repayment history you accumulate is the only thing competitors cannot buy.
Strong and lean
Build: Partner NBFC pulls bureau under their membership → AA through one licensed AA → statement parsing in-house → logistic scorecard with OptBinning → policy rules.
Use when: pre-licence, proving the model works before spending on capital or enterprise contracts.
Cost shape: lowest. A scorecard costs nothing to run and is trivially explainable.
Trade: lower accuracy ceiling, and you inherit the partner’s risk appetite. Acceptable while proving demand; not a permanent position.
All three depend on a licensed entity somewhere, because bureau membership requires being an RBI-registered Credit Institution and AA participation requires FIU registration. And all three carry the reporting obligation — you must submit data back to at least one CIC, on their format and cycle.
What next
An approved application produces a loan. Four things follow:
- Fraud — first-payment default sits between credit and fraud and needs both. A credit model assumes the applicant is real.
- Payments — disbursal, mandates, EMI collection and the reconciliation that proves where the money went.
- Customer operations — servicing, hardship, and collections under conduct rules that constrain what may be said and when.
- Governance — the PD model, the statement analyser and the vendor score are all models. All in the inventory, all tiered, all with a kill switch and a fallback.
And the thing to start on day one: capture outcomes against decisions, and approve a small random slice above your cut-off. Neither can be reconstructed later, and together they are the difference between a lender who knows their risk and one who is extrapolating.
Sources
Every figure, rule and date on this page, and where to check it. Entries are typed so you can see which numbers are primary-sourced and which are industry reporting — they are not equivalent, and treating them as if they were is how a confident wrong number gets repeated.
- officialRBI Digital Lending Directions — the regulated-entity obligations, disclosure requirements and the LSP relationship. www.rbi.org.in
- officialAccount Aggregator framework — the consent architecture for fetching financial data. sahamati.org.in
- officialCredit Information Companies (Regulation) Act — the bureau reporting and dispute obligations. www.rbi.org.in
- industryBureau and alternative data pricing — per-pull costs and alternative data vendor ranges. Compiled; contracts vary widely by volume.
Checked May 2026. Pricing and draft regulation move; the date is part of the claim.
Ask an AI about this page
Opens your assistant with this page as the source, and a question rather than a summary. It will ask what you are building before it answers.
Nothing is sent from here. The link carries only this page’s title and address.