EKYC 3.0 — Stage 2: OTP Verification, PAN Lookup & KRA Check

Backend-Only Developer Implementation Guide • OTP Flow + Background Check Pipeline + Vendor Integrations
Stage ID: OTP_VERIFIED Trigger: Customer submits 4-digit OTP BRD Section: 05 — Stage 2 Version: 1.0

Table of Contents

  1. Stage 2 Objective
  2. Preconditions
  3. Backend Flow — OTP Verification
  4. Background Check Pipeline (Async)
  5. Backend Validations
  6. OTP Specification
  7. Background Check Decision Tables
  8. Existing Stored Procedure Mapping
  9. Error / Drop Codes
  10. Exit State
  11. Vendor & Integration Calls
  12. Database Tables (New Backend)

1. Stage 2 Objective

Customer verifies the 4-digit OTP delivered via SMS. After successful verification, the platform fires background checks asynchronously — the customer does not wait and immediately proceeds to Stage 3.

Background Check Pipeline (Async) The checks execute in a strict dependency chain: Zintlr → (Hyperverge + C-safe in parallel) → (NSDL + CVL KRA in parallel). UTI is a backup only — it is called exclusively when NSDL is DOWN.
Key Principle OTP verification is synchronous and blocking. All background checks (PAN lookup, KRA, C-safe) are asynchronous and non-blocking. The customer never waits for background checks to complete.

2. Preconditions

ConditionDetail
lead.state = INITIATEDLead must have been created in Stage 1 with state INITIATED
Valid OTP in server memory (in-memory store keyed by mobile number)OTP must exist in in-memory OTP store with TTL of 5 minutes, keyed by mobile number
Active session from Stage 1Session token issued during Stage 1 registration must still be valid

3. Backend Flow — OTP Verification

1
Validate OTP against in-memory OTP store for mobile number. Retrieve the stored OTP from in-memory store using the mobile number as key. Compare the submitted 4-digit OTP with the stored value.
2
OTP correct → Set lead.state = OTP_VERIFIED. Record mobile_verified_at timestamp. Customer moves to Stage 3 immediately. Remove OTP from in-memory store.
3
OTP incorrect → Increment attempt_count in in-memory store. Return error response with remaining attempts (e.g., "3 attempts remaining"). Do not change lead state.
4
5th wrong attempt → OTP locked. Set lead.state = DROPPED with reason DROP_OTP_LOCKED. Remove OTP from in-memory store. Journey is terminated.
5
OTP expired → Allow resend. Maximum 3 resends in a 30-minute window with a 30-second cooldown between resends. Each resend generates a new 4-digit OTP and resets the 5-minute TTL.
6
On success: Fire background checks asynchronously. After setting OTP_VERIFIED, dispatch the background check pipeline via async job queue. The customer does NOT wait for these to complete.
Critical: OTP Lock is Permanent Once a lead reaches 5 wrong OTP attempts, the lead is DROPPED with DROP_OTP_LOCKED. There is no unlock mechanism. The customer must restart the entire journey with a new lead.

4. Background Check Pipeline (Async, After OTP Verified)

All background checks run asynchronously after OTP verification succeeds. They follow a strict dependency chain:

Step 1: Zintlr ONLY (needs plain mobile number)
         |
         v  output = PAN number
Step 2: Hyperverge + C-safe in PARALLEL (both need PAN)
         |
         v  Hyperverge output = Name + DOB
Step 3: NSDL + CVL KRA in PARALLEL
         |
         v  If NSDL returns non-success OR throws exception --> UTI fallback
         UTI is backup ONLY, never called if NSDL succeeds

Step 1: Zintlr — Phone to PAN

1
Call Zintlr API with the customer's plain mobile number. Zintlr returns the PAN number associated with that mobile. If Zintlr fails or returns no PAN, the pipeline continues — PAN will be collected manually at Stage 4.

Step 2: Hyperverge + C-safe (Parallel)

Both calls require the PAN from Step 1. If Step 1 returned no PAN, these calls are skipped.

2a
Hyperverge — Call with PAN. Returns pan_name and pan_dob. These are stored for pre-fill at Stage 4.
2b
C-safe — Call with PAN. Returns PEP/sanctions screening result. Stored as csafe_result. Non-blocking for the customer journey.

Step 3: NSDL + CVL KRA (Parallel)

Both calls run in parallel. NSDL validates the PAN. CVL KRA checks the KRA registration status.

