KYC & Documents

RBI-compliant KYC verification — PAN, Aadhaar & document upload.

KYC Verification KYC

@if (!isNonIndividual) { @if (!isMinor) { @if (readOnly) {
Client has PAN Card @if (kycForm.get('hasPan')?.value) { check_circle Yes } @else { cancel No }
} @else {
Client has PAN Card
} } @if (!isMinor && kycForm.get('hasPan')?.value) {
PAN Number credit_card @if (kycForm.get('panNumber')?.hasError('pattern') && kycForm.get('panNumber')?.touched) { Invalid PAN format } @if (panVerifyStatus === 'VERIFIED') { check_circle } @if (panVerifyStatus === 'FAILED') { cancel } Format: ABCDE1234F
@if (panVerifyStatus === 'NONE' || panVerifyStatus === 'FAILED') { } @if (panVerifyStatus === 'VERIFIED') { Verified }
}
Aadhaar Number fingerprint @if (kycForm.get('aadhaarNumber')?.hasError('pattern') && kycForm.get('aadhaarNumber')?.touched) { Must be 12 digits } @if (aadhaarVerifyStatus === 'VERIFIED') { check_circle }
@if ((aadhaarVerifyStatus === 'NONE' || aadhaarVerifyStatus === 'FAILED') && !aadhaarOtpSent) { } @if (aadhaarOtpSent && aadhaarVerifyStatus !== 'VERIFIED') { } @if (aadhaarVerifyStatus === 'VERIFIED') { Verified }
@if (aadhaarOtpSent && aadhaarVerifyStatus !== 'VERIFIED') {
verified_user
Aadhaar OTP Verification A 6-digit OTP has been sent to the Aadhaar-linked mobile number
@for (digit of otpDigits; track $index) { }
@if (resendCountdown > 0) { Resend in 00:{{ resendCountdown.toString().padStart(2, '0') }} } @else { Didn't receive the code? }
} @if (aadhaarVerifyError) {
{{ aadhaarVerifyError }}
} @if (panVerifyStatus === 'VERIFIED' || aadhaarVerifyStatus === 'VERIFIED') {
@if (panVerifyStatus === 'VERIFIED' && aadhaarVerifyStatus === 'VERIFIED') { Identity Verified } @else if (panVerifyStatus === 'VERIFIED') { PAN Verified } @else { Aadhaar Verified }
@if (panVerifyStatus === 'VERIFIED' && aadhaarVerifyStatus === 'VERIFIED') { Both PAN & Aadhaar confirmed against NSDL & UIDAI records. } @else if (panVerifyStatus === 'VERIFIED') { PAN confirmed against NSDL records. Aadhaar verification pending. } @else { Aadhaar confirmed via UIDAI OTP. PAN verification pending. }
{{ (panVerifyStatus === 'VERIFIED' ? 1 : 0) + (aadhaarVerifyStatus === 'VERIFIED' ? 1 : 0) }} / 2
PAN @if (panVerifyStatus === 'VERIFIED') { Verified } @else { Pending }
@if (panVerifyStatus === 'VERIFIED') {
Name on PAN {{ panVerifyName }}
PAN Number {{ kycForm.get('panNumber')?.value }}
Verified On {{ panVerifiedOn }}
} @else {
Enter PAN above and click Verify PAN.
}
Aadhaar @if (aadhaarVerifyStatus === 'VERIFIED') { Verified } @else { Pending }
@if (aadhaarVerifyStatus === 'VERIFIED') {
Name on Aadhaar {{ aadhaarVerifyName }}
Aadhaar Number XXXX XXXX {{ (kycForm.get('aadhaarNumber')?.value || '').slice(-4) }}
Channel UIDAI OTP
} @else {
Enter Aadhaar above and click Verify Aadhaar.
}
@if (panAadhaarSeedingStatus) { }
} } @if (isNonIndividual) { Incorporation Date calendar_today
GSTIN Number receipt_long @if (kycForm.get('gstinNumber')?.hasError('pattern') && kycForm.get('gstinNumber')?.touched) { Invalid GSTIN format (e.g. 22AAAAA0000A1Z5) } @if (gstinVerifyStatus === 'VERIFIED') { check_circle } @if (gstinVerifyStatus === 'FAILED') { cancel }
@if (gstinVerifyStatus === 'NONE' || gstinVerifyStatus === 'FAILED') { } @if (gstinVerifyStatus === 'VERIFIED') { Verified }
@if (gstinVerifyStatus === 'VERIFIED') {
GSTIN Verified Successfully
@if (gstinVerifyName) {
Trade Name / Legal Name {{ gstinVerifyName }}
} @if (gstinVerifyState) {
State {{ gstinVerifyState }}
}
Verified On {{ gstinVerifiedOn }}
} @if (constitutionMeta?.value === 'PROPRIETORSHIP') {
PAN of Proprietor credit_card @if (kycForm.get('panNumber')?.hasError('pattern') && kycForm.get('panNumber')?.touched) { Invalid PAN format } @if (panVerifyStatus === 'VERIFIED') { check_circle } @if (panVerifyStatus === 'FAILED') { cancel } PAN of Proprietor (no separate entity PAN needed)
@if (panVerifyStatus === 'NONE' || panVerifyStatus === 'FAILED') { } @if (panVerifyStatus === 'VERIFIED') { Verified }
@if (panVerifyStatus === 'VERIFIED') {
PAN Verified Successfully
Proprietor Name (as per PAN) {{ panVerifyName }}
Verified On {{ panVerifiedOn }}
} } @else {
{{ constitutionMeta?.panLabel || 'PAN of Entity' }} credit_card @if (kycForm.get('enterprisePan')?.hasError('pattern') && kycForm.get('enterprisePan')?.touched) { Invalid PAN format } @if (entPanVerifyStatus === 'VERIFIED') { check_circle } @if (entPanVerifyStatus === 'FAILED') { cancel } {{ constitutionMeta?.panHint || 'PAN mandatory' }}
@if (entPanVerifyStatus === 'NONE' || entPanVerifyStatus === 'FAILED') { } @if (entPanVerifyStatus === 'VERIFIED') { Verified }
@if (entPanVerifyStatus === 'VERIFIED') {
Enterprise PAN Verified
Entity Name (as per PAN) {{ entPanVerifyName }}
Verified On {{ entPanVerifiedOn }}
} } @if (constitutionMeta?.showCin) {
CIN (Corporate Identity Number) business @if (kycForm.get('cinNumber')?.hasError('pattern') && kycForm.get('cinNumber')?.touched) { Invalid CIN format (21 characters) } @if (cinVerifyStatus === 'VERIFIED') { check_circle }
@if (cinVerifyStatus === 'NONE' || cinVerifyStatus === 'FAILED') { } @if (cinVerifyStatus === 'VERIFIED') { Verified }
@if (cinVerifying) {
{{ cinProgressValue }}%
@for (step of cinProgressSteps; track step.label; let i = $index) {
{{ step.label }}
}
} @if (cinVerifyStatus === 'VERIFIED') {
CIN Verified — {{ cinCompanyName }}
CIN {{ kycForm.get('cinNumber')?.value }}
Verified On {{ cinVerifiedOn }}
} } }
@if (!clientId) {
PAN verification requires client to be created first. Complete General tab and click Next to save.
}

KYC Document OVD @if (kycTemplate?.clientTypeName) { {{ kycTemplate.clientTypeName }} }

badge

Identity proof

Select document type — upload requirements will update automatically

@for (group of kycDocGroups; track group.label; let last = $last) {
folder_open {{ group.label }}
@for (p of group.items; track p.id) { {{ isKycDocSelected(p) ? 'check_circle' : 'radio_button_unchecked' }} {{ p.name }} }
@if (!last) {
} }
@if (kycForm.get('kycDocumentName')?.hasError('required') && kycForm.get('kycDocumentName')?.touched) {
KYC Document Type is required
}
@if (selectedKycDoc?.info) {
check_circle {{ selectedKycDoc.info }}
} @if (selectedKycDoc) {
image {{ selectedKycDoc.sides === 2 ? 'Front side' : selectedKycDoc.name + ' (single copy)' }} Required
@if (existingKycDocuments.length > 0 && !kycDocFiles.front && readOnly) {
@if (kycDocMergedPreview) { Saved KYC document } @else { description }
{{ existingKycDocuments[0].fileName || existingKycDocuments[0].name }}
} @else { }
@if (selectedKycDoc.sides === 2) {
image Back side @if (!(existingKycDocuments.length > 0 && !kycDocFiles.back)) { Required }
@if (existingKycDocuments.length > 0 && !kycDocFiles.back && readOnly) {
@if (kycDocMergedPreview) { Saved KYC document } @else { description }
{{ existingKycDocuments[0].fileName || existingKycDocuments[0].name }}
} @else { }
}
@if ( selectedKycDoc.sides === 2 && kycDocMergedPreview && !readOnly && (kycDocFiles.front || kycDocFiles.back) ) {
filter_none Combined preview (front + back) Saved as a single PNG. Re-crop a side to update the merged file.
Merged KYC document
} }
{{ selectedKycDoc?.documentKeyLabel || 'Document Number' }} tag @if (kycForm.get('kycDocumentNumber')?.hasError('required') && kycForm.get('kycDocumentNumber')?.touched) { Document number is required } @else if ( kycForm.get('kycDocumentNumber')?.hasError('pattern') && kycForm.get('kycDocumentNumber')?.touched ) { Only letters, numbers, hyphens and slashes allowed } Issue Date calendar_today Expiry Date event_busy If applicable (Passport, DL, Visa)

Risk Category & KYC Review

Override Auto-calculated Risk Category
Risk Category @if (kycForm.get('riskCategory')?.value === 1) { Low Risk } @else if (kycForm.get('riskCategory')?.value === 2) { Medium Risk } @else if (kycForm.get('riskCategory')?.value === 3) { High Risk } Low Risk 10 yr renewal Medium Risk 8 yr renewal High Risk 2 yr renewal KYC Review Due Date Auto: Low 10yr, Medium 8yr, High 2yr @if (riskCalculationReason && !kycForm.get('allowRiskOverride')?.value && !readOnly) {
info Auto-determined: {{ riskCalculationReason }}
}
@if (showRiskRulesTable) {
Risk Category Primary Criteria / Basis Examples / Thresholds
High Risk PEPs, High-Risk Industries, High Income / Turnovers Politicians; Real Estate, Import/Export; Commission Agents; Income > ₹20L/yr; Turnover > ₹1 Crore
Medium Risk Self-Employed, Professionals, Cross-Border, Moderate Income Business Owners, Traders; Doctors, Lawyers, CAs; NRI, PIO, OCI; Income > ₹6L/yr; Turnover > ₹20L
Low Risk Non-Earning, Government Employment, Agriculture / Manual Students, Minors, Homemakers; Govt/PSU Employees; Farmers, Daily Wage; Income <= ₹6L/yr
} {{ isKycVerified ? 'Reason for Re-edit *' : 'Remarks' }} @if (isKycVerified && !kycForm.get('remarks')?.value?.trim()) { Remarks are mandatory when updating a verified KYC record } @if (isKycVerified) {
info KYC is VERIFIED. Any update will reset the status to IN PROGRESS and require PAN re-verification.
}
KYC Expiry Alert
@for (rc of riskCards; track rc.value) {
{{ rc.label }} Renewal every {{ rc.years }} years
@if (kycForm.get('riskCategory')?.value === rc.value) { Next: {{ getKycExpiryDate(rc.years) }} }
}
@if (kycForm.get('riskCategory')?.value) {
System will auto-alert before KYC expiry on {{ getKycExpiryDate(getRenewalYears()) }}.
}

AML & Compliance Checks

Override Auto-calculated AML & Compliance Checks
AML Check Status Clear Flagged Blocked Banned Entity List Match No Yes Partial Match Willful Defaulter Flag No Yes ECGC SAL Flag No Yes Dormant / Inoperative Flag Active Dormant Inoperative
@if (kycForm.get('amlCheckStatus')?.value === 'BLOCKED' || kycForm.get('bannedEntityMatch')?.value === 'YES') {
BLOCKED — Account opening cannot proceed.
}

Freeze / Debit Controls

Account Status Active Frozen Unfreeze No Debit @if (kycForm.get('accountStatus')?.value === 'FROZEN') { Reason for Freeze Court Order AML Regulatory Internal Other Freeze Authority Branch Manager Head Office Regulatory Authority }

Form 97 / 98 @if (!kycForm.get('hasPan')?.value) { Required — No PAN }

@if (!kycForm.get('hasPan')?.value) {
warning Client does not have PAN. Form 97/98 is mandatory.
Form Type Form 97 Form 98 Declaration Date
} @else {

Client has PAN — Form 97/98 not required.

}

Form 121

Form 121 Applicable
@if (kycForm.get('form15Applicable')?.value) { Form Type Form 121 (Below 60 years) Form 121 (Senior Citizen 60+) Financial Year }

Photograph & Signature Required

@if (photographPreview) {
Photo
} @else {
Client Photograph * JPG / PNG, max 2MB
@if (photoSigTouched) { Photograph is required } }
@if (signaturePreview) {
Signature
} @else {
Specimen Signature * Scanned image, max 1MB
@if (photoSigTouched) { Signature is required } }