EKYC 3.0 — Stage 1: Registration

Complete Backend Developer Implementation Guide • BRD Requirements + Existing Code Mapping + What to Build
Stage ID: INITIATED Trigger: Customer submits mobile number and consents BRD Section: 04 — Stage 1 Version: 1.0

Table of Contents

  1. Stage 1 Objective (BRD)
  2. Preconditions
  3. Backend Flow — What to Build
  4. API Design
  5. Eligibility Decision Table — All Combinations
  6. Backend Validations
  7. Consent Specification
  8. OTP Specification
  9. Existing Stored Procedure Mapping
  10. New Stored Procedures / Logic Needed
  11. Database Schema — New & Modified Tables
  12. External Integrations
  13. Error / Drop Codes
  14. Exit State
  15. System Interactions (Post-Lead Creation)
  16. Implementation Task Checklist

1. Stage 1 Objective (from BRD)

Customer enters their mobile number and accepts all three mandatory consents. The platform checks eligibility (negative list, existing account, dedupe) and either:

Backend Only — No Frontend This document covers only backend APIs, stored procedures, database changes, and business logic. All frontend/UI concerns are excluded.

2. Preconditions

ParameterRequired ValueSource
session_idActive session from Stage 0. Stored in Redis (TTL 15 min).Stage 0 output
journey_variant_idSet by LaunchDarkly at Stage 0Stage 0 output
location_tagSOUTH or OTHERS — must be set before Stage 1 loadsStage 0 output
OTP stateNo OTP in flight for this mobile number (in-memory store key must not exist)In-memory store check

3. Backend Flow — What to Build

Complete Backend Execution Sequence

1
Receive Request NEW API
Input: mobile_number (10 digits), registration_name (full name), consent_1 (bool), consent_2 (bool), consent_3 (bool), session_id
Validate session_id exists in cache/memory and is not expired.
2
Backend Validation
• Mobile: 10 digits, starts with 6/7/8/9
• Name: 2-100 chars, alpha + spaces only
• All 3 consents must be true
• No OTP currently in flight for this mobile (check in-memory store key)
3
Run Eligibility Checks in Parallel NEW
Fire these three checks simultaneously:
(a) Negative List API — mobile + IP against MOFSL blacklist + SEBI debarred list
(b) CBOS Dedupe API — check for existing active Demat account for this mobile
(c) EKYC DB Dedupe — check for in-progress application in TBL_DEDUPE_DATA_DUMP
4
Apply Eligibility Decision Table (see Section 5)
Priority-ordered rules determine outcome: block, redirect, resume, or create new lead.
5
Create Lead Record MODIFY
If eligible for new lead:
• Generate lead_id (UUID)
• Store mobile_hash (SHA-256 of mobile, not plain mobile)
• Store registration_name
• Store channel, source, UTM params, device info, journey variant from session
• Set lead.state = INITIATED
Existing SP: [DIY].[USP_CLIENT_REGISTRATION_SJET] — needs modification to store hash instead of plain mobile, simplified input params.
6
Save Consent Records NEW
Each consent saved with: consent_id, version, timestamp, ip_address, platform, lead_id
Consent save MUST succeed before OTP is sent — compliance requirement.
Retry once on failure. If still fails → error BE_REG_004, OTP not sent.
7
Generate & Send OTP MODIFY
• Generate 4-digit OTP
• Store in in-memory OTP store (ConcurrentDictionary keyed by mobile number) (key: keyed by mobile number + OTP type, TTL: 5 minutes). Never in DB.
• Send via cascade: SMS (primary) → WhatsApp → Push → RCS
• Record which channel succeeded: otp_channel_used
• If all 4 channels fail: CS Journey CS_OTP_PROVIDER_DOWN
Existing: SEND_RESUME_OTP SP + SingleMessageService SOAP for SMS only. Needs in-memory OTP store migration + cascade channels.
8
Push to External Systems (Async, Non-Blocking)
• CleverTap: registration_proceed_en, lead_created_en
• Zoho CRM: New lead record with mobile_hash, source, location_tag
• RIISE App: Lead created notification
• Datalake: Full lead creation payload
• CDP: Lead creation event
9
Return Response
Success: { status: true, lead_id, otp_sent: true }
Failure: { status: false, error_code, message }

