{
"title": "Building ReserveFlow: AI-Powered Property Search for Kenya's Mobile-First Market",
"content": "# Building ReserveFlow: AI-Powered Property Search for Kenya's Mobile-First Market\n\nLast month, a developer in Kisumu used ReserveFlow to find a bedsitter in Karen, Nairobi—without visiting a single property in person. Three WhatsApp messages. Two AI-powered property recommendations. One completed booking via M-Pesa in under 20 minutes.\n\nThis is the future of real estate in Africa, and it's built on some seriously interesting engineering challenges. Let me walk you through how ReserveFlow actually works under the hood.\n\n## The Problem We're Solving\n\nKenya's real estate market is fragmented. You've got:\n- **Fragmented data**: Properties listed across 50+ websites, WhatsApp groups, and spreadsheets\n- **Mobile-first users**: 60M+ mobile money users, but most property platforms are desktop-optimized\n- **Inconsistent connectivity**: Average speeds of 8-12 Mbps in Nairobi, dropping to 2-3 Mbps in secondary cities\n- **Trust gaps**: Unverified listings, inflated prices, and sketchy agents dominate the market\n\nWe built ReserveFlow to aggregate, verify, and intelligently surface properties using AI—optimized for 2G fallback and offline-first architectures.\n\n## Architecture Overview\n\n```
\n┌─────────────────────────────────────────┐\n│ Mobile Client (React Native) │\n│ - Offline-first with SQLite │\n│ - Compressed image syncing │\n└────────────┬────────────────────────────┘\n │\n ▼\n┌─────────────────────────────────────────┐\n│ API Gateway (Kong) │\n│ - Rate limiting (100req/min for free) │\n│ - Request compression (gzip) │\n│ - M-Pesa webhook routing │\n└────────────┬────────────────────────────┘\n │\n ┌────┴────┐\n ▼ ▼\n ┌─────────┐ ┌──────────────┐\n │ GraphQL │ │ REST (Legacy)│\n │ Server │ │ Endpoints │\n └────┬────┘ └──────┬───────┘\n │ │\n └───────┬───────┘\n ▼\n ┌──────────────────────┐\n │ Service Layer │\n │ - Property AI │\n │ - Payment Handler │\n │ - Verification Bot │\n └──────────┬───────────┘\n ▼\n ┌──────────────────────┐\n │ Data Layer │\n │ - PostgreSQL (props) │\n │ - Redis (cache) │\n │ - Elasticsearch │\n └──────────────────────┘\n
```\n\n## Database Design for African Constraints\n\nWe use **PostgreSQL for structured data** (properties, bookings, users) and **Elasticsearch for search**. Here's why this matters in Kenya's context:\n\n### 1. Property Schema (Optimized for Queries)\n\n```
sql\nCREATE TABLE properties (\n id UUID PRIMARY KEY,\n title VARCHAR(255) NOT NULL,\n description TEXT,\n price_ksh BIGINT NOT NULL,\n location_point GEOGRAPHY(POINT, 4326), -- Indexed for spatial queries\n bedrooms SMALLINT,\n bathrooms SMALLINT,\n area_sqm DECIMAL(10, 2),\n amenities JSONB, -- Flexible schema for varied data\n images_urls TEXT[] -- Array for multiple images\n verification_score DECIMAL(3, 2), -- AI confidence (0-1)\n verified_at TIMESTAMP,\n agent_id UUID REFERENCES agents(id),\n created_at TIMESTAMP DEFAULT NOW(),\n updated_at TIMESTAMP DEFAULT NOW()\n);\n\nCREATE INDEX idx_location ON properties USING GIST(location_point);\nCREATE INDEX idx_price ON properties(price_ksh);\nCREATE INDEX idx_verified ON properties(verification_score DESC, created_at DESC);\n
```\n\n**Why JSONB for amenities?** Real estate data in Kenya is messy. One agent lists \"WiFi+Generator,Solar\" as a string. Another lists it as an array. JSONB lets us normalize queries across inconsistent sources.\n\n### 2. Booking Schema with M-Pesa Integration\n\n```
sql\nCREATE TABLE bookings (\n id UUID PRIMARY KEY,\n property_id UUID REFERENCES properties(id),\n user_id UUID REFERENCES users(id),\n check_in_date DATE,\n check_out_date DATE,\n total_price_ksh BIGINT,\n payment_status VARCHAR(20) DEFAULT 'pending', -- pending, processing, completed, failed\n mpesa_transaction_id VARCHAR(10) UNIQUE,\n mpesa_receipt_number VARCHAR(32),\n mpesa_initiated_at TIMESTAMP,\n mpesa_completed_at TIMESTAMP,\n status VARCHAR(20) DEFAULT 'confirmed', -- confirmed, cancelled, completed\n created_at TIMESTAMP DEFAULT NOW()\n);\n\nCREATE INDEX idx_user_bookings ON bookings(user_id, created_at DESC);\nCREATE INDEX idx_mpesa_status ON bookings(payment_status, mpesa_transaction_id);\n
\n\n## API Design: GraphQL + REST Hybrid\n\nWe expose GraphQL for complex queries (ideal for mobile with slow connections) and REST
Top comments (0)