EKYC 3.0 — Stage 3: Email Verification

Complete Backend Developer Implementation Guide • BRD Requirements + Existing Code Mapping + What to Build
Stage ID: EMAIL_VERIFIED Trigger: Customer provides email via KRA pre-fill, Google OAuth, or manual OTP BRD Section: 06 — Stage 3 Version: 1.0

Table of Contents

  1. Stage 3 Objective (BRD)
  2. Preconditions
  3. Email Capture Decision Flow
  4. Backend Validations
  5. Email OTP Specification
  6. Existing Code Mapping
  7. Error / Drop Codes
  8. Exit State
  9. Vendor & Integration Calls
  10. Database Tables (New Backend)

1. Stage 3 Objective (from BRD)

Customer provides their email address. Three paths are available: KRA pre-fill confirm, Google OAuth, or manual entry with OTP verification. The email is stored as email_hash (SHA-256). The plain email is never persisted.

Backend Only — No Frontend This document covers only backend APIs, stored procedures, database changes, and business logic. All frontend/UI concerns are excluded.
KRA RESTRICTED Not Surfaced Here If KRA returned a RESTRICTED status in Stage 2, that information is NOT surfaced or acted upon at this stage. The KRA RESTRICTED flag is handled later in the journey. Stage 3 only uses the kra_prefill_email value if available.

2. Preconditions

ParameterRequired ValueNotes
lead.state OTP_VERIFIED Stage 2 (PAN + KRA) must be complete. Lead must be in OTP_VERIFIED state.
KRA Background Checks Should be complete Wait a maximum of 3 seconds for CVL KRA result. If CVL result is not available within 3 seconds, render the stage with kra_prefill_email = null (manual entry path).
KRA Wait Logic The backend should poll or wait up to 3 seconds for the KRA/CVL background check to return the pre-fill email. If the result is not ready, proceed without it. The customer will use manual entry or Google OAuth instead. This prevents the journey from stalling on slow KRA responses.

3. Email Capture Decision Flow

ConditionPathBehaviour
kra_prefill_email is set (non-null) KRA Pre-fill Path Email is pre-filled from KRA. If customer confirms, no OTP is needed — email is accepted directly. If customer chooses to change, they fall through to the manual entry path with OTP.
kra_prefill_email is null Manual Entry Path Customer must enter email manually. Google Sign-In option is also presented. If neither is used, a 4-digit OTP is sent to the entered email for verification.
Google Sign-In used successfully Google OAuth Path Email is extracted from the Google OAuth token. No OTP needed. Email is accepted as verified via Google.
Google Sign-In closed or failed Silent Fallback No error shown to customer. Silent fallback to manual email entry path. No retry prompt for Google.
Manual email entry OTP Verification Path A 4-digit OTP is sent to the entered email address. Customer must enter the OTP to verify ownership. Standard OTP rules apply (see Section 5).

4. Backend Validations

ValidationRuleResult if FailsBlocking?
Email OTP match OTP entered must match OTP stored in in-memory OTP store for this lead + email combination Attempt count incremented. Customer can retry until max attempts reached. Yes
Email OTP expiry OTP has a 10-minute TTL enforced by in-memory store OTP expired. Customer must request a new OTP. FE_EMAIL_003 Yes
Email OTP max attempts Maximum 5 wrong OTP attempts per email address Email locked. Customer must enter a different email address to proceed. BE_EMAIL_001 Yes
Email duplicate check Email hash is checked against existing leads. If email hash is already linked to a post-eSign lead (completed or near-complete), it is blocked. Duplicate email blocked. BE_EMAIL_002 Yes
Email OTP delivery failure OTP email fails to deliver (Netcore returns failure) Journey continues. email_verified = false is stored. No block. No
Google OAuth failure Google Sign-In flow fails, times out, or is cancelled by customer Silent fallback to manual email entry. No error surfaced to customer. No
Restricted email domain check Email domain checked against restricted_email_domains table (disposable email services like mailinator, guerrillamail, tempmail, etc.) Email rejected. Customer must use a non-disposable email provider. Yes
Suspicious email flag Email checked against known suspicious patterns (e.g., random strings, known fraud patterns) Email is flagged for ops review but the journey is NOT blocked. Customer can proceed. No — flag only