3a
NSDL / Protean — Validate PAN against NSDL database. Store result as nsdl_pan_valid = true/false. If NSDL returns non-success OR throws an exception, trigger UTI fallback.
3b
CVL KRA — Check KRA registration status. Returns a status code (007, 001, 000, 006, 999, etc.) mapped to kra_status_pan_stage.
UTI Fallback Rule UTI (PanAPI.GetPanDetails_UTI()) is a backup provider. It is called only when NSDL returns a non-success response or throws an exception. UTI is never called if NSDL succeeds. This is not a parallel call — it is a conditional fallback.

5. Backend Validations

CheckConditionResultBlocking?
OTP match Submitted OTP matches in-memory stored OTP for mobile number Proceed to Stage 3; fire background checks Yes — synchronous gate
OTP expiry OTP key does not exist in in-memory store (TTL expired) Return error FE_OTP_002; prompt resend Yes — must resend
OTP lock (5 wrong attempts) attempt_count >= 5 lead.state = DROPPED, reason = DROP_OTP_LOCKED Yes — journey terminated
KRA RESTRICTED CVL KRA returns status code 006 Stored as kra_status_pan_stage = RESTRICTED No — deferred to Stage 4
KRA 999 (INVALID_PAN) CVL KRA returns status code 999 Stored as kra_status_pan_stage = INVALID_PAN No — deferred to Stage 4
All background checks fail Zintlr, Hyperverge, NSDL, CVL KRA all return errors Log failures; customer proceeds to Stage 3 unaffected. PAN collected manually at Stage 4. No — non-blocking
Deferred Validations KRA RESTRICTED (006) and KRA INVALID_PAN (999) results are stored at Stage 2 but acted upon at Stage 4. The customer is not blocked or notified at this stage.

6. OTP Specification

ParameterValueNotes
Length 4 digits Numeric only (0000-9999)
Validity 5 minutes TTL enforced by in-memory store with background cleanup
Max wrong attempts 5 5th wrong attempt → lead.state = DROPPED, DROP_OTP_LOCKED
Max resend 3 per 30-minute window Tracked via in-memory counter with 30-min TTL
Cooldown 30 seconds Minimum interval between consecutive resend requests
Storage In-memory only OTP is never persisted to the database. In-memory store keyed by mobile number + OTP type
Delivery SMS cascade Primary SMS provider with fallback cascade if delivery fails

7. Background Check Decision Tables

7.1 PAN Pre-fetch Permutation Table

All combinations of Zintlr, Hyperverge, and NSDL results and their outcomes:

#ZintlrHypervergeNSDLOutcome
1 Found (PAN returned) Found (Name + DOB) Valid All stored. pan_number, pan_name, pan_dob, nsdl_pan_valid = true. Pre-fill at Stage 4.
2 Found (PAN returned) Found (Name + DOB) Invalid PAN stored. nsdl_pan_valid = false. Name/DOB from Hyperverge stored but PAN flagged for re-validation.
3 Found (PAN returned) Failed / No data Valid PAN stored and validated. pan_name and pan_dob not available — customer enters manually at Stage 4.
4 Found (PAN returned) Failed / No data Invalid PAN stored but nsdl_pan_valid = false. No Hyperverge data. Customer enters PAN details manually at Stage 4.
5 Found (PAN returned) Failed / No data Failed / Down PAN stored. NSDL failed → trigger UTI fallback. If UTI also fails, nsdl_pan_valid = null. Deferred validation at Stage 4.
6 Not found / Failed Skipped (no PAN) Skipped (no PAN) No PAN available. All downstream checks skipped. Customer enters PAN manually at Stage 4. Full validation runs at that point.
7 Found (PAN returned) Found (Name + DOB) Failed / Down PAN and Hyperverge data stored. NSDL failed → trigger UTI fallback. If UTI succeeds, nsdl_pan_valid set from UTI result.
Pipeline Rule If Zintlr does not return a PAN (row 6), all downstream checks (Hyperverge, C-safe, NSDL, CVL KRA) are skipped. The pipeline terminates gracefully and PAN is collected manually at Stage 4.

7.2 KRA Status Results

CVL KRA API response codes mapped to internal status values:

KRA Raw CodeInternal StatusDescriptionAction
007 series KRA_VALIDATED Customer is KRA registered and validated Store status. Pre-fill KRA data (email, name) at Stage 4.
001, 002, etc. KRA_MOD Customer has KRA record but with modifications pending Store status. Allow journey to continue. Handle at Stage 4.
000, 003, etc. NON_KRA Customer is not KRA registered Store status. Fresh KRA registration required during journey.
006 RESTRICTED Customer is restricted / blacklisted in KRA Store as DROP_KRA_RESTRICTED. Deferred — acted upon at Stage 4.
999 INVALID_PAN PAN not recognized by KRA system Store status. Deferred — re-validation at Stage 4.
API timeout / error API_DOWN CVL KRA API is unreachable or timed out Log failure. Store kra_status_pan_stage = API_DOWN. Non-blocking.

