Fields catalog
Every borrower-provided input in the proposed flow. Grouped by surface. Doc uploads are modeled as logical fields, not one fixed database column per file slot.
Lead qualification
Anonymous calculator plus contact capture. The live form shows mortgage pre-approval immediately after the property-found question.
| # | Field ID | Label | Type | Required | Validation | Source form / step | Supabase column (proposed) | Notes |
|---|
Dashboard profile questions
21 logical questions after OTP. The live dashboard already carries service selection and co-applicant gating state; this page treats them as contract fields.
| # | Field ID | Label | Type | Required | Validation | Source form / step | Supabase column (proposed) | Notes |
|---|
Co-applicant branch
Only unlocked when `has_co_applicant = yes`. The current dashboard copy already reflects this branch.
| # | Field ID | Label | Type | Required | Validation | Source form / step | Supabase column (proposed) | Notes |
|---|
Docs + final submission
Three borrower answers plus logical upload fields. Bank statements and identity documents stay grouped here because storage is row-based, not fixed-column.
| # | Field ID | Label | Type | Required | Validation | Source form / step | Supabase column (proposed) | Notes |
|---|
Computed fields
Derived outputs the frontend can show immediately or the backend can persist. This table intentionally mixes display math, policy booleans, and score components because they all shape the API contract.
| Field name | Formula / source | Depends on | Used in | Notes |
|---|
Closing-cost math comes from the canonical Dubai closing-cost formula. The dashboard can keep it client-side now and move to an engine call later without changing field names.
`eligibility-gates.json` also defines G11-G13. This page keeps G1-G10 as requested and flags the extra gates as post-contract extensions.
Category J (Sector Tier, weight 10%) is now modelled. Source: employer_name from F2 → sector lookup against the underwriting-engine employer taxonomy (~25 named employers in v0.1.1, expandable to ~370 via APP-1844). T4/T5 sectors score 0 — feeds the auto-decline pathway in addition to gate G7 employment-tenure logic.
Relationships
Three diagrams capture the contract shape: journey flow, prefill chain, and doc-extraction surface.
Data flow: F1 → dashboard → engine
One borrower row. Progressive enrichment. Documents update the same record and trigger the decision engine.
contact + property + residency + income"] --> H["URL params +
create buyer_lead row"] H --> D["Dashboard reads buyer_lead
and shows cost breakdown"] D --> F2["Form 2 updates same row
21 inputs + service selection"] F2 --> G{"has_co_applicant?"} G -->|yes| F25["Form 2.5 writes co_app_* fields"] G -->|no| F3["Form 3 starts"] F25 --> F3 F3 --> DOCS["Supabase Storage + buyer_document rows"] DOCS --> OCR["OCR + bank-statement parsing
fills extracted fields"] OCR --> ENG["Underwriting engine call
indicative_scs + fee_tier_id + decision"] ENG --> SAVE["Update buyer_lead
decision fields + gates_failed"]
Prefill chain: F1 → F2 → F2.5
Ask once. Display later. Re-verify only the phone.
| F1 source field | F2 / F2.5 destination | Treatment |
|---|---|---|
fullName | F2 contact_name | Display only |
emailInput | F2 contact_email | Display only |
phoneInput | F2 phone_otp_verified | Re-verify via OTP |
heroValue | F2 property_budget | Display only |
areaMulti | F2 property_area | Display only |
propertyType | F2 property_type | Editable |
bedrooms | F2 bedrooms | Editable |
residency | F2 residency_status | Editable |
incomeInput + commissionInput | F2 household_income_total + variable_income | Split editable |
householdIncomeInput + partnerCommInput | F2 has_co_applicant = yes | Sets gate ON |
| (when has_co_applicant = yes) | F2.5 co_app_monthly_income + co_app_variable_income | Prefilled from F1 partner inputs |
Doc-to-field extraction map
File uploads do not just unlock the next step. They replace manual questions and finalize gates.
| Document upload | Fields it populates |
|---|---|
| Emirates ID | DoB · Nationality · EID number |
| Passport | Romanized name · Nationality · Passport number · Expiry |
| Visa page | Residency duration · Visa type · Sponsor |
| Bank statements | Variable income patterns · Rent payment · Savings buffer · Recurring debits |
| AECB report | AECB score · Credit history · Existing debts · Defaults |
| Salary certificate | Employer · Salary · Tenure · Allowances |
| Employment letter | Role · Tenure confirmation |
| Trade license | Business name · License number · Years in business |
| Audited financials | Business revenue · Business profit |
Doc extraction
OCR and parser requirements by tile. Keep the manual-review path first-class because AECB PDFs and self-employed financials will not be clean enough every time.
| Doc tile | Required if | Extracted fields | OCR vendor (proposed) | Confidence threshold | Manual review fallback |
|---|
Supabase schema
Three-table proposal. `buyer_lead` remains the orchestration spine. `buyer_document` becomes extraction-aware. `buyer_journey` gives the CTO an explicit audit trail instead of inference from timestamps.
`buyer_lead`
Extended from the current lead row. This is the single API record the dashboard and engine mutate.
| Column | Type | Default | Constraints | Source | Notes |
|---|
`buyer_document`
Existing document row with extraction metadata added. One file upload = one row.
| Column | Type | Default | Constraints | Source | Notes |
|---|
`buyer_journey`
New audit trail table for progress, abandonment, and replay. Useful for CRM routing, analytics, and support.
| Column | Type | Default | Constraints | Source | Notes |
|---|
buyer_lead is mostly ALTER. buyer_document adds extraction columns. buyer_journey is net-new.
buyer_lead(session_id) unique, buyer_lead(status, updated_at desc), buyer_lead(decision, decision_at desc), buyer_document(buyer_id, doc_kind, uploaded_at desc), buyer_journey(buyer_id, step, entered_at desc).
Use jsonb only for extracted document payloads and journey snapshots. Everything queryable in ops or underwriting stays first-class.