Fahiq — one system for every room, tenant and rupee
A property owner running several buildings and PGs lists rooms with photos, rent and amenities on a public site; prospects enquire and get onboarded digitally; the platform then generates every monthly bill and chases it over WhatsApp with a UPI payment link that reconciles itself.
Overview
Today the business runs on WhatsApp chats, a notebook and manual UPI follow-ups. There is no single view of vacancies across properties, utility splits are disputed, tenant records and agreements live nowhere, and collections slip because chasing is manual. Fahiq replaces that with one operating system for the entire tenant lifecycle: list → market → enquire → onboard → bill → collect → maintain → renew → move-out — with routine communication fully automated over WhatsApp.
Goals
- Zero-effort billing cycle — invoices auto-generate on the due day; reminders and payment links go out on a schedule; payments auto-reconcile.
- One source of truth for properties, rooms, tenants, agreements, documents and money.
- Public storefront so vacancies fill without depending only on brokers.
- Paperless onboarding — application, KYC, agreement e-sign, inventory, opening meter readings.
- Owner clarity — occupancy %, expected vs collected rent, overdue aging, per-property P&L.
Out of scope for v1
- Multi-owner SaaS with self-serve signup and per-owner billing — deferred; the data model is namespaced by
organizationto allow it later. - Full accounting-suite replacement — Fahiq integrates with / exports to Tally & Zoho instead.
- Short-stay / nightly booking engine — later phase.
Who uses it
Runs the business
Occupancy, cash position, per-property P&L, approvals — sees everything.
Day-to-day ops
Leads, onboarding, move-outs, tickets, meter readings for assigned properties.
Money in / out
Invoicing, reconciliation, expenses, refunds, payouts, reports.
On the ground
Assigned tickets, meter-reading capture, inventory photos.
Browsing the site
Find a room in budget and area, see photos & rules, enquire, book.
Living in a unit
See dues, pay, download receipts & agreement, raise complaints, read notices.
Success metrics
- Collection efficiency (collected ÷ billed) ≥ 95% by day 10 of the month.
- Average days-to-collect after the due date — trending down.
- Average vacancy days per unit turnover — trending down.
- % invoices sent automatically (no manual touch) — target 100%.
- % onboardings fully digital (agreement e-signed, KYC uploaded) — target ≥ 90%.
- Manual hours / month on billing & reminders — target near zero.
Product workflow
The tenant lifecycle, end to end. Every stage below is a real screen or automated job in the platform.
Flow A — Enquiry to conversion
- Enquiry. Visitor submits the form on a listing (
POST /public/enquiries). A Lead is created withsource = website_enquiry. - Acknowledge. The
enquiry_ackWhatsApp template fires immediately; a "new lead" alert reaches the assigned manager. - Qualify. Manager works the pipeline:
New → Contacted → Visit scheduled → Visited → Negotiating → Won / Lost, logging calls and notes. - Visit. A slot is booked;
visit_confirmednow andvisit_remindertwo hours before, to prospect and staff. - Convert. One click turns the won lead into an onboarding, carrying name, phone and the unit of interest.
Flow B — Digital onboarding
- Application. Personal details, occupation, co-occupants, emergency contact, vehicle — captured in a resumable wizard (or self-filled by the prospect via a link).
- KYC. Aadhaar / PAN / DL / passport + photo uploaded; each document marked
pending → verified → rejectedwith a note. ID numbers stored masked and encrypted. - Terms. Structured lease terms: start & end date, rent, deposit, lock-in, notice period, escalation %, due day, utilities & parking included.
- Agreement. Generated from a template with merge fields → e-signed (Digio / Leegality / Aadhaar eSign) or a signed scan uploaded; stamp / e-stamp details recorded.
- Inventory & meters. Move-in inspection with condition photos; opening electricity / water / gas readings with meter photos.
- Activate. Unit / bed assigned, status flips to
Occupied, the rent schedule starts, and theonboarding_welcomeWhatsApp goes out with house rules, Wi-Fi password, manager contact and payment instructions.
Flow C — Monthly billing run automated
Celery Beat job billing.run_daily_invoicing, 06:00 IST daily. Idempotent per (tenancy, period), guarded by a Redis lock.
- Select. Every active tenancy whose
due_dayis today (optionally minus lead days). - Rent line. Current post-escalation rent; prorated if the tenancy started mid-period.
- Recurring charges. One line each — Wi-Fi, parking, maintenance, mess — prorated where applicable.
- Electricity. Per unit mode: sub-meter =
(current − previous) × rate + fixedfrom the period's reading; fixed = flat amount; shared = parent bill split equally / by headcount / by ratio. No reading → electricity is skipped, the invoice is flaggedelectricity_pendingand staff are alerted. - Water. Fixed, per head, or metered — analogous.
- Tax. If GST is enabled, apply the rate to taxable lines and use the tax-invoice format.
- Totals & number. Compute subtotal, discount, tax, total; assign the next gap-free invoice number in the financial-year series.
- Render & post. Branded PDF generated; a rent-account ledger entry written; status set to
issued; theinvoice_issued(or itemized) WhatsApp template queued with the PDF and payment link.
Flow D — Collection & dunning automated
Job collections.run_reminders, 08:00 IST daily. Stops automatically the moment an invoice balance hits zero.
| Offset | Template | Tone | Channel |
|---|---|---|---|
| T−3 | rent_reminder_pre | Friendly heads-up | |
| T0 | rent_due_today | Neutral | |
| T+1 | rent_overdue_1 | Reminder | |
| T+3 | rent_overdue_2 | Firm | |
| T+7 | rent_overdue_3 | Firm + late-fee warning | |
| T+15 | rent_overdue_final | Escalation + manager alert | WhatsApp + SMS |
The engine skips a send when the tenant has opted out, when the clock is inside quiet hours (queued for morning), or when a promise-to-pay date is still in the future. Every attempt is written to a ReminderLog so a given stage is never sent twice.
Flow E — Payment & reconciliation automated
- Link.
POST /payments/linkcreates a Razorpay Payment Link for the invoice balance; the URL is what the reminder templates carry. - Pay. Tenant pays by UPI, card, netbanking or wallet — or an approved UPI Autopay mandate auto-debits on the due day.
- Webhook.
payment_link.paid/payment.capturedarrives; signature verified; deduped on the Razorpay event id. - Allocate. A
Payment+ allocations are written (oldest invoice first if overpaid); invoice balance & status update; a credit ledger entry is written. - Receipt. A branded receipt PDF is generated, the
payment_receivedtemplate is sent with it attached, and any pending reminders for that invoice are cancelled.
Flow F — Maintenance ticket
- Tenant raises a ticket from the portal or by texting
TICKET <text>on WhatsApp — category, description, photos, priority. - Workflow:
Open → Assigned → In progress → On hold → Resolved → Closed, with an SLA timer per priority and a tenant-visible thread separate from internal notes. - Assigned to staff or a vendor; cost recorded; if it is tenant-caused damage, a charge is auto-added to the next invoice.
- Status changes push a WhatsApp update; on resolution the tenant rates the fix.
Flow G — Renewal & move-out
- Renewal. Reminders at 60 / 30 / 7 days before agreement expiry; capture new terms; regenerate & re-sign; the rent schedule extends.
- Notice. Tenant (portal / WhatsApp) or owner records notice; the system computes the last day from the notice period and lock-in;
notice_ackis sent. - Settlement. Inventory re-check with damage deductions, final meter readings & utility bill, unpaid dues — the deposit refund is computed and a settlement statement generated.
- Close. On tenant acknowledgement the refund pays out (Razorpay Payout or manual); the unit returns to
Available(orUnder maintenancewith a turnover task) and re-lists.
Functional requirements
Seventeen modules. Every capability is tagged with the phase it lands in.
| Module | Key capabilities | Phases |
|---|---|---|
| A · Property & room management | Multiple properties with geo-location & photos; Property → Block → Floor → Unit → Bed hierarchy; unit types 1RK / Studio / 1–4BHK+ / PG bed (single–dormitory) / commercial; furnishing, area, commercials (rent, deposit, maintenance, lock-in, notice, escalation, due day); photo & video galleries, floor plans, 360° tour; property- and unit-level amenity checklist (~40 items); status available / booked / occupied / notice / maintenance / blocked + available-from; tenant-type & house rules; nearby landmarks; rent-revision history; CSV import & bulk edit; clone unit. |
P1 P2 P3 |
| B · Public website | Landing + search with filters (locality, budget, type, furnishing, availability, amenities, tenant type, sharing); listing cards + detail page (gallery, rent breakdown, rules, map, nearby, similar); enquiry & schedule-a-visit forms; WhatsApp click-to-chat; book-now token payment; shortlist; verified reviews; per-locality SEO pages, sitemap, JSON-LD; CMS-lite content; English / Hindi; PWA. | P1 P2 P3 |
| C · Lead / enquiry CRM | Capture from website, WhatsApp, call, walk-in, broker, referral; pipeline stages with lost-reason; assignment, follow-up reminders, activity timeline; auto acknowledgement; visit scheduling with calendar; convert-to-onboarding; duplicate detection; source & funnel reporting; cold-lead drip. | P1 P2 P3 |
| D · Tenant onboarding | Digital application + co-occupants; KYC upload & verification; rental-agreement generation + e-sign; police / tenant-verification form; structured lease terms; move-in inventory with photos; opening meter readings; unit / bed assignment + welcome message; guarantor & co-tenant records; self-onboarding link. | P1 P2 P3 |
| E · Tenancy / lease lifecycle | Active-tenancy register with full profile; automatic monthly rent schedule; anniversary rent escalation; renewal workflow + expiry reminders; notice-to-vacate with computed last day; move-out settlement; transfer between units; tenancy history; blacklist; corporate lease. | P1 P2 P3 |
| F · Billing & invoicing | Charge types (rent, deposit, electricity, water, maintenance, wifi, mess, housekeeping, parking, gas, late fee, damage, one-time, custom); per-tenancy recurring charges; electricity by sub-meter / fixed / shared split; water fixed / per-head / metered; automatic monthly batch + ad-hoc invoices; proration; auto late fees with cap; branded invoice PDF; credit notes / waivers with approval; partial payments & allocation rules; tenant ledger & statement; aging buckets; separate deposit ledger; gap-free numbering; GST tax invoice; TDS-on-rent note. | P1 P2 P3 |
| G · Payments & collections | Razorpay Payment Links per invoice; webhook auto-reconciliation; receipts; offline payment entry + approval; UPI Autopay / e-mandate; configurable reminder cadence with escalation; collection dashboard; promise-to-pay; deposit refund via Payout; owner payout tracking; convenience-fee pass-through; BBPS bill pay. | P1 P2 P3 P4 |
| H · WhatsApp automation | BSP adapter with swappable provider; ~35 approved templates; variable merge; daily scheduler; two-way inbound with keyword auto-replies; broadcasts to segments; delivery tracking with SMS / email fallback; opt-out, quiet hours, per-message cost log; template-management UI; AI concierge. | P1 P2 P3 P4 |
| I · Maintenance & tickets | Tenant-raised tickets with photos & priority; assignment to staff / vendors; SLA timers & escalation; internal vs tenant-visible thread; vendor directory & rate cards; recharge tenant-fault costs; preventive-maintenance schedules; asset / appliance register; common-area tickets; WhatsApp status updates & ratings. | P1 P2 P3 |
| J · Staff / team | Role-based staff accounts; property-scoped access; audit trail; task management & checklists; field-staff check-in; internal notes & mentions. | P1 P2 P3 |
| K · Accounting & finance | Auto income capture; categorized expenses with receipts, per property; per-property P&L with yield; cash-flow statement; owner drawings; exports to Tally / Zoho; GST output report; e-invoicing; expense-bill OCR. | P2 P3 P4 |
| L · Reports & analytics | Rent roll; occupancy; collections; aging; upcoming vacancies; expiring agreements; utility-consumption trends; lead funnel & source performance; vacancy days; churn / retention; maintenance cost; custom date ranges + PDF / Excel export; scheduled email reports. | P1 P2 P3 |
| M · Tenant self-service portal | Phone-OTP login; dues & one-tap pay; invoices, receipts, ledger / statement; agreement & KYC status; raise / track tickets; submit meter reading; announcements feed; move-out request; profile edits; PG roommate & mess info; refer-a-friend; contact manager. | P1 P2 P3 |
| N · Notifications (multi-channel) | WhatsApp, email, SMS, in-app; per-event templates; per-user channel preference & opt-out; staff alerts (new lead, payment, ticket, send failure, notice given, agreement expiring, missing meter reading, low occupancy); daily digest option. | P1 P2 |
| O · Documents & templates | Central document vault per tenant / unit / property; generated invoice, receipt & ledger PDFs; merge-field templates (agreement, police verification, rent receipt, notice letter, settlement statement, NOC); template versioning; signed-URL access & expiry tracking; watermarking. | P1 P2 P3 |
| P · Configuration / settings | Business profile & branding; master data (properties, amenities, charge types & rates); billing policy (grace, late fee, numbering, financial year); electricity slabs & water charges; integration credentials with test buttons; reminder-cadence editor; users / roles / property assignment; localization; public-site CMS; agreement clause library. | P1 P2 P3 |
| Q · Advanced / future | React Native apps (tenant + field); smart locks & visitor management; IoT smart-meter auto-read; Aadhaar / DigiLocker KYC; channel manager (NoBroker, MagicBricks, 99acres, Housing, OLX); dynamic pricing; mess & laundry management; referral & loyalty; AI (chatbot, default-risk scoring, ticket triage, OCR); renter's insurance; BBPS; short-stay mode; multi-owner SaaS. | P4 |
Non-functional requirements
Security
- Authentication. Staff: email + password (Argon2), mandatory TOTP 2FA for Owner/Admin & Accountant, lockout after 5 failed attempts. Tenant: phone + 6-digit OTP, 5-minute TTL, rate-limited, single-use. Short-lived access JWT (15 min) + rotating refresh token with reuse detection.
- Authorization. RBAC plus object-scoping by property, enforced at the queryset layer. Deny by default. Sensitive changes route through an approval workflow; waivers & discounts are amount-limited per user.
- Data protection. TLS 1.2+ with HSTS; DB volume encryption; field-level encryption (Fernet / KMS) for integration credentials and KYC ID numbers; KYC files in a separate stricter bucket with 2–5 minute signed URLs. PII masked in logs and Sentry.
- Payments. Card / UPI data never touches our servers — Razorpay Checkout & hosted links only; we hold tokens and references, keeping PCI scope at SAQ-A.
- Webhooks. Signature verified (Razorpay, BSP), idempotent processing, raw payload stored for replay, IP allowlist where offered.
- Hardening. CSRF on session routes, locked CORS, strict CSP on the public site, security headers, dependency & secret scanning in CI, throttling on auth / OTP / enquiry, bot protection on public forms, upload type / size allowlist with image re-encoding and AV scan.
- Audit. Every money / status / agreement / KYC / permission change writes an immutable before→after log entry with actor, IP and time.
Compliance (India)
- DPDP Act 2023 — explicit consent capture (WhatsApp opt-in, KYC purpose), consent records, purpose limitation, retention schedule, data-subject access / erasure handling, breach process, named grievance officer.
- WhatsApp / Meta policy — approved templates only for business-initiated messages, correct utility vs marketing categorization, opt-out strictly honoured.
- GST (if registered) — compliant tax-invoice format, sequential numbering, retention.
- Retention defaults — financial records 8 years; KYC per statutory need; message bodies 18 months (metadata longer); server logs 90 days.
Performance & scalability
- API p95 < 300 ms for list / detail endpoints at target scale (≤ 2,000 units, ≤ 5,000 active tenancies, ≤ 50 staff).
- Public listing pages: LCP < 2.5 s on mid-tier 4G, INP < 200 ms, CLS < 0.1.
- Pagination everywhere; no unbounded queries;
select_related/prefetch_related; indexes on the hot paths. - Redis caching for public listing & facet queries, dashboard KPI aggregates and report results; media on a CDN with content-hash URLs and AVIF / WebP.
- Billing run is chunked, resumable and idempotent; a bad tenancy fails alone, never the batch. Separate Celery queues (
default,billing,messaging,pdf,exports). - Stateless API containers scale horizontally; exports & broadcasts are background jobs with progress.
Reliability & availability
- Target 99.5% monthly app availability; billing & reminder jobs alert on a missed or late run ("last successful run" freshness monitor).
- Daily Postgres snapshots + PITR (WAL), ≥ 14-day retention, quarterly restore drills — RPO ≤ 5 min with PITR, RTO ≤ 4 h.
- All scheduled jobs idempotent and Redis-locked; safe to re-run.
- Webhook processing at-least-once with dedupe, dead-letter queue, backoff and a manual replay tool.
- Outbound sends retry then fall back WhatsApp → SMS → email; staff are alerted if a tenant is unreachable on every channel.
- If the BSP or Razorpay is down, invoices still generate and queue, flushing when the provider recovers.
- Zero-downtime deploys; backward-compatible migrations (expand → migrate → contract).
Data integrity
- Invoices immutable after issue — the only permitted post-issue mutation is an audited late-fee line; other corrections are credit notes.
- Ledgers append-only; balances derived and materialized, with a nightly reconciliation check that alerts on drift.
- Gap-free invoice / receipt numbering via a DB sequence + unique constraint per financial year.
- Soft-delete for anything with financial history; every money mutation inside a DB transaction;
Decimalarithmetic with documented rounding.
Observability
- Structured JSON logs with request / task / tenancy correlation ids; Sentry on API, workers and both frontends with PII scrubbing.
- Business metrics: invoices generated, messages sent / delivered / failed, reminder queue depth, payments reconciled, reconciliation lag, webhook latency, Celery queue lengths, billing-run duration.
- Paging alerts: billing run failed / late, webhook backlog, message failure rate, reconciliation drift, backup failed, error-rate spike.
- External uptime monitoring on the public site, the API and job freshness.
Accessibility & UX quality
- WCAG 2.1 AA on the public site and tenant portal — semantic HTML, labelled controls, keyboard navigation, visible focus, AA contrast, alt text, reduced-motion support.
- Responsive: site & portal mobile-first; dashboard usable on tablet; field flows (meter reading, ticket update) usable on a phone.
- Friendly error copy mapped from the RFC-7807
detail; empty states name the next action; destructive actions confirm with a reason. - Localization-ready — INR, Asia/Kolkata,
dd MMM yyyy, English / Hindi.
Roles & permissions
Two populations: staff users in the dashboard (role-based, property-scoped) and tenant users in the portal (scoped to their own tenancy). Public visitors are anonymous.
| Capability | Owner | Manager | Accountant | Field | Front desk | Read-only |
|---|---|---|---|---|---|---|
| See all properties | ✅ | assigned | assigned/all | assigned | assigned | assigned/all |
| Create / edit properties & units | ✅ | ✅ | — | — | — | — |
| Manage leads & visits | ✅ | ✅ | — | — | ✅ | view |
| Onboard a tenant / verify KYC | ✅ | ✅ | — | — | onboard | — |
| Generate agreement & trigger e-sign | ✅ | ✅ | — | — | — | — |
| Edit tenancy terms | ✅ | approval | — | — | — | — |
| Enter meter readings | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Generate invoices | ✅ | ✅ | ✅ | — | — | — |
| Discounts / credit notes / waivers | ✅ | ≤ limit | ≤ limit | — | — | — |
| Record / approve offline payments | ✅ / ✅ | record | ✅ / ✅ | — | record | — |
| Issue refunds / payouts | ✅ | — | ✅ | — | — | — |
| Edit reminder cadence / templates | ✅ | — | — | — | — | — |
| Send broadcasts | ✅ | own property | — | — | — | — |
| Manage maintenance tickets | ✅ | ✅ | — | assigned | create | view |
| Record expenses | ✅ | ✅ | ✅ | — | — | — |
| View financial reports & P&L | ✅ | own property | ✅ | — | — | assigned/all |
| Manage users & roles | ✅ | — | — | — | — | — |
| Edit integration credentials & settings | ✅ | — | — | — | — | — |
approval = the action creates a pending change-request the Admin approves. ≤ limit = allowed up to a per-user rupee cap, above which it needs approval. Property scoping is filtered in the queryset, not just the view.
Authentication by population
| Population | Method |
|---|---|
| Staff | Email + password; mandatory TOTP 2FA for Owner/Admin & Accountant; JWT session |
| Tenant | Phone + OTP (SMS, WhatsApp secondary); optional device PIN |
| Public visitor | None; optional email magic-link only to sync a shortlist |
| Service / webhooks | Signed webhook secrets; IP allowlist where supported |
Tenant portal scope
A tenant only ever sees data where tenancy.tenant == request.user.tenant (or they are a listed co-tenant): their own invoices, receipts, ledger, agreement, KYC status, tickets and property announcements. They can pay their own invoices, raise / rate their own tickets, submit their own meter readings (if enabled), initiate a move-out, and edit contact fields — KYC edits go to a review queue. They cannot see other tenants, other units, staff notes or any financial totals beyond their own.
System architecture
A Django + DRF API with a Celery worker and Beat scheduler; PostgreSQL as the system of record; Redis for cache, locks and the Celery broker; S3 / R2 for media and generated PDFs. Three React frontends talk to the same API.
┌──────────────────────── Clients ────────────────────────┐ Public visitor ─────► Public Website (Next.js · SSR/SSG for SEO) Tenant ─────► Tenant Portal (React + Vite SPA) Staff / Owner ─────► Admin Dashboard (React + Vite SPA) └────────────────────────────┬───────────────────────────┘ │ HTTPS / JSON (REST, /api/v1) ▼ ┌──────────── Django + Django REST Framework ────────────┐ │ Auth · RBAC · domain services · serializers · OpenAPI │ └──┬───────────────────┬────────────────────┬───────────┘ │ │ │ ┌──────▼─────┐ ┌──────▼──────┐ ┌──────▼────────┐ │ PostgreSQL │ │ Redis │ │ Object storage│ │ (records) │ │ cache+broker│ │ S3 / R2 │ └────────────┘ └──────┬──────┘ └───────────────┘ │ ┌───────────▼────────────┐ │ Celery workers │ │ Celery Beat (cron) │ │ billing · reminders │ │ escalations · PDFs │ │ webhook retries │ └───────────┬────────────┘ │ outbound ┌──────────────┬──────────────┬──────────────┼───────────────┬───────────────┐ ▼ ▼ ▼ ▼ ▼ ▼ WhatsApp BSP Razorpay SMS gateway Email (SES/ e-sign Maps / (AiSensy/…) (Links·Autopay· (MSG91/Twilio) SendGrid/SMTP) (Digio/ Sentry Payouts·webhook) Leegality) Inbound webhooks → Django receivers → verified, deduped, queued for async processing.
Components
| Component | Tech | Responsibility |
|---|---|---|
| API service | Django 5 · DRF · Gunicorn/Uvicorn | Business logic, auth, REST API, webhook receivers, admin |
| Worker | Celery | Async & scheduled jobs — billing, reminders, PDFs, notifications, webhook processing, exports |
| Scheduler | Celery Beat (DB schedule) | Fires the daily & periodic jobs |
| Admin dashboard | React + Vite + TS | Staff UI |
| Public website | Next.js (App Router) | Marketing, listings, enquiry / booking — SSR/SSG for SEO |
| Tenant portal | React + Vite + TS | Tenant self-service (shares the design system with the dashboard) |
| Database | PostgreSQL 16 (managed) | System of record |
| Redis | Managed Redis | Cache, Celery broker + result backend, rate-limit counters, OTP store, job locks |
| Object storage | S3 / Cloudflare R2 | Photos, video, generated PDFs, KYC docs — private buckets, signed URLs |
Scheduled jobs (Celery Beat)
| Job | Schedule (IST) | Purpose |
|---|---|---|
billing.run_daily_invoicing | 06:00 daily | Build & issue invoices for tenancies due today; render PDF; queue the invoice message. Idempotent per (tenancy, period). |
collections.run_reminders | 08:00 daily | Evaluate the cadence for every unpaid invoice and send the due reminder with its payment link; skip if paid / opted-out / quiet hours / promised. |
billing.apply_late_fees | 09:00 daily | Add a late-fee line once per invoice after the grace period. |
tenancies.escalate_rent | 02:00 daily | Apply rent escalation on tenancies hitting their anniversary. |
tenancies.expiry_reminders | 07:00 daily | 60 / 30 / 7-day agreement-expiry reminders + staff alert. |
meters.reading_reminders | 10:00 daily | Alert staff about units missing a current-month reading before billing. |
maintenance.preventive_due | 07:30 daily | Create / notify preventive-maintenance tasks that are due. |
payments.reconcile_pending | every 30 min | Poll Razorpay for links still "created" past the webhook grace; sync status. |
messaging.retry_failed | every 15 min | Retry failed sends with backoff; fall back channel after N tries. |
reports.email_digests | Mon 08:00 / 1st 08:00 | Weekly collections + monthly P&L email to the owner. |
common.nightly_backup_check | 01:00 daily | Verify the last DB + media backup succeeded; alert if not. |
Integrations
| Integration | Purpose | Direction |
|---|---|---|
| WhatsApp BSP | Send template messages; receive delivery status & inbound replies | out + webhook |
| Razorpay | Payment Links, UPI Autopay mandates, Payouts, webhooks | out + webhook |
| SMS (MSG91 / Twilio) | OTP + fallback notifications | out |
| Email (SES / SendGrid / SMTP) | Invoices, receipts, digests, staff alerts | out |
| e-sign (Digio / Leegality) | Rental-agreement signing | out + webhook |
| Maps (Google / Mapbox) | Geocoding on save; map view on the site | out |
| Sentry | Error monitoring — API, workers, frontends | out |
WhatsApp adapter: one WhatsAppProvider interface (send_template, parse_status_webhook, parse_inbound_webhook) with concrete AiSensyProvider / GupshupProvider / TwilioWhatsAppProvider / InteraktProvider. Switching provider is a config change plus a template-name remap — no domain code changes.
Consistency rules
- Money is
Decimal(12,2), currency stored explicitly (INR in v1); rounding documented per charge. - Invoices immutable once issued; ledger append-only; balances derived and cached.
- Idempotency keys on invoice generation (
tenancy:period), payments (razorpay_payment_id) and outbound messages (event_id). - Soft-delete for tenants, units and tenancies with financial history — never hard-deleted.
- Store UTC, present IST.
Environments & delivery
- local — Docker Compose: api, worker, beat, postgres, redis, minio, mailhog.
- staging — mirrors prod; sandbox BSP / Razorpay keys; seeded demo data.
- production — managed Postgres + Redis; object storage; HTTPS; backups; monitoring.
- One container image for api / worker / beat (different command). Nginx for TLS. CI: ruff → mypy → pytest → build → migrate → deploy. Frontends build eslint → tsc → vitest → deploy.
Data model
All tables carry id (uuid), created_at, updated_at; money is Decimal(12,2) INR; business tables carry organization_id (one org in v1, namespaced for a future SaaS).
Organization 1─┬─* User(staff) ─* PropertyAssignment *─ Property
│
├─* Property 1─* Block 1─* Floor 1─* Unit 1─* Bed
│ ├─* MediaAsset
│ ├─* UnitAmenity *─ Amenity
│ └─* Meter 1─* MeterReading
│
├─* Lead 1─* LeadActivity └─1 VisitSchedule
│
├─* Tenant 1─* TenantDocument(KYC) └─* CoTenant / Guarantor
│
├─* Tenancy ──1 Unit / Bed
│ ├─1 RentalAgreement ─* AgreementVersion
│ ├─* RecurringCharge ─ ChargeType
│ ├─* Inspection(move-in/out) ─* InspectionItem
│ ├─* Invoice 1─* InvoiceLineItem
│ │ ├─* Payment ─* PaymentAllocation
│ │ └─* CreditNote
│ ├─* LedgerEntry (rent account, append-only)
│ ├─* DepositLedgerEntry (deposit account, append-only)
│ ├─* ReminderLog · PromiseToPay
│ └─1 MoveOutSettlement
│
├─* MaintenanceTicket ─* TicketComment / TicketAttachment ─ Vendor
├─* PreventiveMaintenance ─* PreventiveTask ├─* Asset ─ Unit
│
├─* Expense ─ ExpenseCategory / Vendor / Property ├─* OwnerPayout
│
├─* NotificationTemplate · MessageLog · InboundMessage
├─* Broadcast ─* BroadcastRecipient ├─* Announcement ─* AnnouncementAck
│
├─* Document(vault) · AuditLog · ChangeRequest · Review
└─1 Settings / IntegrationCredentials
Entity groups
Access
Organization, Settings, IntegrationCredentials (encrypted), User, PropertyAssignment.
Property & inventory
Property, Block, Floor, Unit, Bed, Amenity, UnitAmenity, PropertyAmenity, MediaAsset, Meter, MeterReading.
Leads
Lead, LeadActivity, VisitSchedule.
Tenants & tenancies
Tenant, TenantDocument, CoTenant, Guarantor, Tenancy, RentalAgreement, AgreementTemplate / Version, Inspection, InspectionItem.
Billing & ledger
ChargeType, RecurringCharge, Invoice, InvoiceLineItem, CreditNote, Payment, PaymentAllocation, LedgerEntry, DepositLedgerEntry, MoveOutSettlement, ReminderLog, PromiseToPay.
Maintenance
MaintenanceTicket, TicketComment, TicketAttachment, Vendor, PreventiveMaintenance, PreventiveTask, Asset.
Messaging
NotificationTemplate, MessageLog, InboundMessage, Broadcast, BroadcastRecipient, Announcement, AnnouncementAck.
Finance
ExpenseCategory, Expense, OwnerPayout.
Governance
Document, AuditLog, ChangeRequest, Review, WebhookEvent.
Key derived rules
| Value | Rule |
|---|---|
Unit becomes occupied | When a Tenancy goes active; back to available on ended (or maintenance if a turnover task is open). |
| Tenancy outstanding | Sum of unpaid invoice balances = last LedgerEntry.running_balance on the rent account. |
| Electricity (sub-meter) | (current − previous) × rate + fixed_charge; a reading must exist for the period or the run flags it. |
| Electricity (shared) | Parent bill split by equal / headcount / ratio; rounding remainder assigned to the largest share so it reconciles exactly. |
| Proration | rent × days_occupied_in_period ÷ days_in_period, rounded per policy (default: actual days). |
| Late fee | Once per invoice after due_date + grace_days: flat | percent × balance | per_day × days, capped at late_fee_cap. |
| Rent escalation | On the start-date anniversary: rent ×= (1 + escalation_percent ÷ 100); writes escalation_last_applied_on. |
| Deposit refund | deposit_held − outstanding_dues − damage_deductions − other_deductions, floored at 0; negative → tenant owes. |
| Aging bucket | today − invoice.due_date → 0–30 / 31–60 / 61–90 / 90+. |
WhatsApp automation
WhatsApp is the primary notification channel, delivered through a BSP (AiSensy / Gupshup / Twilio / Interakt) behind a single swappable adapter. Business-initiated messages use pre-approved templates; variable order matches what is registered on the BSP.
Template catalogue (representative)
| Event | Trigger | Attach | Phase |
|---|---|---|---|
enquiry_ack | Website enquiry created | — | P1 |
onboarding_welcome | Tenancy activated | House-rules PDF | P1 |
invoice_issued | Monthly / ad-hoc invoice issued | Invoice PDF | P1 |
electricity_bill · water_bill · bill_generic | Utility line billed separately | — | P1 |
rent_reminder_pre · rent_due_today · rent_overdue_1 | Cadence T−3 / T0 / T+1 | — | P1 |
rent_overdue_2/3/final · late_fee_applied | Cadence T+3 / T+7 / T+15; late fee added | — | P2 |
payment_received | Payment success | Receipt PDF | P1 |
otp_login | Tenant portal login | — | P1 |
agreement_ready · agreement_signed_copy | e-sign sent / completed | Signed PDF | P2 |
visit_confirmed · visit_reminder | Visit scheduled / 2 h before | — | P2 |
autopay_setup · autopay_failed | Mandate active / debit failed | — | P2 |
agreement_expiry_60/30/7 · renewal_offer | Renewal window | — | P2 P3 |
notice_ack · moveout_settlement · deposit_refund_initiated | Notice / settlement / refund | Statement PDF | P2 |
ticket_created/status/resolved | Maintenance ticket lifecycle | — | P2 |
preventive_notice · announcement · festival_greeting | Planned work · broadcast · seasonal | optional | P2 P3 |
meter_reading_request | Ask tenant to submit a reading | — | P2 |
Reminder cadence (editable)
[
{ "offset_days": -3, "event": "rent_reminder_pre", "channel": "whatsapp" },
{ "offset_days": 0, "event": "rent_due_today", "channel": "whatsapp" },
{ "offset_days": 1, "event": "rent_overdue_1", "channel": "whatsapp" },
{ "offset_days": 3, "event": "rent_overdue_2", "channel": "whatsapp" },
{ "offset_days": 7, "event": "rent_overdue_3", "channel": "whatsapp" },
{ "offset_days": 15, "event": "rent_overdue_final", "channel": "whatsapp", "also": ["sms"] }
]
The daily job matches today − due_date against the entries, sends once per stage (tracked in ReminderLog), and stops the moment the balance is zero. If bill_delivery_mode = consolidated, only invoice_issued is used and the per-utility templates stay dormant.
Two-way (inbound) keywords
| Keyword | Auto-reply |
|---|---|
BALANCE / DUE | Current outstanding + a payment link |
PAY | A fresh link for the oldest unpaid invoice |
RECEIPT / STATEMENT | Last receipt / ledger statement PDF |
TICKET <text> | Creates a maintenance ticket, replies with the number |
STOP / START | Sets / clears opt-out |
| anything else | Help text + routed to the staff inbox |
Guardrails
- Opt-in captured at onboarding (logged); opt-out always honoured; quiet hours 21:00–08:00 IST for non-critical sends.
- Approved templates only for business-initiated messages; correct utility vs marketing category; marketing respects opt-out strictly.
- Payment links are per-invoice and expiring; no sensitive IDs in message bodies.
- Delivery status tracked (sent / delivered / read / failed) with retry then SMS / email fallback; per-message cost logged.
- A
consoleprovider for local / CI;POST /messages/testfor template QA.
Billing & payments
Charge types
- Rent
- Security deposit
- Electricity
- Water
- Maintenance / society
- Wi-Fi / internet
- Mess / food
- Housekeeping
- Parking
- Gas / cylinder
- Late fee / penalty
- Damage / repair recovery
- One-time (key deposit, painting)
- Custom
Electricity billing modes
| Mode | Amount |
|---|---|
| Sub-meter | (current − previous) × rate_per_unit + fixed_charge, from the period's reading + meter photo. Optional tiered slabs. |
| Fixed | A flat monthly amount per unit. |
| Shared / split | Parent property bill split across sibling units — equal, by headcount, or by custom ratio; rounding remainder to the largest share. |
Water billing mirrors this (fixed / per-head / metered). If no reading exists at billing time, electricity is skipped, the invoice is flagged electricity_pending, staff are alerted, and a later reading produces a supplementary line.
Invoice → ledger example
| Date | Description | Debit | Credit | Balance |
|---|---|---|---|---|
| 2026-09-01 | Invoice INV-2026-0043 (Sep) | 16,462.00 | — | 16,462.00 |
| 2026-09-04 | Payment · UPI · RCPT-0091 | — | 16,462.00 | 0.00 |
| 2026-10-01 | Invoice INV-2026-0061 (Oct) | 15,930.00 | — | 15,930.00 |
| 2026-10-09 | Late fee (Oct) | 300.00 | — | 16,230.00 |
Payments
- Online — Razorpay Payment Link per invoice (UPI / card / netbanking / wallet); webhook
payment_link.paid→ verify signature → dedupe →Payment+ allocations (oldest-first on overpayment) → ledger credit → receipt PDF →payment_received→ cancel pending reminders. - UPI Autopay — one-time e-mandate; Razorpay auto-charges on the due day;
subscription.chargedbooks it identically; a failed debit sendsautopay_failedand falls back to the normal cadence. - Offline — cash / cheque / bank transfer entered with proof;
pendinguntil an Accountant / Admin approves; then same downstream (allocation, ledger, receipt). - Partial & allocation — invoice goes
partially_paid; allocation order oldest-first, then rent → utilities → other → late fee; overpayment becomes a credit applied to the next invoice. - Refunds & payouts — deposit refund via Razorpay Payout or manual, tracked on the deposit ledger; owner payouts recorded per property for the P&L.
Move-out settlement
refund = deposit_held
− outstanding_rent_ledger_balance
− Σ inspection.damage_deductions
− other_deductions (cleaning, unpaid utilities from final readings)
(floored at 0; if negative, tenant owes → final invoice)
bill_delivery_mode) · grace period & late-fee formula & cap · allocation priority · tenant-submitted meter readings on/off · GST registration · deposit months & non-refundable deductions · website token amount & hold duration.
Tech stack
Backend
| Concern | Choice |
|---|---|
| Language / framework | Python 3.12+ · Django 5.x · Django REST Framework |
| API schema & docs | drf-spectacular (OpenAPI 3 → Swagger UI, TS client) |
| Auth | djangorestframework-simplejwt · django-otp / pyotp (2FA) · custom phone-OTP backend |
| Async / scheduling | Celery 5 · django-celery-beat · Redis broker |
| Database / cache | PostgreSQL 16 · Redis (django-redis) |
| Files | django-storages + boto3 → S3 / Cloudflare R2 (private, signed URLs) |
| WeasyPrint (HTML → PDF) — invoices, receipts, statements, agreements | |
| Domain helpers | Decimal / django-money · django-phonenumber-field · django-filter · django-import-export · Pillow |
| Payments / messaging | razorpay SDK · httpx BSP adapters · MSG91 / Twilio SMS · django-anymail (SES / SendGrid) |
| Security | cryptography (Fernet) for credential encryption · django-ratelimit · pip-audit · gitleaks |
| Quality | ruff · mypy + django-stubs · pytest + pytest-django + factory_boy + freezegun · coverage.py |
Frontend
| Concern | Choice |
|---|---|
| Dashboard & portal | React 18 + Vite + TypeScript · React Router v6 |
| Public website | Next.js (App Router) — SSR/SSG for SEO |
| Server state / HTTP | TanStack Query · Axios wrapper with token refresh |
| API types | openapi-typescript + orval, generated from /api/schema/ |
| Client state / forms | Zustand · React Hook Form + Zod |
| UI | Tailwind CSS + shadcn/ui (Radix) · TanStack Table · Recharts · date-fns (Asia/Kolkata) |
| Maps / uploads | MapLibre / Leaflet · react-dropzone with presigned S3 uploads |
| Payments | Razorpay Checkout JS + hosted Payment Links |
| i18n / PWA | i18next (English / Hindi) · vite-plugin-pwa (portal) |
| Quality | eslint · prettier · Vitest + React Testing Library · Playwright (E2E) |
Why this stack
- Django + DRF — the fastest path to a correct, auditable domain model with migrations, admin and mature auth; ideal for money and records.
- Celery + Beat — the billing and reminder automation is inherently scheduled and async; this is the standard tool.
- PostgreSQL — transactions, constraints, JSONB for flexible config, strong indexing.
- React + Vite for the data-dense dashboard; Next.js only where SEO needs server rendering.
- Razorpay — first-class UPI, Payment Links (ideal for WhatsApp), UPI Autopay and Payouts.
- BSP adapter — no lock-in; the WhatsApp provider is a config choice.
Project structure
A monorepo — pnpm workspaces for the frontends beside a Django backend directory.
fahiq/ ├─ backend/ # Django project │ ├─ config/ # settings (base/dev/prod), urls, celery, asgi/wsgi │ ├─ apps/ │ │ ├─ accounts/ # staff users, roles, permissions, 2FA, property assignment │ │ ├─ organization/ # business profile, settings, encrypted integration credentials │ │ ├─ properties/ # property, block, floor, unit, bed, amenity, media │ │ ├─ catalog/ # public read models, search, filters, SEO pages │ │ ├─ leads/ # enquiries, pipeline, visits, activities │ │ ├─ tenants/ # tenant persons, KYC documents, co-tenants, guarantors │ │ ├─ tenancies/ # lease, terms, escalation, renewal, notice, move-out, transfer │ │ ├─ agreements/ # templates, generation, e-sign, versions │ │ ├─ inspections/ # move-in/out inventory, condition photos │ │ ├─ meters/ # meter definitions, readings, consumption │ │ ├─ billing/ # charge types, recurring charges, invoices, proration, late fees, numbering │ │ ├─ payments/ # razorpay, links, autopay, webhooks, offline, receipts, refunds, payouts │ │ ├─ ledger/ # tenant ledger, deposit ledger, aging, statements │ │ ├─ collections/ # reminder cadence engine, dunning, promise-to-pay │ │ ├─ maintenance/ # tickets, SLA, vendors, preventive schedules, assets │ │ ├─ messaging/ # templates, channel router, WhatsApp BSP adapter, delivery logs, inbound │ │ ├─ finance/ # expenses, categories, vendors, P&L, owner payouts, exports │ │ ├─ reports/ # rent roll, occupancy, collections, aging, funnel, exports │ │ ├─ documents/ # document vault, PDF rendering, signed URLs │ │ ├─ audit/ # audit log, change requests / approvals │ │ └─ common/ # base models, money & phone helpers, pagination, permissions │ ├─ manage.py · pyproject.toml · Dockerfile │ ├─ frontend/ │ ├─ packages/ │ │ ├─ api-client/ # generated TS client + shared types │ │ ├─ ui/ # shared design-system components │ │ └─ config/ # eslint / tsconfig / tailwind presets │ └─ apps/ │ ├─ dashboard/ # React + Vite (staff) │ ├─ portal/ # React + Vite (tenant) │ └─ website/ # Next.js (public, SEO) │ ├─ infra/ │ ├─ docker-compose.yml # local: db, redis, minio, mailhog, api, worker, beat │ └─ nginx/ · render.yaml | fly.toml | terraform/ │ ├─ docs/ # the Markdown specification (01–15) + this page └─ .github/workflows/ # CI
Domain logic lives in services.py per app (thin views, thin models, fat services); scheduled / async entry points in tasks.py per app.
Environment variables (excerpt)
# core DJANGO_SETTINGS_MODULE=config.settings.dev SECRET_KEY=… DATABASE_URL=postgres://… REDIS_URL=redis://… FIELD_ENCRYPTION_KEY=… # Fernet key for IntegrationCredentials # storage AWS_S3_ENDPOINT_URL=… AWS_STORAGE_BUCKET_NAME=… KYC_BUCKET_NAME=… AWS_ACCESS_KEY_ID=… AWS_SECRET_ACCESS_KEY=… # payments RAZORPAY_KEY_ID=… RAZORPAY_KEY_SECRET=… RAZORPAY_WEBHOOK_SECRET=… BOOKING_TOKEN_AMOUNT=2000 BOOKING_HOLD_DAYS=3 # whatsapp (BSP) WHATSAPP_PROVIDER=aisensy # aisensy | gupshup | twilio | interakt | console WHATSAPP_API_KEY=… WHATSAPP_SENDER_ID=… WHATSAPP_WEBHOOK_SECRET=… # sms / email / esign / maps / sentry SMS_PROVIDER=msg91 SMS_API_KEY=… EMAIL_URL=… DEFAULT_FROM_EMAIL=… ESIGN_PROVIDER=digio ESIGN_CLIENT_ID=… ESIGN_CLIENT_SECRET=… MAPS_API_KEY=… SENTRY_DSN=… # frontend (per app) VITE_API_BASE_URL=… VITE_RAZORPAY_KEY_ID=… VITE_MAPS_TOKEN=… VITE_SENTRY_DSN=…
Roadmap
Phased so a usable, revenue-affecting slice — automated billing + WhatsApp reminders + payments — ships first, then breadth. Durations assume ~1–2 backend + 1 frontend engineer.
- Monorepo, CI, Docker Compose, environments, Sentry.
- Django + Postgres + Redis + Celery + Beat + object storage.
- Auth: staff login + 2FA, tenant phone-OTP, RBAC + property scoping, audit log.
- Organization / Settings / encrypted IntegrationCredentials with test buttons.
- Property / Unit / Bed / Amenity / Meter models + dashboard shell + Properties CRUD with photo upload.
- OpenAPI schema → generated TS client; seed/demo data command.
- Public website: landing, search + filters, listing & property pages, enquiry form, WhatsApp click-to-chat, SSR/SEO, sitemap.
- Leads: capture, pipeline, assignment, auto
enquiry_ack, convert-to-onboarding. - Onboarding: application + KYC + terms + unit assignment + welcome (agreement = signed-scan upload for now).
- Billing: charge types, recurring charges, daily billing run (rent + recurring + sub-meter / fixed electricity + water), numbering, invoice PDF, tenant + deposit ledger, ad-hoc invoices.
- Payments: Razorpay Payment Links, webhook reconciliation, receipt PDF, offline entry + approval, partial payments + allocation.
- WhatsApp: BSP adapter +
consoleprovider, template registry, scheduler, P1 templates, cadence T−3 / T0 / T+1,payment_received, delivery tracking. - Dashboard home KPIs; tenant portal P1 (OTP, dues + pay, invoices, receipts, ledger, tickets); reports: rent roll, occupancy, collections.
- Proration, late-fee engine, credit notes with approvals, shared-meter split, full cadence with escalation, aging report.
- UPI Autopay, promise-to-pay, deposit refund via Payout, reconciliation view.
- Agreement templates + e-sign + versioning + police-verification form.
- Rent escalation, renewal workflow + expiry reminders, notice → move-out settlement, transfers, move-in/out inspections.
- Maintenance tickets (portal + WhatsApp), vendors, SLA, preventive schedules.
- Two-way WhatsApp, keyword auto-replies, staff inbox, broadcasts, channel fallback, opt-out + quiet hours + cost logging.
- Tenant portal P2; website schedule-a-visit + book-now token + reviews; reports P2 + scheduled digests; cadence & template editors.
- Expenses, owner payouts, per-property P&L, cash-flow, Tally / Zoho exports, GST report.
- Asset register; churn / retention & maintenance-cost analytics; richer dashboard charts.
- Public-site CMS-lite, locality SEO pages, English / Hindi, blog.
- Tenant portal P3 (PG roommate + mess, refer-a-friend, PWA, push); electricity slabs; convenience-fee pass-through.
- React Native apps; channel manager; smart access & IoT meters; AI (chatbot, default-risk scoring, ticket triage, OCR).
- Aadhaar / DigiLocker KYC; mess & laundry; short-stay mode; renter's insurance; BBPS; multi-owner SaaS.
First three demo milestones
- M1 (end P0) — manage properties & units with photos; see them listed.
- M2 (mid P1) — website search + listing detail + enquiry → lead in the dashboard →
enquiry_ackon WhatsApp. - M3 (end P1) — onboard a tenant → billing run creates an invoice → WhatsApp bill with a Razorpay link → pay → auto-reconcile → receipt on WhatsApp → dashboard shows the collection.
Initial setup
Local development brings up Postgres, Redis, MinIO (S3) and MailHog in Docker; the API, worker and Beat run on the host.
# one-time cp .env.example .env docker compose -f infra/docker-compose.yml up -d db redis minio mailhog # backend cd backend python -m venv .venv && source .venv/bin/activate pip install -r requirements/dev.txt python manage.py migrate python manage.py seed_demo # demo org, properties, units, a tenant, invoices python manage.py runserver celery -A config worker -l info celery -A config beat -l info # frontend cd ../frontend pnpm install pnpm --filter website dev # public site :3000 pnpm --filter dashboard dev # admin :5173 pnpm --filter portal dev # tenant :5174 pnpm --filter api-client generate # regenerate the TS client from the backend OpenAPI
Services & ports
| Service | URL |
|---|---|
| API | localhost:8000 · schema at /api/schema/ · docs at /api/docs/ |
| Admin dashboard | localhost:5173 |
| Tenant portal | localhost:5174 |
| Public website | localhost:3000 |
| MailHog | localhost:8025 |
| MinIO console | localhost:9001 |
Webhooks in development
Run an ngrok / cloudflared tunnel to :8000 and point the Razorpay and BSP sandbox webhooks at it. Use Razorpay test mode and the console WhatsApp provider to avoid real charges and sends; POST /messages/test sends any template to a chosen number for QA.
Reading order for the full spec
This page consolidates the Markdown specification in /docs: 01 product overview · 02 feature catalogue · 03 roles & permissions · 04 architecture · 05 data model · 06 API design · 07 WhatsApp automation · 08 billing & payments · 09 admin dashboard · 10 public website · 11 tenant portal · 12 tech stack · 13 non-functional requirements · 14 roadmap · 15 glossary.