# Lendly — Technical design and implementation handoff Version 1.0 · 01 November 2026 · Proposed architecture; not deployed ## 1. Architecture decision Use a modular backend for the MVP instead of independently deployed microservices. Separate application/consent, verification, offers/documents, operations and servicing modules by interfaces and database ownership. Split services only when load, isolation or team ownership justifies the overhead. Two deployment domains are essential. The LSP domain handles minimal profile/contact data, user experience, references and scoped workflow coordination. A lender-owned or lender-controlled domain owns sensitive assessment/KYC evidence, issued offers, financial instructions and the authoritative ledger. Contractual permission and legal review determine exact boundaries; hosting a database in India alone does not authorize unrestricted LSP storage. ```text Borrower mobile app Operations browser │ │ ├──── LSP edge / API gateway ────────────┤ │ Auth, minimal profile, ticket routing, │ consent UI references, status projection │ │ mTLS / scoped API └──────────── LENDER CONTROLLED DOMAIN ────────────┐ Application workflow │ Verification adapters │ Credit review + policy versions │ Offer / KFS / document signing │ Payout & repayment orchestration │ Ledger / LOS / LMS integration │ │ │ PostgreSQL + outbox + job worker │ Encrypted document vault │ │ │ Authorized external providers ◄───────┘ OTP · KYC · bureau · income · signing mandate · bank / payment rails Funds: lender account ⇄ verified borrower account No funds pass through the LSP application or its accounts. ``` The diagram is a proposed ownership model, not an assertion that every lender offers these APIs. Agree an integration boundary with the selected lender first. Browser/mobile clients never carry provider secrets or directly trust redirect parameters. ## 2. Proposed stack | Layer | Recommendation | Rationale | | --- | --- | --- | | Mobile | React Native + TypeScript | Shared Android/iOS product logic; validate provider SDK support | | Admin | React + TypeScript | Component reuse, accessible case workflows | | Backend | NestJS + TypeScript modular application | Explicit modules, guards, validation and adapter boundaries | | Transaction store | Managed PostgreSQL in India | Transactional state, constraints and durable workflow records | | Async work | Durable queue and worker with transactional outbox | Reliable delivery and controlled retries | | Documents | India-region private object store with key management | Versioned lender-controlled evidence and short-lived retrieval | | Identity | Borrower OTP/session service; workforce OIDC + MFA | Separate consumer and operator trust boundaries | | Observability | Structured logs, metrics, traces and alert routing | Correlation without exposing raw PII | These are proposed engineering choices, not user-mandated technologies. Pin supported stable versions after checking lender SDKs, security maintenance and native build compatibility. This package deliberately does not guess a dependency lockfile. Official implementation references: [React Native](https://reactnative.dev/docs/getting-started), [NestJS](https://docs.nestjs.com/), [PostgreSQL transaction isolation](https://www.postgresql.org/docs/current/transaction-iso.html). PostgreSQL documents concurrency behavior; choose explicit locking/version checks for decision and financial transitions rather than assuming a read followed by write is atomic. ## 3. Modules and contracts | Module | Inputs | Outputs / responsibility | | --- | --- | --- | | Authentication | OTP challenge or workforce token | Identity, session, role and lender/customer scope | | Consent | Purpose, notice version, recipients, action | Immutable evidence reference and current grant projection | | Application | Profile reference, requested amount/tenor | Versioned draft, status and submission validation | | Verification | Application version and valid consent | Provider reference, result, expiry, manual-review reason | | Credit review | Current evidence and policy version | Maker proposal and independent lender decision | | Offers | Lender decision, pricing and schedule | Versioned offer, KFS and expiry | | Contracting | Accepted offer and acknowledgements | Signature reference, mandate reference and verified status | | Money movement | Fully satisfied lender release gates | Idempotent payout/payment instruction and enquiry status | | Servicing projection | Lender ledger/schedule events | Customer-visible due, receipt and status; no independent ledger truth | | Complaints/privacy | Request, requester and scope | Case, ownership, deadline, response and evidence | | Audit/reporting | Authorized command/event | Append-only activity and controlled exports | Keep lender-specific integration DTOs inside adapters. Normalize states into the documented application model while preserving raw provider status in the lender-controlled event record. ## 4. Data model and ownership All identifiers are UUIDs or opaque lender references. Every applicable record includes `lender_id`, timestamps, version and provenance. Store money as integer paise in `bigint`; rates as fixed decimal. Record timestamps in UTC and display borrower schedules in the lender's business timezone, proposed Asia/Kolkata. Encrypt sensitive columns with managed keys and rotation policy. | Entity / key | Essential fields and relationships | Storage owner | | --- | --- | --- | | Customer / id | auth_subject, masked contact, lender_customer_ref | LSP minimal permitted data; lender full customer | | ConsentEvent / id | customer_id, purpose, recipient_set, notice_version, action, recorded_at, evidence_ref | Lender evidence; LSP scoped reference | | Application / id | customer_id, amount_paise, tenor_months, profile_ref, status, policy_version, version | Lender authoritative; LSP status/reference | | Verification / id | application_id, kind, provider_ref, status, evidence_version, expires_at | Lender; provider-controlled raw evidence | | BankAccount / id | customer_id, provider_token, last4, match_status, verified_at | Lender vault; masked LSP reference if permitted | | DecisionProposal / id | application_id, application_version, maker_user_id, outcome, reason_code, evidence_hash | Lender | | Decision / id | proposal_id, checker_user_id, outcome, policy_version, decided_at | Lender | | Offer / id | application_id, principal, rate, fees, tax, apr, net_disbursal, expires_at, version, status | Lender | | Document / id | offer_id, type, storage_key, sha256, version, delivered_at | Lender-controlled vault | | Acknowledgement / id | document_id, customer_id, hash, notice_version, accepted_at | Lender | | Signature / id | offer_id, provider_ref, status, signed_document_ref | Lender | | Mandate / id | bank_account_id, provider_ref, max_amount, frequency, status | Lender | | Disbursement / id | offer_id, unique_instruction_ref, net_amount, provider_ref, status | Lender | | Loan / id | customer_id, lender_loan_ref, offer_id, status, disbursed_at | Lender; scoped servicing mirror | | Installment / id | loan_id, sequence, due_date, principal, interest, amount_due, settled_amount | Lender schedule/ledger | | Payment / id | loan_id, installment_id, intent_ref, provider_ref, amount, status | Lender | | Allocation / id | payment_id, installment_id, amount, lender_ledger_ref | Lender; unique ledger posting reference | | Ticket / id | customer_ref, category, received_at, status, assignee, response_ref | Approved service scope; avoid raw KYC attachments | | PrivacyRequest / id | customer_ref, purpose, requested_at, retention_decision, completed_at | Lender decision; routed LSP reference | | ProviderEvent / id | provider, event_id, raw_hash, signature_result, processing_status | Lender inbox; unique provider/event pair | | AuditEvent / id | actor, role, resource_ref, action, reason, request_id, occurred_at | Append-only, access-controlled | Relationships: Customer 1:N Application; Application 1:N Verification/Proposal/Offer; Offer 1:N Documents and 0:1 settled Loan; Loan 1:N Installments/Payments; Payment 1:N Allocations. Support ticket references a customer and optional application/loan, without duplicating sensitive evidence. Constraints: one active application/loan per borrower according to policy; one accepted current offer; unique payout instruction per offer; unique provider event; unique lender-ledger posting reference. Enforce financial and version constraints in the database, not only form validation. Maintain current status projections separately from immutable events. ## 5. API conventions and draft contract `api-contract.json` is an OpenAPI 3.1 draft describing the principal borrower/admin resources. It is a starting contract, not a deployed endpoint set or a complete vendor specification. Its example server uses the reserved `.invalid` domain. Application authentication uses bearer tokens; workforce tokens require a separate audience and role/tenant verification. Amounts are integer INR paise, never formatted strings. IDs are opaque. List responses use bounded cursor pagination. All state-changing business requests carry `Idempotency-Key`; sensitive commands require an expected resource version. Provider callbacks use provider-specific signatures over the raw body with replay checks and are specified separately from bearer authentication. Representative operations include OTP challenge/verification, draft creation/update/submission, consent events, verification sessions, current offers, offer acceptance, contracting sessions, application status, loan/schedule retrieval, payment intents, exit quotes, tickets, privacy requests, admin proposals/confirmations and reconciliation enquiries. The contract labels actor scopes and includes conflict, auth, validation and throttling errors. ```json { "error": { "code": "OFFER_EXPIRED", "message": "This offer has expired. Request a refreshed offer.", "requestId": "opaque-correlation-id" } } ``` Error classes: `400` malformed request; `401` invalid authentication; `403` resource/role denied; `404` absent or deliberately concealed resource; `409` version/idempotency/state conflict; `422` business validation; `429` throttled; `503` temporary dependency unavailability. Never expose stack traces, bureau details or provider secrets in errors. ## 6. Critical transactional sequences ### Decision confirmation Verify workforce identity, checker scope, lender scope and `checker_user_id != maker_user_id`. In one transaction lock the application, compare expected version and proposal status, verify current evidence/policy and commit decision, new version, audit event and outbox event. A repeated idempotency key with the same body returns the prior result; a changed body returns conflict. A stale proposal cannot be confirmed after borrower data changes. ### Offer acceptance and contracting The server checks borrower ownership, current offer, expiry, required acknowledgement/document hash and application validity atomically. Persist acceptance and contracting request before contacting providers through a worker. Signature and mandate are separate records. Revalidate the final disbursement gates against the accepted offer and verified provider results. A button tap or provider redirect is never a trusted completion event. ### Financial instruction and webhook processing Create a unique instruction and outbox message in a transaction. Worker sends the instruction with a stable provider reference; persist attempts. A timeout becomes UNKNOWN and triggers enquiry against that reference. Verified events enter a durable inbox with unique provider/event ID before asynchronous processing. Acknowledge successful receipt only after durable persistence; repeated valid deliveries return success without duplicate effects. Check monotonic state rules, amount, currency and reference; send conflicting events for investigation. After provider confirmation, reconcile with lender settlement/ledger evidence. Post one allocation or store the authoritative lender posting reference, then update the servicing projection and emit the receipt notification. Reversal is explicit and compensating. A failed callback arriving after settlement cannot erase a paid status. Network retries must never invent a new payout reference. ### Privacy and document handling Validate requester, purpose and lender scope. Route privacy changes to the lender and provide a request reference. A deletion request creates a case; retention decisions are recorded per data category. Do not promise immediate erasure of loan records. Documents use hash/version binding and short-lived, scope-limited retrieval; no public URLs or KYC attachments in support email. ## 7. Security and deployment Use TLS for clients, mTLS or equivalent service authentication where supported, secret storage with rotation and separate development/test/production credentials. Workforce MFA and least privilege are mandatory proposed controls. Validate ownership for every resource, prevent object-level authorization bypass, sanitize uploads inside lender-approved flows and redact PII/tokens from analytics and logs. Mobile tokens belong in platform-protected storage; clear sessions on logout and support session revocation. Host approved data stores, backups, logs, observability and document processing in contracted India locations. Review telemetry vendors and support access as part of the same boundary. Infrastructure should use separate accounts/projects, private databases, restricted egress, encrypted backups and audited privileged access. Keep production customer data out of development environments. The prototype stores no real records and has no security boundary. Do not deploy it as a borrower-facing loan application. Its role picker and rail are review controls. ## 8. Operational targets and observability Proposed pilot targets, subject to capacity and lender agreements: 99.5% monthly availability for owned non-provider services; p95 under 700ms for simple owned API reads at 50 requests/second; no synchronous provider latency promise. Load-test the agreed traffic model before accepting these targets. Initial recovery objectives: RPO 15 minutes and RTO 4 hours, proven by restore exercises and reconciled against lender financial records. Track application conversion by step without raw PII, provider success/latency, stale reviews, expiring offers, unknown payout age, unreconciled receipts, duplicate callbacks, queue lag, complaint age and notification failures. Alert on financial mismatches and persistent unknowns; identify an on-call owner and runbook for each. Dashboard reporting must distinguish counts of attempts, unique borrowers, approved applications and settled loans. Runbooks: provider outage pauses new affected steps while preserving drafts; unknown payout opens enquiry and blocks duplicate issue; settlement mismatch goes to authorized operations; compromised credential triggers revocation, investigation and applicable reporting; restore test replays only deduplicated durable events and checks lender ledger consistency. ## 9. Implementation phases and verification | Phase | Deliverable | Exit evidence | | --- | --- | --- | | Foundation | Identity, scopes, consent, draft persistence, schema, audit | Ownership/role tests and consent evidence review | | Assessment | KYC/income/bureau adapters, requests, maker/checker | Provider sandbox results; self-approval/version-race tests | | Contracting | Pricing, KFS, acknowledgement, e-sign, mandate | Golden lender fixtures; expiry/hash/callback ordering tests | | Servicing | Payout, schedule mirror, payment, reconciliation | Timeout/duplicate/reversal/partial-payment scenarios | | Operations | Complaints, privacy, reporting, controlled exports | Role-scope UAT, retention and grievance exercises | | Pilot | Hardened native/admin build, monitoring and runbooks | Pen test fixes, restore drill, lender signoff, rollback rehearsal | Use unit tests for pricing/state guards, contract tests for each adapter, integration tests for transaction/idempotency behavior and end-to-end tests for borrower and operator journeys. Include concurrent checker approval, cross-customer access, revoked consent, expired KFS, provider callback replay and orphaned payment recovery. A passing UI test cannot demonstrate regulatory compliance or real bank settlement. For the delivered wireframe package, verify JavaScript syntax, screen render/navigation, calculator sensitivity, consent and acknowledgement guards, role-based proposal confirmation, linked application status, settlement deduplication, local document links and narrow viewport layout. Production criteria above remain implementation work. ## 10. Dependencies and decision register Before engineering estimates: choose the lender/LOS/LMS and clarify APIs, identity/KYC route, bureau permissions, income provider, signing, mandate, bank rails, notification provider and service-level agreements. Obtain approved fee/tax fixtures, actual KFS/agreement templates, credit-policy versions, complaint ownership and a record-retention schedule. Confirm which domain may store each field. The API contract intentionally uses generic verification/contracting adapters until vendor selection. Vendor signature algorithms, callback schemas, request credentials, bank settlement formats and detailed decision engine rules cannot be finalized without those agreements. No hidden real providers or credentials are included. Regulatory basis and remaining legal reviews are in the functional specification. This document is an engineering proposal and does not certify licensing or compliance.