5. Email OTP Specification

ParameterValueNotes
OTP Length 4 digits Numeric only (0000–9999)
Validity 10 minutes TTL enforced by in-memory store. OTP expires after 10 minutes from generation.
Max Wrong Attempts 5 After 5 wrong OTP entries, the email is locked. Customer must enter a different email address to continue.
Resend Cooldown 30 seconds Minimum 30-second gap between resend requests for the same email.
Max Resend Count 3 Maximum 3 OTP resends per email address. After 3 resends, customer must enter a different email.
Attempt Counter Reset On new email When the customer enters a different email address, all counters (wrong attempts + resend count) reset to zero.
Storage In-memory only OTP stored in server memory only (keyed by email address + OTP type in in-memory store). Never stored in the database.

6. Existing Code Mapping

Old Stored Procedures

Stored ProcedurePurpose
USP_SEND_RESUME_EMAILOTP_SJET Generate email OTP, validate email format, check whitelisted domains, perform duplicate checks across multiple tables. (Note: CYBRIDGE and Karza email validation removed per Product team decision.)
USP_VALIDATE_UPDATE_OTP_EMAIL_SJET Validate submitted OTP against stored value, update stage on success
USP_UPDATE_OTPEMAIL_STAGE_SJET Update OTP email stage tracking
USP_ALLOW_EMAIL_SJET Multi-layer email validation: whitelist check, format validation. (CYBRIDGE and Karza validation removed — not required per Product team.)
USP_CUSTOM_EMAIL_VALIDATION_SJET Custom email format validation rules
USP_RESTRICT_EMAIL_DOMAIN_SJET Domain restriction check against blocked/disposable domain list
USP_BYPASS_MOBILE_EMAIL_PAN_SJET EMAIL SECTION Bypass logic for INACTIVE / PMS / OWNER emails — allows certain emails to skip standard validation
USP_INSERT_UPDATE_MOBILE_EMAIL_OTP_BYPASS_SJET OTP bypass mechanism for RM-assisted resume from Zoho — when an RM resumes a dropped-off journey from Zoho CRM, OTP is bypassed. (Changed: was franchise bypass, now RM-resume-from-Zoho only.)
Usp_GetSuspiciousPhoneOrEMailId Check email against suspicious email patterns / known fraud indicators
USP_GET_EMAILVALIDATION_FLAG Retrieve email validation configuration flags (controls which validations are active)
USP_UPDATE_EMAIL_STAGE_SJET Update the lead stage after email verification is complete

Old External APIs

API CallPurpose
EmailAPI.REALTIME_EMAIL_VALIDATION() Real-time email deliverability check via CYBRIDGEREMOVED per Product team. Not required.
EmailAPI.KARZA_EMAIL_VALIDATION() Karza email validationREMOVED per Product team. Not required.
EmailAPI.CUSTOM_EMAIL_VALIDATION() Custom SP-based email validation — invokes USP_CUSTOM_EMAIL_VALIDATION_SJET
EmailAPI.CHECK_RESTRICTED_DOMAIN() Domain restriction SP call — invokes USP_RESTRICT_EMAIL_DOMAIN_SJET
EmailAPI.sendMail_Netcore() Email OTP delivery via Netcore email service

Old Reference Tables Used