4. API Design

POST /api/v3/registration/initiate

Creates a new lead, validates eligibility, saves consents, sends OTP.

Request Body

FieldTypeRequiredValidation
mobile_numberstringYes10 digits, starts with 6/7/8/9
registration_namestringYes2-100 chars, alpha + spaces only
consent_account_openingbooleanYesMust be true
consent_communicationbooleanYesMust be true
consent_termsbooleanYesMust be true
session_idstring (UUID)YesMust exist in cache/memory, not expired

Success Response (HTTP 200)

{
  "status": true,
  "lead_id": "uuid-here",
  "lead_state": "INITIATED",
  "otp_sent": true,
  "otp_channel_used": "SMS",
  "message": null
}

Error Responses

Error CodeHTTPConditionMessage
DROP_NEGATIVE_LIST200Mobile on MOFSL/SEBI listThis number is not eligible. Please use a different mobile number.
BE_REG_001200Active Demat exists (CBOS)An active account already exists. Please log in to RIISE.
BE_REG_002200In-progress lead, different channel/RMThis mobile number already has an application in progress.
BE_REG_003200Lead creation failed after 3 retriesSomething went wrong. Please try again.
BE_REG_004200Consent save failed after retrySomething went wrong saving your consent. Please try again.
CS_OTP_PROVIDER_DOWN200All OTP channels failedWe are having trouble sending your OTP. We will notify you once it is ready.

5. Eligibility Decision Table — All Combinations

Priority Order Checks run in parallel. If multiple conditions are true simultaneously, the priority order below determines which outcome fires (top = highest priority).
PriorityConditionOutcomeError CodeExisting SP / Logic
1 Mobile on MOFSL negative list OR SEBI debarred list Journey ends permanently DROP_NEGATIVE_LIST NEW — Current TBL_FFF_NOT_ALLOW is partial. Need full MOFSL + SEBI API.
2 Active Demat account exists in CBOS for this mobile Journey ends BE_REG_001 EXISTS USP_CHECK_MOBILE_EXISTS_BO_EXP_SJET checks MOSL_FEED + DPMASTER
3 In-progress app on OLD platform (< 90 days) Redirect to old platform — (redirect) EXISTS USP_CHECK_NINTY_DAY_LOGIC_LSQ_SJET + Tbl_DAD_TO_RETAIL_TRANSFERRED_LEADS
4 In-progress app on NEW platform (< 90 days), same channel / same BA / same RM OTP sent. On verify, resume existing lead. — (resume) MODIFY Dedupe exists via Proc_DedupeCheck_LeadSquare_LSQ_SJET. Need to add channel/BA/RM comparison.
5 In-progress app on NEW platform (< 90 days), DIFFERENT channel or BA/RM Journey blocked until day 91 BE_REG_002 NEW — Channel/BA/RM comparison not in current dedupe logic
6 In-progress app in REJECTED or PERMANENTLY_CLOSED state Treated as no active app. New lead created. MODIFY Extend dedupe to check lead state
7 In-progress app in CS_EXPIRED state Old lead archived. New lead created. NEW — CS_EXPIRED state handling
8 No application exists — all checks pass New lead created. OTP sent. EXISTS USP_CLIENT_REGISTRATION_SJET

6. Backend Validations

