Design problems
Platform & infrastructureHardSDE-3 35 min

Design eKYC identity verification

Asked at
CREDGrowwPaytmPhonePePolicyBazaarZerodha

The interviewer asks

“Design the digital KYC (know your customer) flow for an Indian investing or lending app. A new user verifies PAN, Aadhaar (via DigiLocker or OTP eKYC), a selfie with liveness, and a bank account, and is approved within minutes when everything matches, with manual review or video KYC when it does not. Signups jump 10× on the day of a hot IPO.”

What a strong answer looks like

A strong answer models KYC as a long-running, resumable workflow, not a request. A durable orchestrator (Temporal or a state machine on a database) runs steps that each call an external verifier: PAN check, DigiLocker or Aadhaar OTP eKYC through a licensed partner, CKYC/KRA lookup to reuse existing KYC, selfie liveness and face match against the Aadhaar photo, penny-drop bank verification with name match, and sanctions screening. Every step is idempotent with stored results so retries and app restarts never re-charge vendors or lose progress. A rules engine decides auto-approve, reject or manual review; uncertain cases go to a review console or a video KYC call. PII is the core risk: Aadhaar numbers live only in an encrypted vault and are referenced by token, documents sit in encrypted object storage, and every access is audited. Vendor rate limits, failover and an IPO-day queue complete the design.

Concepts to mention

Durable workflow with activities, signals and timersIdempotent vendor calls with stored resultsPAN, DigiLocker / Aadhaar eKYC, CKYC/KRA reuseLiveness and face match against the Aadhaar photoPenny drop with fuzzy name matchingRules engine with auto-approve, reject and reviewAadhaar Data Vault and tokenisation of PIIEncryption, data localisation and audit of every accessCircuit breakers, rate limits and multi-vendor failoverHuman-in-the-loop review with SLAs

1 Requirements

Functional

  • Collect and verify PAN (name, date of birth, status, PAN-Aadhaar link)
  • Verify Aadhaar identity and address through DigiLocker or OTP-based eKYC via a licensed partner
  • Selfie with liveness detection and face match against the Aadhaar photo
  • Bank account verification by penny drop, with fuzzy name match against PAN name
  • Reuse existing KYC from the CKYC registry or a KRA where available; upload new KYC records after approval
  • Sanctions and PEP screening; risk categorisation (low, medium, high)
  • Auto-approve, auto-reject or route to manual review; video KYC (V-CIP) when required
  • Users can leave and resume where they stopped, on any device

Non-functional

  • Happy-path completion under 5 minutes of user time; decision p95 under 60 s after the last step
  • Scale: 50k onboardings/day normally, 500k on a hot IPO day (peak ~30 starts/s)
  • No lost progress and no duplicate paid vendor calls on retries
  • PII encrypted at rest and in transit; Aadhaar number never stored outside a vault; all data stored in India
  • Full audit trail of every check, decision and data access, kept 5+ years after the relationship ends (PMLA)
  • Graceful degradation when a vendor or government API is slow or down

Out of scope (say it out loud)

  • Ongoing transaction monitoring for AML
  • Credit underwriting
  • Building our own face recognition models

2 Estimates

Onboarding starts~0.6/s normally, ~30/s at IPO peak50k/day ÷ 86,400 ≈ 0.6/s; 500k on IPO day concentrated in ~6 busy hours with a 3× peak hour ≈ 23-30/s
Vendor calls~8-10 per onboarding, ~300/s at peakPAN, DigiLocker (2-3 calls), CKYC search + download, liveness, face match, penny drop, sanctions; ×30 starts/s
Vendor cost~₹30-80 per completed KYCSum of per-call prices (PAN ~₹1-3, penny drop ~₹2-5, liveness/face ~₹3-10, CKYC download, V-CIP far more) plus retries; why duplicate calls must be avoided
Document storage~2 MB per user, ~1 TB/day on IPO daySelfie frames, Aadhaar XML/photo, PAN image, signature; 500k × 2 MB; kept for the retention period in encrypted S3 (Mumbai/Hyderabad region)
Manual review load~5-10% of casesName mismatches, low face-match scores, poor images; 500k × 7% = 35k reviews on IPO day, which is why auto-decision quality matters

