PropTech · B2B2C
LiveConfirmHai
Real-time rental availability confidence for student housing
The problem
Three specific, named pain points
- 01
The "rented out" trap
Listing sites show rooms that were rented weeks ago, because nobody re-verifies availability after the first post.
- 02
Roommate roulette
No structured way to check schedule, cleanliness, or budget compatibility before moving in with a stranger.
- 03
Broker leakage
Heavy upfront commissions and spam contact leakage before a tenant even sees a genuine, currently-available listing.
Architecture
FastAPI, async end to end, 37 routes in production
Routers → services → models is enforced as a contract, not a convention: shared files (models, schemas, config, main) are treated as contract changes that require wider review before touching them.
Client
tenant.html / owner.html
- Single-file HTML frontends
API layer
FastAPI routers
- 37 routes
- async Python
Service layer
Business logic
- ConfidenceEngine
- PredictiveEngine
- Auth / OTP service
Data layer
SQLAlchemy 2.0 (async)
- Models + migrations
Database
PostgreSQL (Neon)
- SQLite in dev
- Deployed on Render
Product decision
The Availability Confidence Score
Why a live score, not a status toggle
Most listing platforms ask owners to manually flip an "available / unavailable" switch — and owners never do it once the room is filled, which is exactly how the "rented out" trap happens. A boolean field trusts the owner to act; it doesn't model how trust decays.
Instead, ConfirmHai scores availability from freshness and vacancy signals that decay automatically over time, and treats a single WhatsApp reply from the owner as a first-class signal that can move the score instantly. The trade-off accepted here is real: a scoring engine plus a WhatsApp integration is more to build and operate than a boolean field. But the boolean field is the entire reason competitors' listing data goes stale.
Signal
Freshness
- Time since last confirmation
Signal
Vacancy activity
- Interest / booking signals
Engine
ConfidenceEngine + PredictiveEngine
- Blends signals into a live score
Output
0–100% confidence
- Shown to every tenant
Override signal
Owner WhatsApp reply: "rent ho gaya"
- ("it's rented")
Result
Score → 0, status: Rented
- Verified end-to-end
Integration
WhatsApp / Meta Cloud API
Owner onboarding and freshness nudges run over WhatsApp — verified with webhook signature checks and a rate-limited OTP flow.
Owner
WhatsApp message
Meta
Cloud API
Verify
Signature check
- Rejects unsigned payloads
FastAPI
OTP issue / verify
- bcrypt-hashed
- TTL + attempt-capped
Session
JWT issued
The same channel also drives the freshness-nudge loop: a stale listing triggers an automatic owner nudge; the reply refreshes (or zeroes) the confidence score. An "Invisible Listing" flow parses an owner's Hinglish WhatsApp caption directly into a live property listing.
Security
Zero-trust audit remediation
Every row below was verified against a specific threat, not shipped speculatively.
| Control | Threat mitigated | Implementation | Status |
|---|---|---|---|
| JWT denylist | Stolen or leaked token reused after logout | Revoked tokens tracked server-side and checked on every authenticated request | Enforced |
| bcrypt hashing | Credential or OTP database leak | Passwords and OTPs bcrypt-hashed before storage — never stored in plaintext | Enforced |
| IDOR / authz guards | Cross-account access to another user's data | Explicit ownership checks on every resource route, verified in a zero-trust audit | Fixed |
| OTP rate limiting | Brute-force OTP guessing | 5 attempts per 30 minutes, then locked | Capped |
| PII masking | Contact leakage before a tenant pays | Owner and tenant phone numbers stay masked until a paid unlock clears | Enforced |
| Stored XSS remediation | Malicious script injected via listing content | Input sanitized and output-encoded; found and fixed during a zero-trust security audit | Fixed |
| Money / PII audit logging | Undetected fraud or data misuse | Every payment event and PII access is written to an audit log | Enforced |
| Prod fail-fast config | Silent misconfiguration reaching production | App refuses to boot in production if dev-only settings or secrets are detected | Enforced |
| Webhook signature verification | Spoofed WhatsApp/Meta webhook payloads | Every inbound webhook is signature-checked before it's processed | Enforced |
Monetization
Pay-to-unlock, with idempotency
The trade-off that actually mattered
ConfirmHai gates owner/roommate contact details behind a paid unlock — phone numbers stay masked until payment clears. The interesting risk here isn't pricing, it's idempotency: a payment callback can fire twice — webhook retries, a double-tap, a flaky network retry — and if that duplicate isn't caught, one real payment unlocks two different contacts for free.
Every unlock is tied to a payment_ref that's checked for prior use before access is granted — anti-replay by construction, not by trusting the client.
Engineering discipline
Anti-spiral-error scoping: the FEATURE_MAP
AI-directed engineering fails in a specific way: a change scoped to one feature quietly breaks an unrelated one because it touched a shared file without understanding the blast radius. The rule: check the map, touch only that feature's files. Changes to shared files (models, schemas, config, main) are flagged as contract changes.
Illustrative FEATURE_MAP structure
| Feature | Files | Endpoints | Tables |
|---|---|---|---|
| Confidence score | services/confidence_engine.py | POST /listings/{id}/refresh-score | listings, confidence_signals |
| Owner OTP auth | routers/owners.py, services/otp_service.py | POST /owners/register, /verify-otp | owners |
| Paid unlock | routers/unlock.py, services/payment_service.py | POST /unlock/{listing_id} | unlocks, payments |
Testing strategy
~24 automated pytest suites, kept green per change
Tests must pass before a commit lands; a smoke test on /health plus the affected endpoints runs before every merge.
| Suite category | What it verifies | Why it exists |
|---|---|---|
| auth | Registration, OTP issuance/verification, JWT issuance and expiry | Prevents silent regressions in login flows as AI-directed changes touch shared auth code |
| authz / IDOR | Deliberate cross-account access attempts against every resource route | Authorization bugs rarely show up in happy-path testing — they show up when you try to access someone else's data on purpose |
| confidence engine | Score computation across freshness/vacancy signal combinations, WhatsApp reply override | The scoring engine is the product's moat — a silent regression here is a trust regression for real users |
| rate limiting | OTP attempt and TTL enforcement under repeated requests | Confirms brute-force protection actually triggers at runtime, not just that a config value exists |
| security headers | Required response headers present on every route | Security headers regress silently when middleware order or route registration changes |
Status
Live at confirmhai.com, in front of early real users pre-marketing.
See this system broken down by category: Architecture · API Design · Security · Testing