CheckAPI CalledConditionResult if FailsBlocking?
Negative List Internal Negative List API NEW Mobile found in MOFSL blacklist or SEBI debarred DROP_NEGATIVE_LIST — journey ends Yes
CBOS active account CBOS Dedupe API Active Demat found for this mobile BE_REG_001 — journey ends Yes
EKYC DB dedupe Internal Lead API In-progress lead found Varies per eligibility table Yes (priorities 1-5)
Negative List API unavailable Service down Proceed. Tag NEGATIVE_LIST_CHECK_SKIPPED. Manual check post-eSign. No — non-blocking with flag
CBOS unavailable Service down Proceed. Tag CBOS_DEDUPE_SKIPPED. Manual check post-eSign. No — non-blocking with flag
OTP resend rate limit In-memory counter Same mobile > 3 OTP resends in 30 min Block resend. BE_OTP_002 Yes — resend blocked
Lead creation retry Internal Lead API Lead creation fails on first attempt Retry 3x at 2-sec intervals. If all fail → BE_REG_003 Yes
Consent save Internal Lead API Consent record fails to persist Retry once. If still fails → BE_REG_004. OTP not sent. Yes
ConsentWhat Customer Agrees ToStored Fields
Account Opening Consent Permission to open a Demat account on their behalf with Motilal Oswal consent_id, version, timestamp, ip_address, platform, lead_id
Communication Consent Permission to contact via WhatsApp, SMS, email, push. Also acts as WhatsApp opt-in (TRAI). consent_id, version, timestamp, whatsapp_optin = true
Terms & Conditions Acceptance of platform terms of use and privacy policy consent_id, version, timestamp
Compliance Requirement Storing the exact version of the consent text is mandatory. If consent text is updated in future, historical records must show what the customer agreed to at the time of consent. This is a new requirement — current code has no consent versioning.

8. OTP Specification

ParameterValueCurrent CodeChange Needed
Length4 digitsExistsNo change
Validity5 minutes from generationExists (DB-stored)TTL enforced by in-memory store
Max wrong attempts5. After 5th → OTP locked, lead.state = DROPPED, DROP_OTP_LOCKEDAttempt tracking existsAlign state codes
Max resend requests3 within 30-min window. 4th blocked for 30 min.Rate limiting existsVerify thresholds
Min wait between resends30 secondsMay existEnforce via in-memory store
StorageIn-memory OTP store (ConcurrentDictionary keyed by mobile number). Never written to application DB. TTL = 5 min.Stored in DBNEW Store OTP in server in-memory store (not DB, not Redis)
Delivery cascadeSMS → WhatsApp → Push → RCS. Each attempted only if previous fails.SMS only (Netcore SOAP)NEW Add WhatsApp, Push, RCS channels
All channels failCS Journey: CS_OTP_PROVIDER_DOWN. Customer shown paused state.Error onlyNEW Implement CS Journey pause

9. Existing Stored Procedure Mapping

How existing stored procedures map to BRD Stage 1 requirements:

BRD RequirementExisting SPWhat It Does TodayReusable?Changes Needed
Create lead record [DIY].[USP_CLIENT_REGISTRATION_SJET] 5,780-line master SP. Creates user in TBL_DEDUPE_DATA_DUMP, SSO user, branch/RM assignment, campaign routing. Partially Simplify inputs. Store mobile_hash not plain. Add consent versioning. Remove email (moved to Stage 3).
CBOS / Back-office dedupe [DIY].[USP_CHECK_MOBILE_EXISTS_BO_EXP_SJET] Checks MOSL_FEED, DPMASTER, PAN_MOBILE_EMAIL_WHITELIST for existing active account by mobile. Yes Wrap as CBOS API adapter. Add CBOS_DEDUPE_SKIPPED flag on unavailability.
CRM dedupe [DIY].[Proc_DedupeCheck_LeadSquare_LSQ_SJET] Checks TBL_DEDUPE_DATA_DUMP, LSQ leads, DAD transfer table, FFF_NOT_ALLOW. Partially Add channel/BA/RM comparison for Priority 4 vs 5 distinction.
90-day logic [DIY].[USP_CHECK_NINTY_DAY_LOGIC_LSQ_SJET] Checks Tbl_DAD_TO_RETAIL_TRANSFERRED_LEADS for non-dormant leads by mobile. Yes Add old-platform vs new-platform distinction.
Bypass logic [DIY].[USP_BYPASS_MOBILE_EMAIL_PAN_SJET] Checks INACTIVE, PMS, OWNER bypass conditions against MOSL_FEED tables. Yes No change for Stage 1. Used as-is.
Lead discard/re-register [DIY].[proc_execute_dad_dedupe_discard_LSQ_SJET] Removes duplicate lead for re-registration. Handles DAD-to-Retail transfer. Yes Add REJECTED / CS_EXPIRED state handling per BRD Priority 6-7.
Referral code validation [DIY].[USP_VALIDATE_REFERALCODE_SJET] Validates referral code against MOSL_FEED_CLIENT_DETAILS.CL_code. Yes BRD: Referral validated at Stage 0. Can still reuse for backend validation.
OTP generation & send SEND_RESUME_OTP (SP) + SingleMessageService (SOAP) Generates OTP, stores in DB, sends SMS via Netcore SOAP. Partially Store OTP in server in-memory store (not DB, not Redis). Add WhatsApp/Push/RCS cascade.
Negative list check TBL_FFF_NOT_ALLOW (table check in dedupe SP) Basic fraud/blocked list Partially NEW Need dedicated Negative List API + SEBI debarred integration. IP-based check.