8. Existing Stored Procedure Mapping

8.1 Stored Procedures (Old System)

These stored procedures from the legacy system map to Stage 2 functionality. They serve as reference for what the new backend must replicate:

Stored ProcedurePurposeNew Backend Equivalent
RESUME_AUTHENTICATE_USER_OTP_PASS OTP validation — authenticates user after successful OTP entry OTP verification service + in-memory OTP store lookup
USP_VALIDATE_UPDATE_OTP_MOBILE_SJET Validate OTP and update mobile verification status in DB In-memory OTP check + leads.mobile_verified_at update
USP_INSERT_KRA_LOGS_SJET Insert KRA API request/response logs kra_records table insert + structured logging
USP_CALL_KRAAPI_ONSUBMIT_SJET KRA API call decision logic — determines whether to call KRA Background pipeline orchestrator (Step 3)
USP_INSERT_CSAFE_Req_Resp Insert C-safe API request/response logs csafe_checks table insert + structured logging
USP_GETCSAFEFLAG_SJET Retrieve C-safe configuration flags (enabled/disabled) provider_configurations table lookup
USP_INSERT_UPDATE_CSAFE_PEP_SJET Insert or update PEP (Politically Exposed Person) flag from C-safe csafe_checks.pep_flag update
USP_INSERT_ZINTLR_PANAPI_DETAILS_SJET Log Zintlr phone-to-PAN API request/response details pan_verifications table insert (source = zintlr)
USP_PANVALIDATION_REQUEST_RESPONSE_LOG_SJET Log PAN validation request/response (NSDL/UTI) pan_verifications table insert (source = nsdl/uti)
CHECK_STAFF_PAN Check if the PAN belongs to a staff member Staff PAN lookup in pan_verifications or dedicated staff table
USP_CHECK_NSDLRESP Check and parse NSDL response for PAN validation result NSDL response parser in PAN verification service

8.2 Old External API Calls

Legacy API methods that the new backend must integrate with (or replace):

Old API MethodVendorPurposePipeline Step
PanAPI.CallMobileToPanDetailsAsync() Zintlr Phone-to-PAN lookup — takes mobile number, returns PAN Step 1
PanAPI.GetPanDetailsHyperverge() Hyperverge PAN details lookup — takes PAN, returns Name + DOB Step 2a
PanAPI.VerifyPAN_NSDL() NSDL / Protean PAN validation against NSDL database Step 3a
PanAPI.GetPanDetails_UTI() UTI UTI fallback PAN validation — called only when NSDL is down Step 3a (fallback)
Migration Note The old system stored OTPs in the database (USP_VALIDATE_UPDATE_OTP_MOBILE_SJET). The new system stores OTPs exclusively in server memory (in-memory ConcurrentDictionary keyed by mobile number). No OTP values should be persisted to any database table.

9. Error / Drop Codes

CodeTypeTriggerUser Impact
FE_OTP_001 Frontend Error OTP entered is incorrect (attempts remaining) Show error with remaining attempt count. Customer can retry.
FE_OTP_002 Frontend Error OTP has expired (in-memory store TTL elapsed) Prompt customer to request a new OTP via resend.
DROP_OTP_LOCKED Drop 5th incorrect OTP attempt lead.state = DROPPED. Journey terminated. Customer must restart.
BE_OTP_002 Backend Error OTP resend limit exceeded (3 resends in 30 min) or cooldown not elapsed (30 sec) Return error. Customer must wait before requesting another resend.
DROP_KRA_RESTRICTED Deferred Drop CVL KRA returns status code 006 (RESTRICTED) Stored at Stage 2 but acted upon at Stage 4. Customer is not notified at this stage.
DROP_KRA_RESTRICTED is Deferred Even though the KRA RESTRICTED status is discovered during Stage 2 background checks, the drop action is deferred to Stage 4. This is because Stage 2 background checks are async and the customer has already moved to Stage 3.

10. Exit State

After Stage 2 completes (OTP verified + background checks dispatched), the following fields are populated:

FieldValue / SourceNotes
lead.state OTP_VERIFIED Set synchronously upon OTP match. This is the immediate exit state.
mobile_verified_at Timestamp (UTC) Recorded when OTP is verified successfully
kra_status_pan_stage KRA_VALIDATED | KRA_MOD | NON_KRA | RESTRICTED | INVALID_PAN | API_DOWN Populated async by CVL KRA background check
kra_raw_code 007, 001, 000, 006, 999, etc. Raw response code from CVL KRA API
csafe_result PEP/sanctions screening result Populated async by C-safe background check
pan_number 10-character PAN (e.g., ABCDE1234F) Populated async by Zintlr. May be null if Zintlr fails.
pan_name Full name as per PAN card Populated async by Hyperverge. May be null if Hyperverge fails.
pan_dob Date of birth as per PAN card Populated async by Hyperverge. May be null if Hyperverge fails.
nsdl_pan_valid true | false | null Populated async by NSDL (or UTI fallback). null if both fail.
kra_prefill_email Email from KRA record Available when kra_status_pan_stage = KRA_VALIDATED. Used for pre-fill at Stage 4.
kra_prefill_name Name from KRA record Available when kra_status_pan_stage = KRA_VALIDATED. Used for pre-fill at Stage 4.
Async Fields All fields except lead.state and mobile_verified_at are populated asynchronously. They may still be null when the customer reaches Stage 3 or early Stage 4. The frontend must handle partial data gracefully.

11. Vendor & Integration Calls

Vendor / SystemPurposeWhen CalledSync / Async
In-Memory OTP Store OTP storage, validation, attempt tracking, resend counters OTP submit, OTP resend Sync
Zintlr Phone-to-PAN lookup After OTP verified (pipeline Step 1) Async
Hyperverge PAN-to-Name/DOB lookup After Zintlr returns PAN (pipeline Step 2a) Async
NSDL / Protean PAN validation After Zintlr returns PAN (pipeline Step 3a) Async
UTI (backup only) PAN validation fallback Only when NSDL returns non-success or throws exception Async
CVL KRA KRA registration status check After Zintlr returns PAN (pipeline Step 3b) Async
C-safe PEP / sanctions screening After Zintlr returns PAN (pipeline Step 2b) Async
CleverTap Event tracking (OTP verified, background check results) After OTP verified; after each background check completes Async
Zoho CRM Lead status update After OTP verified; after background checks complete Async
Datalake Analytics event logging After OTP verified; after each background check completes Async
CDP Customer data platform update After background check results are stored Async

12. Database Tables (New Backend)

12.1 Operational Tables

TablePurposeKey Fields (Stage 2 Relevant)
leads Master lead record state (OTP_VERIFIED / DROPPED), mobile_verified_at, pan_number, pan_name, pan_dob, nsdl_pan_valid, kra_prefill_email, kra_prefill_name
otp_verifications OTP verification event log lead_id, attempt_count, verified_at, status (VERIFIED / LOCKED / EXPIRED), resend_count
pan_verifications PAN lookup/validation results from all providers lead_id, source (zintlr / hyperverge / nsdl / uti), request_payload, response_payload, pan_found, pan_valid, created_at
kra_records CVL KRA check results lead_id, kra_raw_code, kra_status (KRA_VALIDATED / KRA_MOD / NON_KRA / RESTRICTED / INVALID_PAN / API_DOWN), kra_email, kra_name, request_payload, response_payload
csafe_checks C-safe PEP/sanctions screening results lead_id, csafe_result, pep_flag, request_payload, response_payload, created_at
journey_stage_events Stage transition audit trail lead_id, stage (STAGE_2), event (OTP_VERIFIED / OTP_FAILED / OTP_LOCKED), metadata, created_at
downstream_events Async events dispatched to external systems lead_id, target (clevertap / zoho / datalake / cdp), event_type, status, dispatched_at
lead_state_transitions State machine audit log lead_id, from_state (INITIATED), to_state (OTP_VERIFIED / DROPPED), reason, triggered_by, created_at

12.2 Reference Tables

TablePurposeKey Fields
cvl_status_mapping Maps CVL KRA raw response codes to internal status values raw_code, internal_status (KRA_VALIDATED / KRA_MOD / NON_KRA / RESTRICTED / INVALID_PAN), description, is_active
nsdl_error_master NSDL error code reference for PAN validation responses error_code, error_description, triggers_uti_fallback, is_active
provider_configurations Feature flags and configuration for external providers provider_name (zintlr / hyperverge / nsdl / uti / cvl_kra / csafe), is_enabled, timeout_ms, retry_count, fallback_provider
In-Memory OTP Store Keys (Not in DB) OTP data lives exclusively in server memory (ConcurrentDictionary keyed by mobile number). Key patterns: mobile number + OTP type (OTP value, TTL 5 min), mobile number + attempts (wrong attempt counter), mobile number + resend (resend counter, TTL 30 min), mobile number + cooldown (cooldown flag, TTL 30 sec). Background cleanup removes expired entries.