TablePurpose
TBL_ALLOW_EMAIL Whitelisted email domains — emails from these domains skip certain validation checks
TBL_RESTRICT_EMAIL_DOMAIN Restricted / blocked email domains — disposable email services and known bad domains
TBL_REALTIME_EMAIL_RESPONSE Cached realtime email validation results — avoids repeated API calls for the same email
TBL_KARZA_EMAIL_VALIDATE_RESPONSE Cached Karza email validation resultsNOT NEEDED. Karza removed.
TBL_SEND_RESUME_OTP OTP tracking table — stores OTP generation, attempt counts, and verification status

7. Error / Drop Codes

CodeTypeSituationCustomer ImpactOps Impact
FE_EMAIL_001 Frontend Email format validation failed (invalid format) Inline validation error — customer must correct the email format None
FE_EMAIL_002 Frontend Wrong OTP entered Error shown, attempt count incremented, customer can retry None
FE_EMAIL_003 Frontend OTP expired (past 10-minute TTL) Customer must request a new OTP None
BE_EMAIL_001 Backend Email locked — 5 wrong OTP attempts reached Customer must enter a different email address to proceed Logged for fraud analysis
BE_EMAIL_002 Backend Duplicate email — email hash already linked to a post-eSign lead Email rejected. Customer must use a different email address. Logged for audit

8. Exit State — What Is Set When Stage 3 Completes

FieldValueStorage
lead.state EMAIL_VERIFIED leads table
email_hash SHA-256 hash of the verified email address. Plain email is never stored. leads table / email_verifications table
email_source KRA_PREFILL | GOOGLE_OAUTH | MANUAL_OTP email_verifications table
email_verified true (verified via KRA confirm, Google OAuth, or OTP match) or false (OTP delivery failed, verification skipped) email_verifications table
email_verified_at Timestamp of successful email verification email_verifications table
Email Source Tracking The email_source field is critical for downstream analytics and compliance. It records how the email was obtained:
KRA_PREFILL — Customer confirmed the KRA-provided email without modification
GOOGLE_OAUTH — Email extracted from Google OAuth token (verified by Google)
MANUAL_OTP — Customer entered email manually and verified via 4-digit OTP

9. Vendor & Integration Calls

SystemTypeWhat It Does at Stage 3Call TypeIf Unavailable
Google OAuth 3rd Party Provides verified email from Google account. Extracts email from OAuth ID token. No OTP needed when used. Sync Silent fallback to manual email entry. No error surfaced.
Netcore Email 3rd Party Delivers email OTP to the customer's email address for manual entry path. Async Journey continues with email_verified = false. No block.
In-Memory OTP Store Internal Stores email OTP (TTL 10 min), attempt counters, resend counters, and cooldown tracking in server memory. Sync OTP flow unavailable. Fallback to Google OAuth or KRA pre-fill only.
CleverTap Analytics Events: email_verified_en, email_otp_sent_en, email_otp_failed_en, email_source_en Async (non-blocking) Non-blocking. Events queued for retry.
Zoho CRM CRM Update lead record with email_hash, email_source, email_verified status. Async (non-blocking) Queued for retry. Lead exists in EKYC DB.
CDP Internal Email verification event: lead_id, email_hash, email_source, verification method. Async (non-blocking) Non-blocking.
Datalake Internal Full email verification payload: lead_id, email_hash, email_source, OTP attempts, verification timestamp. Async (non-blocking) Non-blocking.

10. Database Tables (New Backend)

Operational Tables

leads MODIFY

Existing leads table — updated with email verification fields at Stage 3 completion.

ColumnTypeNotes
email_hashVARCHAR(64)SHA-256 hash of verified email. Set on Stage 3 completion.
stateVARCHAR(30)Updated to EMAIL_VERIFIED on Stage 3 completion.

email_verifications NEW

Tracks email verification details for each lead. One record per lead, updated on each email attempt.