10. New Stored Procedures / Logic Needed

New SP / LogicPurposeKey Logic
USP_CHECK_NEGATIVE_LIST NEW Check mobile + IP against MOFSL negative list + SEBI debarred list Query negative list table by mobile. Query SEBI API by mobile/PAN. Return PASSED / BLOCKED / SKIPPED.
USP_SAVE_CONSENT_RECORDS NEW Save 3 consent records with versioning INSERT into new TBL_LEAD_CONSENTS table. Each row: consent_id, consent_type, version, text_hash, timestamp, ip_address, platform, lead_id.
USP_ELIGIBILITY_CHECK_V3 NEW Unified eligibility check implementing BRD priority table Runs: (1) Negative list, (2) CBOS, (3) EKYC dedupe with channel/BA/RM comparison, (4) 90-day logic, (5) Lead state check (REJECTED/CS_EXPIRED). Returns priority-ordered result.
USP_CREATE_LEAD_V3 NEW Simplified lead creation for EKYC 3.0 Stores mobile_hash (not plain), registration_name, channel, source, UTM params, device_type, location_tag. Sets lead.state = INITIATED. Returns lead_id (UUID).
Channel/BA/RM comparison logic NEW In dedupe check, compare channel + BA code + RM code of existing lead vs new request If same: allow resume (Priority 4). If different: block (Priority 5). Do NOT reveal which channel owns the lead.

11. Database Schema — New & Modified Tables

11.1 New Table: TBL_LEAD_CONSENTS

ColumnTypeNullableDescription
consent_idUNIQUEIDENTIFIERNo (PK)Unique consent record ID
lead_idUNIQUEIDENTIFIERNo (FK)Reference to lead
consent_typeVARCHAR(50)NoACCOUNT_OPENING / COMMUNICATION / TERMS
versionVARCHAR(20)NoVersion of consent text shown (e.g. "v2.1")
text_hashVARCHAR(64)NoSHA-256 hash of exact consent text shown
ip_addressVARCHAR(45)NoCustomer IP at time of consent
platformVARCHAR(20)NoWEB_MOBILE / WEB_DESKTOP / ANDROID_APP / IOS_APP
whatsapp_optinBITYesTrue for Communication consent (TRAI compliance)
created_atDATETIME2NoTimestamp of consent

11.2 Modified Table: TBL_DEDUPE_DATA_DUMP

ChangeColumnTypeDescription
ADDmobile_hashVARCHAR(64)SHA-256 hash of mobile. Replaces plain mobile storage.
ADDchannelVARCHAR(20)DAD / FRANCHISE / BRANCH
ADDba_codeVARCHAR(50)BA code for channel comparison
ADDrm_codeVARCHAR(50)RM code for channel comparison
ADDlead_stateVARCHAR(30)INITIATED / OTP_VERIFIED / DROPPED / REJECTED / CS_EXPIRED
ADDdrop_codeVARCHAR(50)DROP_NEGATIVE_LIST / DROP_OTP_LOCKED / DROP_RESET_JOURNEY / etc.
ADDnegative_list_check_statusVARCHAR(10)PASSED / SKIPPED
ADDcbos_dedupe_statusVARCHAR(10)PASSED / SKIPPED

