Design an instant loan approval pipeline
The interviewer asks
“Design the backend for an instant personal loan product: a user applies in the app, and within about two minutes gets a decision and an offer, signs the agreement and receives money in their bank account.”
What a strong answer looks like
A strong answer models the application as a long-running, resumable workflow, not one big synchronous API call. The workflow gathers data from slow, costly and unreliable third parties: KYC (PAN, Aadhaar-based eKYC or CKYC), the credit bureau report, bank statements via the Account Aggregator framework, and device and fraud signals. It then builds features, scores the applicant with a versioned ML model plus a policy rules engine, and produces an explainable decision and offer. Accepted offers go through a Key Fact Statement, e-sign, an e-mandate for EMIs, and disbursal by the regulated lender directly to the borrower's bank account, as RBI's digital lending rules require. The hard parts are orchestrating the vendor calls with retries and caching, keeping every decision reproducible for audit, and keeping fraud out without slowing honest users.
Concepts to mention
1 Requirements
Functional
- User applies with basic details and consent; the app shows progress through steps
- Verify identity: PAN check, Aadhaar-based eKYC or CKYC, selfie liveness and face match
- Pull the credit bureau report and, where needed, bank statements through the Account Aggregator framework
- Score the application with policy rules and an ML model; decide approve, reject or refer to manual review
- Generate an offer (amount, tenure, interest rate, fees) and a Key Fact Statement (KFS)
- Collect e-sign on the loan agreement and set up an e-NACH or UPI AutoPay mandate for EMIs
- Disburse to the borrower's verified bank account and notify them
- Keep a complete, reproducible record of every decision for audit and customer queries
Non-functional
- Decision for ~80% of applications in under 2 minutes end to end; scoring step itself under 500 ms
- Every application resumable after app close or a server crash, with no step done twice that costs money
- Peak ~200 applications/s during campaigns and festive sales; ~2M applications/day
- Decision reproducibility: the exact inputs, model version and rule version stored for at least the loan's life plus years after
- Data stored in India; PII encrypted; consent captured for each data pull
- 99.9% availability for the application flow; partner outages degrade, not break, the flow
Out of scope (say it out loud)
- Loan servicing after disbursal (EMI collection, collections, foreclosure)
- Model training pipeline in detail (mentioned only)
- Co-lending arrangements between multiple lenders
- The lender's core banking or loan management system internals
2 Estimates
3 API
| Endpoint | What it does |
|---|---|
POST/applications | Start an application with requested amount and consent ids. Header Idempotency-Key. Returns application_id and the next step |
GET/applications/{id} | Current step, status and what the user must do next (poll or receive via push) |
POST/applications/{id}/kyc | Submit KYC inputs (PAN, eKYC OTP reference, selfie upload URL) |
POST/applications/{id}/aa-consent | Start Account Aggregator consent; returns a redirect to the AA app |
POST/applications/{id}/offer/accept | Accept an offer variant (amount, tenure); generates KFS and agreement |
POST/applications/{id}/esign | Start e-sign and mandate setup; callbacks complete the step |
POST/webhooks/{partner} | Callbacks from AA, e-sign, mandate and disbursal partners (signature verified, idempotent) |
gRPCDecisionService.Score | Internal: features in, decision + reasons + model and rule versions out |
4 Data model
applicationsPostgresapplication_id (PK)user_idproduct_idrequested_amountstatuscurrent_stepworkflow_idlender_idcreated_atupdated_atIndex on user_id and status. One active application per user per product, enforced by a partial unique index.
vendor_callsPostgrescall_idapplication_idvendor (bureau, ckyc, aa, esign)request_hashstatusresponse_ref (S3)costlatency_mscreated_atEvery external call logged with cost. request_hash + user lets you reuse a recent bureau report instead of paying for a new pull.
raw_documentsS3 (encrypted, India region)bureau reportsAA statementsKYC imagessigned agreement PDFsObject lock (WORM) for signed agreements and KFS; lifecycle rules per data type and retention policy.
decision_recordsPostgres + S3 snapshotdecision_idapplication_idfeature_vector_refmodel_versionruleset_versionscoredecisionreason_codesofferdecided_atImmutable. Allows replaying the exact decision and explaining rejections with reason codes.
feature_store (online)Redis / DynamoDBuser_id → precomputed features (app behaviour, repayment history on platform, pre-approved limit)Low-latency lookups at scoring time. Offline store (Iceberg on S3) holds point-in-time history for training.
loansPostgres (lender LMS is the system of record)loan_idapplication_idlender_loan_refprincipalratetenuremandate_iddisbursal_utrstatusMirror of the lender's record for the app; reconciled daily with the lender's loan management system.
5 Architecture
Hover a box to highlight its connections.
6 Request flows
Apply and get a decision
- 1User taps 'Get loan', gives consent and requested amount. Application service creates the application (idempotent) and starts a workflow with workflow_id = application_id.
- 2Workflow runs cheap checks first: eligibility (age, city, existing loan), a negative list, and device risk (rooted device, emulator, many accounts on one device).
- 3KYC: PAN verification and CKYC search; if no CKYC record, Aadhaar-based eKYC with OTP and a selfie liveness check with face match.
- 4Bureau: the adapter checks vendor_calls for a report for this user pulled in the last N days; if none, it pulls a fresh report and stores it in S3.
- 5If the bureau file is thin or the amount is high, the workflow asks the user for Account Aggregator consent and waits for a signal (with a timeout) while the user approves in the AA app.
- 6Feature builder combines bureau, AA statement features (salary credits, bounces, average balance), platform behaviour and device signals.
- 7Decision service runs policy rules (hard rejects), then the ML model for probability of default, then pricing and limit rules; it writes an immutable decision record with reason codes.
- 8Outcome: approve with offers, reject with reason category, or refer to manual review queue. The app receives the result by push and on its next poll.
Accept offer and disburse
- 1User picks ₹1,50,000 for 24 months. The system generates the Key Fact Statement with the all-in APR and the agreement from versioned templates.
- 2User e-signs (Aadhaar OTP based e-sign); the signed PDF is stored in S3 with object lock.
- 3User sets up an e-NACH or UPI AutoPay mandate for EMIs; the workflow waits for the mandate success callback.
- 4Workflow calls the lender's LMS to book the loan with an idempotency key = application_id, then requests disbursal to the user's verified bank account (penny-drop verified earlier).
- 5Lender disburses via IMPS/NEFT and returns a UTR; the workflow records it, emits 'loan.disbursed' and the user is notified.
7 Deep dives
Workflow orchestration for slow, paid, flaky vendors
An application spends most of its time waiting: for vendors (bureau p99 can be several seconds), for the user (OTPs, AA consent, e-sign), and for callbacks. A synchronous request chain would time out and lose progress. Use a durable workflow engine like Temporal: the workflow code describes steps, and the engine persists every step result, so a crash resumes from the last completed step without repeating it. That last part matters because bureau pulls and eKYC calls cost money and some leave a footprint (a hard inquiry on the credit report). Each activity has its own timeout and retry policy: retry network errors with exponential backoff, but never retry a 'no record found' or a business rejection. Wait for user actions with signals plus timers ('if no AA consent in 30 minutes, decide without AA or send a reminder'). Order steps by cost: run free and fast checks (eligibility, device, negative list) before paid ones, and stop early on hard rejects. Put each vendor behind an adapter with a circuit breaker and a fallback vendor where possible (two bureaus, two KYC providers). If you do not want Temporal, a Postgres step table with a worker pool and a scheduler is a valid answer at this scale; the key ideas are persisted progress and idempotent steps.
Decisioning: rules plus model, versioned and explainable
Use layers. First, policy rules that lenders and regulators require: minimum age, serviceable pin codes, bureau score cut-offs, no recent write-offs, maximum debt-to-income. These are hard rejects and must be readable by risk teams, so keep them in a rules engine with versioned rule sets, not in code. Second, an ML model (often gradient-boosted trees, because they are accurate on tabular data and explainable with SHAP values) predicts probability of default from features. Third, pricing and limit rules map the risk band to an offer: amount, tenure options and interest rate. Every decision writes an immutable record: the feature vector, model version, rule set version, score, reason codes and offer. This gives reproducibility (re-run the same model on the same features and get the same answer), adverse-action reasons for rejected users, and audit support. Roll out new models in shadow mode first (score but do not act), then champion-challenger on a small traffic slice, and watch approval rate and early delinquency. Feature consistency matters: compute features with the same code online and offline, and store point-in-time snapshots so training does not use data that was not available at decision time (leakage).
Fraud and identity risk
Instant loans attract organised fraud: stolen identities, synthetic identities, and 'first-party' fraud where the borrower never intends to pay. Most fraud is caught by signals around the application, not the bureau score. Device intelligence: emulator or rooted device, one device used by many applicants, SIM recently changed. Identity: face match between selfie and KYC photo, liveness, name match across PAN, bank account (penny drop) and KYC. Graph signals: shared phone numbers, addresses, devices or bank accounts across applications; a graph store or simple linked-entity tables with counts catch fraud rings. Behaviour: typing patterns, time spent on the form, and applying from a location far from the KYC address. Velocity: many applications in a short time from the same IP range or pin code. Score fraud separately from credit risk, run it before paid bureau pulls where possible, and send borderline cases to manual review with all evidence on one screen. Feed outcomes back: loans that default in the first EMI (first payment default) are the best fraud labels, and they arrive 30-60 days later, so the training pipeline must handle delayed labels.
Regulatory guardrails built into the design
In India, RBI's digital lending guidelines shape the architecture. Disbursal and repayment must flow directly between the regulated lender's account and the borrower's bank account, not through the app's pool account, so the app calls the lender's LMS to disburse rather than moving money itself. The borrower must get a Key Fact Statement with the all-in annual percentage rate before signing, and a cooling-off period to exit without penalty, so the KFS generator and an 'exit' workflow branch are part of the system. Data collection needs explicit, purpose-specific consent, and apps should not access contacts or media for lending, so consent ids are stored with every data pull and the feature builder must not use unconsented data. Data must be stored in India. The Account Aggregator framework itself is consent-based: the AA never sees decrypted data, and the consent has a purpose, a date range and an expiry. In the interview, keep this short: name these constraints and show where they appear in the design. That shows you can build regulated systems, which fintech interviewers care about a lot.
8 Trade-offs
Synchronous API vs async workflow with polling/push
A synchronous 'apply and wait' API is simple but breaks when any step takes more than a few seconds or needs user action. An async workflow with a status endpoint and push notifications handles minutes-long flows, app restarts and partner delays. The cost is more moving parts and a UI that must show progress states.
Reuse a recent bureau report vs always pull fresh
Reusing a report pulled in the last few days saves cost and avoids extra hard inquiries. A fresh pull catches new loans taken in between, which is a known fraud pattern (loan stacking). Common policy: reuse within a short window for the same product, pull fresh before disbursal for large amounts.
Build the model in-house vs vendor scores
Bureau scores are a good start but generic. In-house models using platform data (transaction history, repayment behaviour) are much better for your users, but need data science, monitoring and governance. Start with bureau score plus rules, add your own model once you have enough repayment outcomes.
9 Bottlenecks
Vendor rate limits during campaigns
At 10x applications, bureau and KYC vendors throttle first. Queue applications at the vendor's allowed rate with per-vendor token buckets, show 'checking your details' in the app, and spread load across two vendors. Pre-approved users (scored in batch overnight from existing data) can skip parts of the flow.
Manual review queue
If rules send too many cases to review, the queue grows and decisions take days. Track referral rate as a metric, tune thresholds, and prioritise the queue by expected value (larger, likely-good loans first).
10 Failure handling
Bureau down
The circuit breaker opens; the workflow waits and retries with backoff, or uses the fallback bureau. Applications are not rejected for a vendor outage; users are told the decision will come shortly by notification.
Disbursal callback never arrives
The loan stays in DISBURSING. The workflow queries the lender for status with the same idempotency key; it never sends a second disbursal request with a new key. Daily reconciliation against the lender's disbursal file closes the gap.
Bad model deployed
Monitor approval rate, score distribution and feature drift in real time; a sudden jump triggers an automatic rollback to the previous model version, which is just a config change because models are versioned and the decision service loads by version.
11 What impresses
- Treating the application as a resumable workflow that waits on users and vendors
- Ordering steps by cost and stopping early on hard rejects
- Storing model version, rule version and features for every decision for reproducibility
- Separating fraud risk from credit risk and using graph signals for rings
- Naming RBI digital lending constraints and showing where they live in the design
- Handling delayed labels (defaults arrive weeks later) in the model feedback loop
12 Mistakes
- One synchronous API that calls every vendor in sequence
- Retrying paid vendor calls without idempotency, paying twice or creating multiple hard inquiries
- Hard-coding credit rules in application code
- No audit trail of why a user was rejected
- Ignoring fraud and treating credit score as enough
- Moving loan money through the app's own account
13 Follow-ups
Lessons this design uses
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.