Core domain entities are reused by origination, loan management, collections, partner APIs and compliance. They should live behind stable service APIs because PAN, GSTIN, CKYC, beneficial-owner, consent and document records are regulatory evidence. Do not duplicate borrower identity inside every loan table. Use immutable identifiers, versioned evidence and role mappings.
The model assumes PostgreSQL-style types. uuid fields are application-generated. Monetary fields are numeric(18,2) and percentages are numeric(9,6) unless stated otherwise. All tables require created_at timestamptz not null, created_by uuid not null, updated_at timestamptz not null, updated_by uuid not null, and version int not null default 1; those common audit fields are omitted from the field tables below to keep each entity focused.
| Rule | Implementation |
|---|
| Party is the root identity | Every natural person, proprietorship, partnership, LLP, company, HUF, trust, society, co-operative, guarantor, DSA, vendor and partner is a party. |
| Legal borrower is explicit | A sole proprietorship may use promoter PAN and a trade name, but the borrower role must show whether the legal obligor is the proprietor natural person or a registered legal entity. |
| Roles are contextual | The same party can be applicant, co-applicant, promoter, beneficial owner, director, partner, guarantor, collateral owner, DSA, vendor or partner on different applications. |
| Evidence is versioned | KYC, document, consent and external-verification results are append-only versions. Status can change; prior evidence cannot be overwritten. |
| Regulatory data is purpose-bound | Consent records must store purpose, scope, retention and revocation because RBI’s Digital Lending Directions require explicit, need-based consent, revocation controls and data minimisation for DLAs/LSPs. |
| Field | Type | Constraints | Nullable | References |
|---|
party_id | uuid | Primary key | No | None |
party_type | enum | Values: natural_person, business_entity, huf, trust, society, cooperative, vendor, channel_partner, regulated_entity | No | None |
display_name | text | 1-200 chars; normalized for search | No | None |
primary_pan | varchar(10) | Uppercase PAN format; unique where present and active | Yes | None |
primary_gstin | varchar(15) | GSTIN format; unique with is_active=true where present | Yes | None |
primary_mobile | varchar(15) | E.164 or India 10-digit normalized; unique only within customer realm | Yes | None |
primary_email | text | Lowercase email format | Yes | None |
risk_segment | enum | retail_sme, micro_sme, small_sme, medium_sme, partner, vendor, employee, unknown | No | None |
party_status | enum | prospect, active, blocked, deceased, merged, archived | No | None |
blocked_reason_code | text | Required when party_status=blocked | Yes | reason_code.code |
merged_into_party_id | uuid | Required when party_status=merged; cannot equal self | Yes | party.party_id |
| Field | Type | Constraints | Nullable | References |
|---|
party_id | uuid | Primary key; party.party_type=natural_person | No | party.party_id |
full_name | text | 1-200 chars | No | None |
date_of_birth | date | Must imply age 18 or higher for signatory; product policy may require 21-70 | Yes | None |
gender | enum | female, male, transgender, not_disclosed | Yes | None |
father_or_spouse_name | text | 1-200 chars | Yes | None |
pan | varchar(10) | Uppercase PAN; unique across active natural persons where present | Yes | None |
aadhaar_last4 | char(4) | Last four digits only; never store full Aadhaar unless legally permitted | Yes | None |
ckyc_id | varchar(14) | CKYC identifier format where available | Yes | None |
pep_flag | boolean | Default false | No | None |
deceased_flag | boolean | Default false | No | None |
deceased_date | date | Required when deceased_flag=true | Yes | None |
| Field | Type | Constraints | Nullable | References |
|---|
party_id | uuid | Primary key; party.party_type in business-capable values | No | party.party_id |
legal_name | text | Name as per PAN, MCA, deed, GST or registration | No | None |
trade_name | text | Business name used with customers | Yes | None |
constitution_type | enum | proprietorship, partnership, llp, private_limited, public_limited, opc, huf, trust, society, cooperative, other | No | None |
date_of_incorporation | date | Required except informal proprietorship where unknown | Yes | None |
pan | varchar(10) | Entity PAN where available; proprietorship may use proprietor PAN by policy | Yes | None |
cin | varchar(21) | Required for companies when available from MCA | Yes | None |
llpin | varchar(8) | Required for LLP when available | Yes | None |
gstin_primary | varchar(15) | Primary GSTIN for underwriting; additional GSTINs go to tax_registration | Yes | None |
udyam_registration_number | varchar(30) | Udyam/UAP identifier; uppercase | Yes | None |
msme_classification | enum | micro, small, medium, not_msme, unverified | No | None |
industry_code_type | enum | nic_2008, internal, gst_hsn_dominant, unknown | No | None |
industry_code | text | Required when industry is known | Yes | None |
business_vintage_months | int | 0-1200; computed or declared | Yes | None |
registered_address_id | uuid | Current registered address | Yes | address.address_id |
operating_address_id | uuid | Main business location | Yes | address.address_id |
entity_status | enum | active, inactive, dissolved, struck_off, gst_cancelled, under_insolvency, unknown | No | None |
| Field | Type | Constraints | Nullable | References |
|---|
relationship_id | uuid | Primary key | No | None |
subject_party_id | uuid | Party being related | No | party.party_id |
related_party_id | uuid | Related party | No | party.party_id |
relationship_type | enum | proprietor_of, partner_of, designated_partner_of, director_of, shareholder_of, karta_of, trustee_of, authorised_signatory_of, guarantor_for, collateral_owner_for, group_company_of, relative_of | No | None |
ownership_percent | numeric(9,6) | 0-100; required for ownership relationships | Yes | None |
control_description | text | Required when control exists without ownership threshold | Yes | None |
effective_from | date | Not future-dated unless pending record | No | None |
effective_to | date | Must be after effective_from | Yes | None |
verification_status | enum | declared, document_verified, registry_verified, rejected | No | None |
| Field | Type | Constraints | Nullable | References |
|---|
application_party_id | uuid | Primary key | No | None |
application_id | uuid | One application | No | application.application_id |
party_id | uuid | Participating party | No | party.party_id |
role | enum | primary_applicant, co_applicant, proprietor, promoter, director, partner, authorised_signatory, beneficial_owner, guarantor, collateral_owner, nominee_contact, reference | No | None |
is_obligor | boolean | True when party signs repayment or guarantee obligation | No | None |
signing_required | boolean | True when loan/security document signature is required | No | None |
kyc_required | boolean | True for borrower, BO, authorised signatory, guarantor, security provider | No | None |
role_status | enum | proposed, verified, rejected, removed | No | None |
RBI KYC requires beneficial-owner identification for legal entities. The thresholds reflected in this schema follow the KYC Master Direction: more than 10 percent for companies and partnerships, more than 15 percent for unincorporated associations/bodies of individuals, and trust-specific author/trustee/beneficiary/control tests.
| Field | Type | Constraints | Nullable | References |
|---|
beneficial_owner_id | uuid | Primary key | No | None |
business_party_id | uuid | Legal entity being assessed | No | business_entity.party_id |
person_party_id | uuid | Natural person BO | No | natural_person.party_id |
bo_basis | enum | ownership, capital, profits, voting_right, control, senior_managing_official, trust_author, trustee, trust_beneficiary | No | None |
ownership_percent | numeric(9,6) | 0-100 where measurable | Yes | None |
threshold_applicable_percent | numeric(9,6) | 10 or 15 or null for control-only | Yes | None |
kyc_profile_id | uuid | BO KYC evidence | Yes | kyc_profile.kyc_profile_id |
verification_status | enum | pending, verified, waived_by_compliance, rejected | No | None |
source_document_id | uuid | Shareholding, deed, declaration or registry evidence | Yes | document_instance.document_id |
| Field | Type | Constraints | Nullable | References |
|---|
address_id | uuid | Primary key | No | None |
party_id | uuid | Owner party | No | party.party_id |
address_type | enum | registered, operating, residential, communication, factory, warehouse, collateral, billing | No | None |
line1 | text | 1-200 chars | No | None |
line2 | text | 1-200 chars | Yes | None |
city | text | 1-100 chars | No | None |
district | text | 1-100 chars | Yes | None |
state_code | char(2) | Indian state/UT code | No | state_master.state_code |
pincode | char(6) | Indian PIN format | No | pincode_master.pincode |
geo_latitude | numeric(10,7) | -90 to 90 | Yes | None |
geo_longitude | numeric(10,7) | -180 to 180 | Yes | None |
verification_status | enum | declared, otp_verified, document_verified, field_verified, negative, undeliverable | No | None |
valid_from | date | Required | No | None |
valid_to | date | Must be after valid_from | Yes | None |
| Field | Type | Constraints | Nullable | References |
|---|
consent_id | uuid | Primary key | No | None |
party_id | uuid | Person/entity giving consent | No | party.party_id |
application_id | uuid | Required for loan-purpose consent | Yes | application.application_id |
consent_type | enum | contact, bureau_pull, gst_fetch, account_aggregator, bank_statement_upload, digilocker, ckyc_fetch, udyam_verify, itr_fetch, privacy_policy, marketing, data_sharing_partner | No | None |
purpose_code | text | Machine-readable purpose; no generic purpose allowed | No | purpose_master.code |
scope_json | jsonb | Data categories, date range, frequency, recipient | No | None |
consent_text_version | text | Version shown to user | No | None |
captured_channel | enum | branch, dsa_app, customer_portal, dla, partner_api, call_recording, paper_form | No | None |
captured_at | timestamptz | Required | No | None |
expires_at | timestamptz | Required for time-bound fetches | Yes | None |
revoked_at | timestamptz | Set on revocation | Yes | None |
evidence_uri | text | Link to OTP, clickwrap, recording, signed form or AA artefact | No | document_instance.storage_uri |
status | enum | active, expired, revoked, superseded, invalid | No | None |
| Field | Type | Constraints | Nullable | References |
|---|
kyc_profile_id | uuid | Primary key | No | None |
party_id | uuid | KYC subject | No | party.party_id |
kyc_level | enum | lead_basic, full_cdd, v_cip, reliance_ckyc, simplified, enhanced_due_diligence | No | None |
risk_category | enum | low, medium, high, prohibited | No | None |
kyc_status | enum | not_started, pending, verified, rejected, expired, rekyc_due, blocked | No | None |
verified_at | timestamptz | Required when verified | Yes | None |
periodic_kyc_due_date | date | Required for active customer relationship | Yes | None |
ckyc_id | varchar(14) | CKYC identifier where retrieved or uploaded | Yes | None |
v_cip_session_id | uuid | Required when KYC method is V-CIP | Yes | v_cip_session.v_cip_session_id |
rejection_reason_code | text | Required when rejected | Yes | reason_code.code |
approved_by | uuid | KYC checker or AML officer | Yes | user.user_id |
| Field | Type | Constraints | Nullable | References |
|---|
screening_hit_id | uuid | Primary key | No | None |
party_id | uuid | Screened party | No | party.party_id |
application_id | uuid | Application context | Yes | application.application_id |
screening_type | enum | sanctions, pep, adverse_media, internal_negative, fraud_negative, watchlist, wilful_defaulter, defaulter_list | No | None |
provider | text | Vendor or internal list | No | None |
match_score | numeric(6,3) | 0-100 | Yes | None |
hit_status | enum | open, false_positive, true_positive, cleared_with_conditions, blocked | No | None |
resolution_note | text | Required on closure | Yes | None |
resolved_by | uuid | Compliance/AML checker | Yes | user.user_id |
resolved_at | timestamptz | Required on closure | Yes | None |
| Field | Type | Constraints | Nullable | References |
|---|
document_requirement_id | uuid | Primary key | No | None |
checklist_id | uuid | Requirement set | No | document_checklist.checklist_id |
document_type | enum | pan, ovd, gst_certificate, udyam_certificate, partnership_deed, llp_agreement, moa_aoa, board_resolution, bank_statement, gst_return, itr, financial_statement, invoice, title_deed, valuation_report, legal_opinion, insurance_policy, loan_agreement, guarantee, nach_mandate | No | None |
required_for_role | enum | applicant, co_applicant, promoter, bo, guarantor, collateral_owner, application, collateral, disbursement | No | None |
mandatory_flag | boolean | Default true | No | None |
allow_waiver_flag | boolean | Default false | No | None |
min_copies | int | 1 or more | No | None |
validity_days | int | Positive; null means not age-limited | Yes | None |
rule_source | text | Product/constitution/policy rule that created requirement | No | None |
status | enum | pending, received, verified, deficient, waiver_requested, waived, not_applicable | No | None |
| Field | Type | Constraints | Nullable | References |
|---|
document_id | uuid | Primary key | No | None |
document_requirement_id | uuid | Requirement satisfied by document | Yes | document_requirement.document_requirement_id |
party_id | uuid | Document owner | Yes | party.party_id |
application_id | uuid | Application context | Yes | application.application_id |
document_type | enum | Same controlled vocabulary as document_requirement.document_type | No | None |
document_number | text | PAN, GSTIN, certificate number, deed number where applicable | Yes | None |
issue_date | date | Required where document has issue date | Yes | None |
expiry_date | date | Required where document expires | Yes | None |
issuer_name | text | Issuer or authority | Yes | None |
storage_uri | text | DMS object path; immutable version | No | None |
sha256_hash | char(64) | Hash of stored object | No | None |
mime_type | text | Allowed MIME list | No | None |
verification_status | enum | uploaded, extracted, verified, deficient, fraud_suspect, rejected, superseded | No | None |
verified_by | uuid | Required when verified/rejected by human | Yes | user.user_id |
verified_at | timestamptz | Required when verified/rejected | Yes | None |
| Field | Type | Constraints | Nullable | References |
|---|
verification_id | uuid | Primary key | No | None |
party_id | uuid | Subject | Yes | party.party_id |
application_id | uuid | Context | Yes | application.application_id |
provider | enum | pan_nsdl, gstn_vendor, udyam, ckyc, digilocker, aa, bureau, mca, cersai, bank_penny_drop, sanctions_vendor, internal | No | None |
request_id | text | Provider request id | No | None |
request_payload_hash | char(64) | Hash only for sensitive payload | No | None |
response_payload_uri | text | Encrypted raw response path | Yes | None |
normalized_status | enum | success, no_hit, mismatch, temporary_failure, provider_error, consent_required, rejected | No | None |
normalized_json | jsonb | Parsed fields used by business logic | Yes | None |
verified_at | timestamptz | Provider response timestamp | No | None |
| Constraint | Build rule |
|---|
| KYC cannot be a boolean | Use kyc_profile with level, risk category, status, evidence and periodic review date. |
| BO checks cannot be optional for legal entities | Application cannot reach kyc_verified until all required beneficial_owner rows are verified, rejected or waived by compliance. |
| Consent must be revocable | Every data pull must reference an active consent_artifact; revocation blocks future pulls but does not delete records the RE must retain for audit/regulatory purposes. |
| Documents are immutable | A corrected upload creates a new document_instance; old versions remain linked and marked superseded. |
| Dedupe must support fuzzy matching | Keep exact identifiers on party; keep fuzzy and cross-identifier results in dedupe_match within LOS/BRE so false positives are auditable. |