11.3 New Table: TBL_NEGATIVE_LIST (if not using external API)

ColumnTypeDescription
idBIGINT IDENTITYPK
mobile_hashVARCHAR(64)SHA-256 of blocked mobile
pan_numberVARCHAR(10)Blocked PAN (nullable)
list_sourceVARCHAR(20)MOFSL / SEBI
reasonVARCHAR(200)Reason for listing
added_dateDATETIME2Date added to list
is_activeBITActive flag

12. External Integrations

SystemTypeWhat It Does at Stage 1Call TypeIf Unavailable
Internal Negative List API NEW Internal Checks mobile + IP vs MOFSL blacklist and SEBI debarred list Sync parallel Allow proceed. Tag NEGATIVE_LIST_CHECK_SKIPPED. Ops review post-eSign.
CBOS Back-office Checks if active Demat account exists for this mobile Sync parallel Allow proceed. Tag CBOS_DEDUPE_SKIPPED. Ops review post-eSign.
In-memory store / Cache Internal OTP: in-memory ConcurrentDictionary keyed by mobile number (TTL 5 min). Session management: cache (TTL 15 min). Sync CS Journey CS_OTP_PROVIDER_DOWN.
Netcore SMS 3rd Party OTP delivery (primary channel). TRAI DND exempt (transactional). Async Fallback to WhatsApp → Push → RCS.
WhatsApp Business API NEW 3rd Party OTP delivery (2nd channel in cascade) Async Fallback to Push.
Push Notification NEW 3rd Party OTP delivery (3rd channel) Async Fallback to RCS.
RCS NEW 3rd Party OTP delivery (4th channel, last resort) Async CS Journey if all 4 fail.
Zoho CRM CRM New lead record: mobile_hash, source, location_tag, journey_variant_id Async (non-blocking) Queued for retry. Lead exists in EKYC DB.
CleverTap Analytics Events: registration_proceed_en, lead_created_en, eligibility_failed_en Async Non-blocking.
Datalake Internal Full lead creation payload: all fields, UTM, device, consent versions Async Non-blocking.

13. Error / Drop Codes

CodeSituationCustomer ImpactOps Impact
DROP_NEGATIVE_LISTMobile on negative list / SEBI debarredJourney ends permanentlyLogged for audit
BE_REG_001Active Demat exists in CBOSJourney ends, told to login to RIISENone
BE_REG_002Different channel/RM owns in-progress leadJourney blocked, no cross-channel info disclosedNone
BE_REG_003Lead creation failed after 3 retriesGeneric error, can retryAlert to engineering
BE_REG_004Consent save failedGeneric error, OTP not sentAlert to engineering
CS_OTP_PROVIDER_DOWNAll 4 OTP channels failedPaused state, will be notifiedCS Journey triggered
BE_OTP_002OTP resend limit reachedWait 30 minutesNone
DROP_OTP_LOCKED5 wrong OTP attemptsMust restart applicationLogged for fraud analysis
DROP_SESSION_TIMEOUT15 min inactivitySession ended, resume availableDrop analysis
DROP_RESET_JOURNEYCustomer chose to resetMobile freed, can re-registerBehavioural tracking

14. Exit State — What Is Set When Stage 1 Completes

