1. Stage 13 Objective
The customer signs the Account Opening Form (AOF) via Aadhaar OTP-based electronic signature. This constitutes a legally binding signature under the Information Technology Act, 2000 (Section 3A — electronic signature). The eSign is performed through a vendor portal (NSDL or Emudhra), with routing determined by a daily budget counter system.
Post-eSign Data Verification is a HARD GATE
After the customer signs, the backend extracts 5 fields from the signed document and verifies them against previously collected data. This verification determines whether the lead proceeds (STP), goes for ops review (NON_STP), or is dropped. There is no manual override for verification failures.
Vendor Budget Limits
Hyperverge is the primary eSign vendor. Emudhra is the first fallback. NSDL is the last-resort fallback. Vendor selection cascades: HV → Emudhra → NSDL. Daily budget counters are tracked in-memory and reset at midnight IST. (Updated per Product team: order is HV first, then Emudhra, then NSDL as last resort. No Claude API integration.)
2. Preconditions
| Condition | Required Value | Notes |
lead.state |
AOF_GENERATED |
New code uses KRA_RECHECKED as the entry state. Both must be handled during migration. |
| AOF Document |
Must exist in aof_documents |
The unsigned AOF PDF must be present and accessible at the configured file storage path. |
| eSign Vendor |
Determined by daily budget counter |
In-memory counter checked at eSign initiation. If both vendors are at capacity, the request is queued. |
State Migration Note
The legacy system uses AOF_GENERATED as the entry state for Stage 13. The new backend uses KRA_RECHECKED. During migration, both states must be accepted as valid preconditions for eSign initiation.
3. eSign Vendor Routing
Vendor routing is determined by an in-memory daily budget counter. The BRD specifies Redis for this purpose, but the current implementation uses in-memory counters (ConcurrentDictionary). Counters reset at midnight IST via a scheduled background task.
3.1 Routing Decision Table
| Condition | NSDL Usage | Emudhra Usage | Selected Vendor | Action |
| NSDL budget < 80% |
< 2,400 |
Any |
NSDL |
Route to NSDL eSign portal |
| NSDL budget ≥ 80% |
≥ 2,400 |
< 1,600 |
Emudhra |
Route to Emudhra eSign portal |
| NSDL circuit breaker tripped |
N/A (down) |
< 1,600 |
Emudhra (100%) |
All traffic routed to Emudhra until NSDL recovers |
| Emudhra also down / at capacity |
N/A (down) |
N/A (down/full) |
Queue |
15-minute retry queue + CS Journey initiated for the customer |
| Both vendors at daily capacity |
≥ 3,000 |
≥ 1,600 |
None |
P0 Alert raised. No eSign possible until next day or capacity increase. |
Counter Implementation
Daily counters are maintained in-memory (not Redis as the BRD suggests). They are keyed by vendor name + date (e.g., NSDL_2026-04-01). A background task resets all counters at 00:00 IST. In a multi-instance deployment, each instance tracks its own counter — this is an accepted trade-off for simplicity.
3.2 Circuit Breaker Logic
- Trigger: 5 consecutive failures to the same vendor within a 2-minute window
- State: Circuit opens, all traffic routes to the other vendor
- Recovery: Half-open after 5 minutes — one test request sent. If successful, circuit closes.
- Both open: Requests enter a 15-minute retry queue and a CS Journey is initiated for the customer
4. Backend Flow
1
Determine eSign Vendor — Check in-memory daily budget counters. Apply routing decision table (Section 3.1). If both vendors unavailable, queue the request and initiate CS Journey. Record selected vendor in esign_transactions.
2
Generate eSign Redirect URL — Call the selected vendor's API (NSDL or Emudhra) with the unsigned AOF document, lead details, and callback URL. Vendor returns a redirect URL for the eSign portal.
3
Redirect Customer to Vendor Portal — Frontend receives the redirect URL and navigates the customer to the vendor's eSign page. Backend logs the redirect event and starts a session timeout timer.
4
Customer Enters Aadhaar Last 4 Digits — On the vendor portal, the customer enters the last 4 digits of their Aadhaar number. This happens entirely on the vendor side.
5
OTP Sent to Aadhaar-Registered Mobile — The vendor triggers an OTP to the customer's Aadhaar-registered mobile number. Important: This mobile number may differ from the eKYC mobile number used in earlier stages.
6
Customer Enters OTP and Signs — Customer enters the OTP on the vendor portal. Upon successful OTP verification, the vendor applies the electronic signature to the AOF document.
7
Callback Received from Vendor — Vendor sends a callback to the backend with the signed document, signature metadata, and transaction status. Backend validates the callback authenticity.
8
Extract 5 Fields from Signed Document — Parse the signed PDF/XML response to extract: Pincode, Year of Birth (YOB), Gender, Full Name, Aadhaar Last 4 Digits. Store all 5 fields in esign_transactions.
9
Post-eSign Data Verification (HARD GATE) — Compare the 5 extracted fields against previously collected data (KRA, Aadhaar, PAN). Apply the verification rules defined in Section 5. This step has no manual override.
10
Store Signed PDF — Save the signed AOF PDF to the configured file storage drive. Update document_path_info and aof_documents with the signed document path.
11
Route Based on Verification Result —
STP (all match): Set lead.state = ESIGN_DONE, proceed to Stage 14.
NON_STP (name fuzzy < 70): Set lead.state = NON_STP_REVIEW, flag for ops review.
DROP (hard field mismatch): Set lead.state = DROPPED, record DROP_ESIGN_DATA_MISMATCH.
Aadhaar Mobile vs eKYC Mobile
The OTP for eSign is sent to the customer's Aadhaar-registered mobile number, which may be different from the mobile number used during the eKYC journey (Stage 1). The backend must not assume these are the same number.
5. Post-eSign Data Verification (CRITICAL)
After the eSign callback is received and the 5 fields are extracted, the backend performs a mandatory verification against previously collected data. This is a HARD GATE — no manual override is permitted.
5.1 Verification Rules
| Field | Match Type | Reference Source | On Match | On Mismatch |
| Pincode |
Exact match |
KRA data (if VALIDATED / MOD) or Aadhaar data (if NON_KRA / API_DOWN) |
Pass |
Hard Block — DROP_ESIGN_DATA_MISMATCH |
| Year of Birth |
Exact match |
PAN DOB (year component) |
Pass |
Hard Block — DROP_ESIGN_DATA_MISMATCH |
| Gender |
Exact match |
Aadhaar data (gender field from eKYC) |
Pass |
Hard Block — DROP_ESIGN_DATA_MISMATCH |
| Full Name |
Fuzzy match ≥ 70% |
ekyc_name (name from Aadhaar eKYC stage) |
Pass |
NON_STP flag only (score < 70%). Does NOT block the lead. |
| Aadhaar Last 4 |
Exact match |
Aadhaar last 4 from eKYC stage |
Pass |
Hard Block — DROP_ESIGN_DATA_MISMATCH |
5.2 Reference Data Priority
| KRA Status | Reference Source for Pincode | Rationale |
KRA_VALIDATED |
KRA data (pincode from KRA record) |
KRA data is the most authoritative source for validated customers |
KRA_MOD |
KRA data (pincode from KRA record) |
KRA MOD data is still authoritative despite modification flag |
NON_KRA |
Aadhaar data (pincode from eKYC) |
No KRA record exists; fall back to Aadhaar eKYC data |
API_DOWN |
Aadhaar data (pincode from eKYC) |
KRA data unavailable; fall back to Aadhaar eKYC data |
No Manual Override
The post-eSign data verification is a regulatory compliance requirement. If Pincode, Year of Birth, Gender, or Aadhaar Last 4 do not match, the lead must be dropped. There is no ops workflow to override this decision. Only the Name field allows a soft mismatch (NON_STP flag).
6. Post-eSign Scenarios
6.1 Verification Outcome Scenarios
| Scenario | Condition | Action | Lead State |
| All fields match |
All 5 fields pass verification (Name ≥ 70%) |
Proceed to Stage 14 |
ESIGN_DONE (STP) |
| Pincode mismatch |
eSign pincode does not match KRA/Aadhaar pincode |
Hard block — drop the lead |
DROPPED with DROP_ESIGN_DATA_MISMATCH |
| Year of Birth mismatch |
eSign YOB does not match year from PAN DOB |
Hard block — drop the lead |
DROPPED with DROP_ESIGN_DATA_MISMATCH |
| Gender mismatch |
eSign gender does not match Aadhaar gender |
Hard block — drop the lead |
DROPPED with DROP_ESIGN_DATA_MISMATCH |
| Aadhaar Last 4 mismatch |
eSign Aadhaar last 4 does not match eKYC Aadhaar last 4 |
Hard block — drop the lead |
DROPPED with DROP_ESIGN_DATA_MISMATCH |
| Name fuzzy < 70% |
eSign name fuzzy score against ekyc_name is below 70% |
Flag as NON_STP for ops review. Does NOT block. |
NON_STP_REVIEW |
| Vendor missing fields |
Vendor callback does not contain one or more of the 5 required fields |
Initiate CS Journey — manual review required |
CS_ESIGN_VENDOR_DOWN |
| Name match service down |
Fuzzy matching service is unavailable |
Default to NON_STP — flag for ops review |
NON_STP_REVIEW |
Name Mismatch is Soft
A name fuzzy match score below 70% results in a NON_STP flag only. The lead is not dropped — it proceeds to ops review. This is by design, as name variations (spelling, transliteration, initials vs. full name) are common in Aadhaar records.
7. eSign OTP Scenarios
The OTP flow happens entirely on the vendor portal (NSDL/Emudhra). The backend receives the outcome via callback. These scenarios describe the expected vendor-side behavior and how the backend handles each outcome.
| Scenario | Vendor Behavior | Backend Handling |
| OTP correct |
Vendor applies eSign to AOF. Returns signed document via callback. |
Process callback, extract fields, run post-eSign verification. |
| Wrong OTP (attempt 1-4) |
Vendor shows error, allows retry. Remaining attempts displayed. |
No backend action needed. Vendor handles retries internally. |
| 5th wrong OTP |
Vendor locks the session for 10 minutes. The same eSign link remains valid after the lock period. |
No immediate callback. Backend waits for eventual success/failure callback. Session timeout timer continues. |
| OTP expired |
Vendor allows customer to request a new OTP on the same session. |
No backend action needed. Vendor handles OTP regeneration. |
| Browser closed |
Vendor session may expire. |
On resume, backend generates a fresh eSign link (new redirect URL). The customer starts the eSign flow again. |
| Session timeout |
Vendor session expires after inactivity. |
On resume, backend generates a fresh eSign link. Previous transaction is marked as expired. |
10-Minute Lock
When the customer enters the wrong OTP 5 times, the vendor locks the session for 10 minutes. The same link remains valid after the lock expires — the customer does not need a new link. However, if the customer closes the browser and resumes, a fresh link is generated.
8. Backend Validations
| Validation | Description | When Applied | On Failure |
| eSign Callback Verification |
Validate callback authenticity — verify vendor signature, check transaction ID matches, ensure callback is from the expected vendor endpoint |
On receiving vendor callback |
Reject callback. Log as suspicious. Do not process. |
| Post-eSign 5-Field Verification |
Extract and verify Pincode, YOB, Gender, Name, Aadhaar Last 4 against reference data (see Section 5) |
After successful callback processing |
DROP (hard fields) or NON_STP (name only) |
| Signed PDF Storage |
Validate that the signed PDF is complete, non-corrupt, and successfully stored to the configured file storage drive |
After callback processing |
Retry storage. If persistent failure, CS Journey. |
| Vendor Capacity Check |
Verify that the selected vendor has not exceeded its daily budget before initiating the eSign request |
At eSign initiation (Step 1) |
Route to alternate vendor or queue (see Section 3.1) |
| OTP Lock Detection |
Handle scenarios where the vendor reports that the Aadhaar OTP has been locked due to excessive wrong attempts |
On vendor callback or session status check |
Wait for lock expiry (10 min). Same link remains valid. |
9. Existing Stored Procedure Mapping
9.1 Stored Procedures (Old System)
These stored procedures from the legacy system map to Stage 13 eSign functionality. They serve as reference for what the new backend must replicate:
| Stored Procedure | Purpose | New Backend Equivalent |
USP_ALLOW_ESIGN_SJET |
Check if eSign is allowed for the lead (precondition validation) |
eSign eligibility check in eSign service — validates lead.state, AOF existence, vendor capacity |
USP_INSERT_EMUDHRA_Req_Resp |
Log Emudhra eSign request/response payloads |
esign_transactions table insert with vendor = EMUDHRA |
USP_INSERT_UPDATE_NSDL_ESIGN_LOG_SJET |
Log NSDL eSign request/response payloads |
esign_transactions table insert/update with vendor = NSDL |
USP_ESIGN_MATCHING_LOGIC_SJET |
Post-eSign data verification logic — compares extracted fields against reference data |
Post-eSign verification service (Section 5 rules). Implements exact + fuzzy matching. |
USP_ESIGNMATCHING_FLAG_SJET |
Set STP/NON_STP/DROP flags based on eSign matching results |
Verification result handler — updates esign_data_verification_result and routes lead |
USP_UPDATE_ESIGNSTAGE_SJET |
Update lead state after eSign completion |
lead.state update to ESIGN_DONE / NON_STP_REVIEW / DROPPED |
USP_INSERT_UPDATE_PreESIGNFINAL_FILEPATH_INFO_SJET |
Store the pre-eSign (unsigned) AOF file path |
aof_documents table — unsigned_pdf_path field |
USP_INSERT_UPDATE_ESIGN_FILEPATH_INFO_SJET |
Store the signed AOF file path after eSign completion |
aof_documents table — signed_pdf_path field + document_path_info entry |
USP_CLIENT_GENERATECLIENTCODE_REVAMP_SJET |
Generate client code after successful eSign (downstream process) |
Client code generation service — triggered after ESIGN_DONE state transition |
9.2 Old External API Calls
Legacy API methods that the new backend must integrate with (or replace):
| Old API Method | Vendor | Purpose | Notes |
PERFORM_EMUDHRA_ESIGN |
Emudhra |
Initiate Emudhra eSign — sends AOF document and receives redirect URL |
Part of EsignAPI controller |
PERFORM_NSDL_ESIGN_REQUEST_STDAPI |
NSDL |
Initiate NSDL eSign — sends AOF document and receives redirect URL |
Part of EsignAPI controller. Uses standard API mode. |
EsignAPI (controller) |
Both |
Main eSign controller handling initiation, callback, and status endpoints |
Routes to NSDL or Emudhra based on vendor selection logic |
Migration Note
The old system uses separate stored procedures for NSDL and Emudhra logging (USP_INSERT_UPDATE_NSDL_ESIGN_LOG_SJET vs USP_INSERT_EMUDHRA_Req_Resp). The new backend unifies these into a single esign_transactions table with a vendor column to distinguish between providers.
10. Error / Drop Codes
| Code | Type | Trigger | User Impact |
DROP_ESIGN_DATA_MISMATCH |
Hard Drop |
Post-eSign verification fails for Pincode, YOB, Gender, or Aadhaar Last 4 |
lead.state = DROPPED. Journey terminated. No manual override. Customer cannot proceed. |
CS_ESIGN_VENDOR_DOWN |
CS Journey |
Both eSign vendors are down or vendor callback is missing required fields |
Lead enters CS queue. Customer is notified to wait. Manual intervention by CS team. |
CS_AOF_FAIL |
CS Journey |
AOF document is missing, corrupt, or cannot be sent to vendor |
Lead enters CS queue. AOF must be regenerated before eSign can proceed. |
DROP_SESSION_TIMEOUT |
Soft Drop |
eSign session expires without completion and customer does not resume |
Lead remains in current state. On resume, a fresh eSign link is generated. |
BE_ESIGN_INIT_FAILED |
Backend Error |
eSign initiation fails — vendor API returns error or timeout during redirect URL generation |
Retry with same or alternate vendor. If persistent, CS Journey. |
DROP_ESIGN_DATA_MISMATCH is Final
This is the most critical drop code in Stage 13. It indicates a fundamental data mismatch between the eSign identity and the previously verified identity. There is no ops workflow, no manual override, and no retry path. The lead is permanently dropped.
11. Exit State
After Stage 13 completes, the following fields are populated:
| Field | Value / Source | Notes |
lead.state |
ESIGN_DONE (STP) or NON_STP_REVIEW (Non-STP) |
STP leads proceed to Stage 14. Non-STP leads go to ops review queue. |
signed_pdf_path |
File path on configured drive |
Full path to the signed AOF PDF stored on the configured file storage drive. |
esign_vendor |
NSDL or EMUDHRA |
Which vendor was used for this eSign transaction. |
esign_completed_at |
Timestamp (UTC) |
When the eSign callback was successfully processed. |
esign_transaction_id |
Vendor-assigned transaction ID |
Unique transaction identifier from the eSign vendor for audit trail. |
esign_data_verification_result |
STP | NON_STP | DROP |
Outcome of the post-eSign 5-field verification. |
esign_pincode |
Extracted from signed document |
Pincode from the eSign response. Compared against KRA/Aadhaar pincode. |
esign_yob |
Extracted from signed document |
Year of Birth from the eSign response. Compared against PAN DOB year. |
esign_gender |
Extracted from signed document |
Gender from the eSign response. Compared against Aadhaar gender. |
esign_name |
Extracted from signed document |
Full name from the eSign response. Fuzzy matched against ekyc_name. |
esign_aadhaar_last4 |
Extracted from signed document |
Last 4 digits of Aadhaar from the eSign response. Compared against eKYC Aadhaar last 4. |
STP Exit
When all 5 fields match (including Name ≥ 70%), the lead exits Stage 13 with lead.state = ESIGN_DONE and proceeds automatically to Stage 14 (Client Code Generation / Final Processing).
12. Vendor & Integration Calls
| Vendor / System | Purpose | When Called | Sync / Async |
| NSDL eSign |
Primary eSign vendor — initiate eSign, receive callback with signed document |
eSign initiation (when NSDL selected by routing logic) |
Sync (initiation) / Async (callback) |
| Emudhra eSign |
Secondary eSign vendor — initiate eSign, receive callback with signed document |
eSign initiation (when Emudhra selected by routing logic) |
Sync (initiation) / Async (callback) |
| In-Memory Counter |
Daily budget tracking for vendor routing (NSDL: ~3000/day, Emudhra: ~1600/day) |
eSign initiation — checked before vendor selection |
Sync |
| File Storage (Configured Drive) |
Store signed AOF PDF and update file path references |
After callback processing |
Sync |
| CleverTap |
Event tracking (eSign initiated, eSign completed, eSign failed, verification result) |
After eSign initiation; after callback processing; after verification |
Async |
| Zoho CRM |
Lead status update — eSign stage transition |
After lead state change (ESIGN_DONE / NON_STP_REVIEW / DROPPED) |
Async |
| CDP |
Customer data platform update with eSign status and verification result |
After eSign completion and verification |
Async |
| Datalake |
Analytics event logging for eSign metrics (vendor usage, verification pass/fail rates) |
After eSign completion and verification |
Async |
| Appsflyer |
Attribution tracking — eSign completion event |
After successful eSign completion |
Async |
| Firebase |
Push notification and analytics events |
After eSign completion or failure (notify customer of result) |
Async |
13. Database Tables
13.1 Operational Tables
| Table | Purpose | Key Fields (Stage 13 Relevant) |
esign_transactions |
Master eSign transaction record — one row per eSign attempt |
lead_id, vendor (NSDL/EMUDHRA), transaction_id, status (INITIATED/COMPLETED/FAILED/EXPIRED), redirect_url, callback_payload, esign_pincode, esign_yob, esign_gender, esign_name, esign_aadhaar_last4, verification_result (STP/NON_STP/DROP), name_match_score, initiated_at, completed_at |
aof_documents |
AOF document records — tracks unsigned and signed PDFs |
lead_id, unsigned_pdf_path, signed_pdf_path, esign_transaction_id, generated_at, signed_at |
leads |
Master lead record |
state (ESIGN_DONE / NON_STP_REVIEW / DROPPED), esign_vendor, esign_completed_at, esign_transaction_id, esign_data_verification_result |
document_path_info |
File path reference for all documents |
lead_id, document_type (SIGNED_AOF), file_path, storage_type, created_at |
13.2 Reference / Audit Tables
| Table | Purpose | Key Fields |
journey_stage_events |
Stage transition audit trail |
lead_id, stage (STAGE_13), event (ESIGN_INITIATED / ESIGN_COMPLETED / ESIGN_FAILED / VERIFICATION_PASS / VERIFICATION_FAIL), metadata, created_at |
lead_state_transitions |
State machine audit log |
lead_id, from_state (AOF_GENERATED / KRA_RECHECKED), to_state (ESIGN_DONE / NON_STP_REVIEW / DROPPED), reason, triggered_by, created_at |
downstream_events |
Async events dispatched to external systems |
lead_id, target (clevertap / zoho / cdp / datalake / appsflyer / firebase), event_type, status, dispatched_at |
14. Implementation Notes
File Storage via Configured Drive
Signed AOF PDFs are stored on a configured file storage drive (not cloud blob storage). The path is determined by the application configuration. Ensure the drive is accessible, has sufficient space, and that file paths are stored in both aof_documents.signed_pdf_path and document_path_info.
In-Memory Counter (Not Redis)
The BRD specifies Redis for daily budget counters, but the current implementation uses in-memory counters (ConcurrentDictionary). This means counters are per-instance and not shared across multiple backend instances. In a multi-instance deployment, each instance independently tracks its own counter. The total daily budget must be divided across instances or a shared counter mechanism must be implemented in a future iteration.
Post-eSign Verification is a HARD GATE
This cannot be overemphasized: the post-eSign 5-field data verification is a regulatory compliance requirement. There is no manual override, no ops workflow to bypass it, and no retry path for hard field mismatches (Pincode, YOB, Gender, Aadhaar Last 4). Only the Name field allows a soft mismatch resulting in NON_STP flag. Developers must not build any override mechanism for the hard fields.
Vendor Callback Security
All vendor callbacks must be validated for authenticity before processing. Verify the vendor's digital signature on the callback payload, confirm the transaction ID matches an initiated transaction, and ensure the callback originates from the vendor's known IP range. Reject and log any suspicious callbacks.