3 API

EndpointWhat it does
POST/v1/kyc/applicationsStart or resume KYC for the user; returns application_id and next step
POST/v1/kyc/applications/{id}/panSubmit PAN and DOB; idempotent per application
GET/v1/kyc/applications/{id}/digilocker/startReturns the DigiLocker consent URL; callback brings the signed Aadhaar XML
POST/v1/kyc/applications/{id}/selfieUpload liveness video/frames via pre-signed S3 URL, then trigger checks
POST/v1/kyc/applications/{id}/bankAccount number + IFSC for penny drop
GET/v1/kyc/applications/{id}Status, completed steps, pending action (for the app to resume)
POST/v1/review/cases/{id}/decisionReviewer approves, rejects or requests re-upload, with reason codes

4 Data model

kyc_applicationsPostgres
application_iduser_idstatuscurrent_steprisk_leveldecisiondecision_reasonsworkflow_idcreated_atupdated_at

Business view of progress; the workflow engine holds execution state. One active application per user (unique partial index).

verification_resultsPostgres
application_idcheck_typevendorrequest_idempotency_keystatusscorenormalised_result jsonbraw_ref (S3)created_at

One row per check attempt; the idempotency key (application_id + check_type + input hash) makes repeats return the stored result instead of calling the vendor again.

pii_vaultDedicated encrypted store (Aadhaar Data Vault) with HSM-backed keys
token → encrypted Aadhaar number / PAN / DOB / address

Only the vault service can decrypt; other services see tokens and masked values (last 4 digits). Separate access policy and audit.

documentsS3 (SSE-KMS, India region)
application_id/{selfieaadhaar_xmlpan_imagesignature}

Uploaded via pre-signed URLs; object lock for retention; lifecycle to cheaper tiers.

review_casesPostgres
case_idapplication_idreasonpriorityassigneesla_due_atstatus

Work queue for reviewers with SLA tracking.

audit_logAppend-only store (e.g. Postgres with no-update role, or QLDB-style ledger)
actoractionapplication_idfields accessedtimestampip

Every read of PII and every decision, for regulators and internal investigations.

5 Architecture

CLIENTSEDGESERVICESASYNCDATAEXTERNALstart / signalactivitiesverify, idem keyliveness, matchtokenise PIIresultspre-signed uploaddecidedecision signalevery accessReview consoleMobile / web appAPI gatewayKYC APIDecision rules enginePII vault serviceTemporal (KYCworkflow)Verification workersDoc extraction + faceAIPostgres (apps,results)Audit logS3 encrypted documentsPAN / DigiLocker /CKYCPenny drop / screening
ClientEdge / gatewayService / workerQueue / streamCacheDatabase / storageAI / modelThird partyrequestasync (event / queue)

Hover a box to highlight its connections.

6 Request flows

Happy path: approved in four minutes

  1. 1User starts KYC; the API creates the application and starts a Temporal workflow with workflow_id = application_id (so a double tap cannot start two)
  2. 2User enters PAN and DOB; the workflow's PAN activity verifies status, name and Aadhaar-seeding via a PAN verification partner and stores the normalised result
  3. 3In parallel it searches the CKYC registry; if a record exists, address and photo can be reused with consent, skipping some steps
  4. 4User completes DigiLocker consent; the callback signals the workflow, which fetches the signed Aadhaar XML, verifies its signature, stores the Aadhaar number in the vault and keeps only a token and masked number
  5. 5User records a 3-second selfie video; liveness and face match against the Aadhaar photo return scores of 0.97 and 0.91
  6. 6Penny drop of ₹1 returns the account holder's name; a fuzzy name match against the PAN name scores 0.94
  7. 7Sanctions screening is clear; the rules engine sees all checks pass with high scores and auto-approves
  8. 8Workflow uploads the KYC record to CKYC/KRA as required, emits KycApproved, and the account is activated