FieldValueStorage
lead.stateINITIATEDTBL_DEDUPE_DATA_DUMP (or new leads table)
lead_idGenerated UUID — primary key for all subsequent stagesDB
registration_nameFull name as entered. Display-only. Does NOT set ekyc_name.DB
mobile_hashSHA-256 hash of mobile. Plain mobile never stored.DB
channelDAD / FRANCHISE / BRANCH (from session)DB
sourceutm_source from Stage 0 sessionDB
journey_variant_idInherited from Stage 0DB
south_tagSOUTH or OTHERS (from Stage 0)DB
device_typeWEB_MOBILE / WEB_DESKTOP / ANDROID_APP / IOS_APPDB
consent_1_versionVersion string of Account Opening Consent textTBL_LEAD_CONSENTS
consent_2_versionVersion string of Communication Consent textTBL_LEAD_CONSENTS
consent_3_versionVersion string of T&C textTBL_LEAD_CONSENTS
otp_sent_atTimestamp of first OTP sendDB
otp_channel_usedSMS / WHATSAPP / PUSH / RCSDB
negative_list_check_statusPASSED / SKIPPEDDB
cbos_dedupe_statusPASSED / SKIPPEDDB

15. System Interactions (Post-Lead Creation)

SystemWhat Is SentWhen
CleverTapregistration_proceed_en, lead_created_en, eligibility_failed_enOn lead creation or eligibility failure
Zoho CRMNew lead: mobile_hash, source channel, south/others tag, journey_variant_id, timestampOn lead creation
RIISE AppLead created notification: lead_id, channel, sourceOn lead creation
GCMLead created: lead_id, mobile_hash, channelOn lead creation
DatalakeFull payload: all fields, UTM params, device, journey variant, consent versionsOn lead creation
CDPLead creation event: mobile_hash, channel, UTM params, journey_variant_idOn lead creation

16. Implementation Task Checklist

#TaskTypePriorityDepends On
1Create TBL_LEAD_CONSENTS tableNEWP0
2Add columns to TBL_DEDUPE_DATA_DUMP: mobile_hash, channel, ba_code, rm_code, lead_state, drop_code, negative_list_check_status, cbos_dedupe_statusMODIFYP0
3Create TBL_NEGATIVE_LIST table (or integrate MOFSL/SEBI external API)NEWP0
4Build USP_CHECK_NEGATIVE_LIST SPNEWP0Task 3
5Build USP_SAVE_CONSENT_RECORDS SPNEWP0Task 1
6Build USP_ELIGIBILITY_CHECK_V3 SP with full priority tableNEWP0Tasks 2, 4
7Build USP_CREATE_LEAD_V3 SP (simplified lead creation with mobile_hash)NEWP0Task 2
8Implement in-memory OTP store via ConcurrentDictionary keyed by mobile number (replace DB storage, not Redis)MODIFYP0
9Build OTP cascade service: SMS → WhatsApp → Push → RCSNEWP1
10Build /api/v3/registration/initiate API endpointNEWP0Tasks 4-8
11Implement lead creation retry (3x at 2-sec intervals)NEWP1Task 10
12Add channel/BA/RM comparison to dedupe logic (Priority 4 vs 5)NEWP0Task 6
13Add REJECTED / CS_EXPIRED state handling in dedupe (Priority 6-7)MODIFYP1Task 6
14Implement Redis session management (15-min TTL)NEWP0
15Implement async push to CleverTap, Zoho, RIISE, Datalake, CDPMODIFYP1Task 10
16Implement CS Journey pause flow for CS_OTP_PROVIDER_DOWNNEWP1Task 9
17Implement graceful degradation flags (NEGATIVE_LIST_CHECK_SKIPPED, CBOS_DEDUPE_SKIPPED)NEWP0Tasks 4, 6
18Add OTP resend rate limiting in in-memory store (3 resends / 30 min, 30-sec cooldown)MODIFYP1Task 8
19Implement SHA-256 mobile hashing utilityNEWP0
20Unit + integration tests for eligibility decision table (all 8 priorities)NEWP0Tasks 6, 10
Reuse Summary Out of the 8 BRD eligibility rules, 3 are fully covered by existing SPs (CBOS check, 90-day logic, bypass), 2 need modifications (CRM dedupe + lead states), and 3 are new (negative list, channel comparison, CS_EXPIRED handling).

The existing reference data tables (MOSL_FEED_*, TBL_DPMASTER, TBL_PAN_MOBILE_EMAIL_WHITELIST, TBL_STATE_CITY_MASTER_LSQ) are all reusable as-is.