ColumnTypeNotes
idBIGINT IDENTITYPK
lead_idUUIDFK to leads table
email_hashVARCHAR(64)SHA-256 hash of the email address
email_sourceVARCHAR(20)KRA_PREFILL | GOOGLE_OAUTH | MANUAL_OTP
email_verifiedBITtrue if verified, false if delivery failed or skipped
email_verified_atDATETIME2Timestamp of successful verification
otp_attemptsINTNumber of wrong OTP attempts for current email
resend_countINTNumber of OTP resends for current email
google_oauth_subVARCHAR(255)Google OAuth subject ID (if Google path used). Nullable.
kra_prefill_usedBITWhether the KRA pre-filled email was confirmed
restricted_domain_checkedBITWhether domain restriction check was performed
suspicious_flagBITWhether the email was flagged as suspicious
created_atDATETIME2Record creation timestamp
updated_atDATETIME2Last update timestamp

otp_verifications (type=EMAIL) EXISTS

Shared OTP verifications table — used for both mobile and email OTP tracking. Filtered by type = 'EMAIL' for this stage.

ColumnTypeNotes
idBIGINT IDENTITYPK
lead_idUUIDFK to leads table
typeVARCHAR(10)EMAIL for this stage (also supports MOBILE)
target_hashVARCHAR(64)SHA-256 hash of email address
otp_sent_atDATETIME2When the OTP was sent
otp_verified_atDATETIME2When the OTP was successfully verified (nullable)
attempt_countINTNumber of wrong OTP attempts
resend_countINTNumber of OTP resends
delivery_statusVARCHAR(20)SENT | DELIVERED | FAILED
delivery_channelVARCHAR(20)NETCORE_EMAIL
created_atDATETIME2Record creation timestamp

downstream_events EXISTS

Async event queue for pushing email verification data to external systems (CleverTap, Zoho, CDP, Datalake).

ColumnTypeNotes
idBIGINT IDENTITYPK
lead_idUUIDFK to leads table
event_typeVARCHAR(50)e.g., EMAIL_VERIFIED, EMAIL_OTP_SENT
target_systemVARCHAR(30)CLEVERTAP | ZOHO | CDP | DATALAKE
payloadNVARCHAR(MAX)JSON payload for the target system
statusVARCHAR(20)PENDING | SENT | FAILED
retry_countINTNumber of delivery attempts
created_atDATETIME2Event creation timestamp

Reference Tables

restricted_email_domains NEW

List of blocked email domains (disposable email services). Replaces the old TBL_RESTRICT_EMAIL_DOMAIN table.

ColumnTypeNotes
idBIGINT IDENTITYPK
domainVARCHAR(255)Blocked domain (e.g., mailinator.com, guerrillamail.com, tempmail.com)
reasonVARCHAR(100)Reason for restriction (e.g., disposable, spam, known fraud)
is_activeBITActive flag — allows soft-disabling without deletion
added_byVARCHAR(50)Who added this domain
created_atDATETIME2Date added

suspicious_contacts NEW

Tracks emails (and phone numbers) flagged as suspicious. Replaces the old Usp_GetSuspiciousPhoneOrEMailId SP logic with a table-driven approach.

ColumnTypeNotes
idBIGINT IDENTITYPK
contact_typeVARCHAR(10)EMAIL | MOBILE
contact_hashVARCHAR(64)SHA-256 hash of the suspicious contact
pattern_matchVARCHAR(255)Description of the pattern that triggered the flag (nullable)
flagged_byVARCHAR(30)SYSTEM | OPS_MANUAL | FRAUD_ENGINE
is_activeBITActive flag
created_atDATETIME2Date flagged
Migration Summary The new backend replaces the fragmented old structure (11 stored procedures + 5 reference tables + 5 external API calls) with a cleaner architecture:
Operational tables: leads, email_verifications, otp_verifications, downstream_events
Reference tables: restricted_email_domains, suspicious_contacts
OTP storage: In-memory store only (10-min TTL), never in DB
Three verification paths: KRA pre-fill (no OTP), Google OAuth (no OTP), Manual entry (4-digit email OTP)