Name mismatch goes to review

  1. 1PAN name is 'RAMESH KUMAR SHARMA', bank name is 'R K SHARMA'; name match scores 0.72, below the 0.85 auto threshold
  2. 2Rules engine returns 'review'; workflow creates a review case with all evidence and waits for a ReviewDecision signal (timeout 48 h)
  3. 3Reviewer sees masked data side by side and approves with reason 'initials'; the console sends the signal and the decision is audited
  4. 4If no decision in 48 h, the workflow escalates priority; the user sees 'Under review' with an ETA

Vendor outage on IPO day

  1. 1Face match vendor starts timing out; the activity's retry policy backs off and the circuit breaker opens
  2. 2Workflows wait durably on that step instead of failing; the app tells users 'We'll notify you when verification completes'
  3. 3Traffic shifts to the secondary vendor for new checks; when the primary recovers, waiting workflows resume automatically

7 Deep dives

KYC as a durable workflow

A KYC journey takes minutes to days: the user drops off after PAN and returns tomorrow on another phone, DigiLocker consent happens in a browser redirect, a reviewer decides after hours, a vendor is down for 20 minutes. A request-response design with a status column and cron jobs ends with many half-finished states and nobody sure which checks ran. A durable execution engine such as Temporal makes the whole journey one piece of code whose progress is persisted: each verification is an activity with its own retry policy and timeout; user actions and reviewer decisions arrive as signals; waits ('wait 48 h for review, remind at 24 h') are durable timers that survive restarts. Rules from the guide apply: workflow code must be deterministic (no clock or network calls directly), activities must be idempotent because they will be retried, and large payloads such as images stay in S3 with only references in workflow history. Use workflow_id = application_id so a second start returns the running workflow. Version workflow code carefully, since thousands of applications are mid-flight during every deploy.

PII protection and Aadhaar rules

This system holds the most sensitive data a company has, so design for breach containment. Aadhaar numbers, where you are permitted to collect them at all, must be stored only in an Aadhaar Data Vault: a separate, encrypted store with keys in an HSM, accessed through one service that returns a reference token. Everything else (applications, logs, analytics, reviewer screens) sees the token and the last four digits. DigiLocker or offline eKYC XML is digitally signed by UIDAI, so verify the signature instead of trusting the payload. Encrypt documents in S3 with KMS keys, restrict decryption to the services and roles that need it, and keep all data in Indian regions. Apply PII redaction in logging middleware and in any prompt sent to an LLM. The Digital Personal Data Protection Act requires purpose-limited use and deletion when no longer needed, while PMLA rules require KYC records for five years after the relationship ends, so retention is a policy per field, enforced by lifecycle rules, not ad hoc deletes. Every PII read writes an audit event with actor and reason.

Decisions: rules, scores and where AI fits

Each check returns a normalised result: pass, fail or a score with reasons. A rules engine (versioned config, not code scattered across services) combines them: auto-approve when PAN is valid and linked, Aadhaar signature verified, liveness above 0.9, face match above 0.85, name match above 0.85 and screening clear; auto-reject on hard failures such as a PAN that does not exist; everything else goes to review. Thresholds are tuned with data: track false approvals found by audits and the review rate, since every 1% more review costs thousands of reviewer hours on IPO day. AI helps in three places: document extraction for unstructured address proofs, liveness and face matching (from specialised vendors), and drafting review summaries. Keep it out of the final decision path: an LLM reading a document can be prompt-injected by text printed on that document, and its output varies. Use structured extraction with field-level confidence, cross-check extracted values against authoritative sources (PAN database, signed XML), and send low-confidence results (below 0.9) to humans. Record the model and prompt version used in the verification result for audit.

External verifiers: idempotency, rate limits and failover

Most of the system's latency, cost and failures come from outside: PAN verification partners, DigiLocker, CKYC, KRAs, penny-drop banks, liveness vendors and screening lists. Every call goes through an activity that first checks verification_results for the same idempotency key (application_id + check_type + input hash) and returns the stored result if present, so a retry after a timeout or a user re-submitting the same PAN does not pay twice. Where vendors support client reference ids, pass them so the vendor can dedupe too. Wrap each vendor with timeouts, a circuit breaker and a per-vendor token bucket sized to its contracted rate limit. Keep a second vendor for critical checks (face match, penny drop) and route by health; normalise responses behind one internal interface so switching is configuration. On IPO day, queue onboarding work instead of hammering vendors: the workflow simply waits for capacity, and the app shows progress and notifies the user when done. Many vendor results arrive by callback, so the incoming webhook design (verify, store, signal the workflow) is reused here.

8 Trade-offs

Temporal vs a home-grown state machine

Temporal gives retries, timers, signals and visibility out of the box, but adds a cluster to run and a programming model to learn. A Postgres state machine with an outbox and job queue is simpler for 4-5 steps. With many vendors, human review and multi-day waits, the durable engine pays for itself.

DigiLocker vs Aadhaar OTP eKYC vs offline XML

DigiLocker returns signed documents with user consent and is widely used by brokers and lenders. OTP eKYC through a licensed KUA partner is fast but RBI limits what accounts opened with OTP-based eKYC alone can do until full KYC is completed. Support more than one path so an outage in one does not stop onboarding.

Auto-decision thresholds

Higher thresholds reduce fraud but send more people to manual review, raising cost and drop-off. Lower thresholds speed up onboarding but risk regulatory findings. Tune with measured false-approve and review rates, and use stricter thresholds for high-risk segments.

Sync steps in the app vs async completion

Showing each result instantly feels good but ties UX to vendor latency. Do the fast checks synchronously (PAN in ~1 s) and let slow ones finish async with a push notification.

9 Bottlenecks

Vendor rate limits on IPO day

Contracted TPS caps throughput, not our servers. Negotiate burst capacity before known events, multi-vendor routing, and a durable queue so excess users wait rather than fail.

Manual review backlog

35k cases in a day can overwhelm the team. Prioritise by risk and customer value, auto-resolve common patterns (initials vs full name) with better matching, and add temporary reviewers with scoped access.

Temporal history size

Storing images or large XML in workflow history slows replays. Keep only S3 references and small normalised results in history.

10 Failure handling

DigiLocker down

Offer an alternative path (Aadhaar OTP eKYC via partner, or CKYC reuse). Users already in the DigiLocker step get a 'try again later' with a notification when it recovers.

Worker crash mid-verification

Temporal reschedules the activity; the idempotency check returns the stored vendor result if the call had completed, so there is no duplicate charge or duplicate penny drop.

Vault unavailable

Steps that need to store or read raw identifiers pause and retry; nothing writes raw PII elsewhere as a shortcut. Onboarding slows but data protection is never relaxed.

Bad rule deployed

Rules are versioned and every decision records the rule version, so affected applications can be found and re-evaluated. Roll out rule changes as a shadow evaluation first.

11 What impresses

  • Treating KYC as a resumable workflow and choosing durable execution for it
  • Knowing the Aadhaar vault and masking requirements and verifying UIDAI signatures
  • Idempotency keys to prevent paying vendors twice
  • Keeping LLMs out of the final decision and noting prompt injection via documents
  • Balancing DPDP deletion against PMLA retention per field
  • Planning IPO-day surges around vendor limits, not server capacity

12 Mistakes

  • Doing every check synchronously in one API call
  • Storing Aadhaar numbers or full PII in the main database and logs
  • Retrying vendor calls without idempotency, causing double charges and double penny drops
  • One vendor per check with no fallback
  • Letting an AI model approve or reject on its own
  • No audit trail of who viewed or decided what

13 Follow-ups

Q1How would you add video KYC with a live agent, and what must be recorded?
Q2How do you re-KYC users periodically based on their risk level?
Q3How would you detect the same face being used for many accounts?
Q4How would you support NRIs without Aadhaar?
Q5How do you measure and reduce drop-off in the funnel?
Q6A regulator asks for all decisions made under a faulty rule last month. How do you answer?

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.

Start free