LeadFlow AI (Dubai Real Estate Lead Automation)
AI-powered SaaS platform for Dubai real estate brokerages. Capture inbound leads, classify with GPT-4o, auto-reply via WhatsApp, assign to agents, and track follow-ups, all behind Stripe subscription billing.
- React
- Tailwind CSS
- Node.js
- Express
- Firebase
- OpenAI
- Stripe

- Type: Product Case Study, SaaS Platform
- Product: LeadFlow AI, Dubai Real Estate Lead Automation
- Industry: Proptech / Real Estate (Dubai, UAE)
- Frontend: React (Vite) + Tailwind CSS, hosted on Vercel
- Backend: Node.js + Express, hosted on Render
- Database: Firebase Firestore + Firebase Auth
- AI Engine: OpenAI GPT-4o
- Messaging: WhatsApp Cloud API (Meta Graph API v21.0)
- Payments: Stripe (AED subscriptions with 7-day trial)
Why we built this
Dubai’s real estate market moves fast. Leads come in around the clock from buyers, renters, and investors often in multiple languages, often with a specific unit, area, or budget in mind. Brokerages that respond within minutes close deals; those that respond the next day don’t.
The core problems Dubai brokerages were facing:
- No consistent lead qualification: every agent applied their own judgment to decide which leads to call first, leading to hot buyers being left to wait while cold inquiries were chased
- Response time was too slow: manually reading, sorting, and replying to leads during business hours meant international prospects in different time zones received no reply at all
- WhatsApp was unmanaged: the primary communication channel in the UAE was handled informally, with no message history, no threading, and no record-keeping in the CRM
- Language was a barrier: Dubai’s buyer pool is global; leads arrived in Arabic, Russian, Chinese, French, and English, and most brokerages had no way to reply professionally in anything other than English
- Agent workload was unbalanced: lead distribution depended on who was online or who a manager happened to message, with no fair rotation system
- Follow-up fell through the cracks: with no automated reminders, leads that needed a call in three days were simply forgotten
The goal was to build a turnkey SaaS platform that any Dubai brokerage could subscribe to, embed a lead capture form on their website, and immediately have every inbound enquiry processed, classified, replied to, and assigned, automatically, in any language, within seconds.
What this system does
LeadFlow AI is a multi-tenant SaaS platform that gives Dubai real estate brokerages two things: an automated lead pipeline and a dashboard for managing it. Every brokerage gets its own isolated data environment, its own agent team, and its own Stripe subscription.
Automated lead pipeline
- Accepts inbound leads via a REST API endpoint that can be embedded into any website contact form, landing page, or chatbot
- Validates and checks the brokerage subscription before processing: if the Basic plan’s 100-lead monthly limit is reached, the endpoint returns a clear upgrade prompt
- Sends the lead through GPT-4o for classification: language detection, intent classification (buy / rent / invest / general inquiry), temperature scoring (hot/warm / cold), and AI-generated reply
- Assigns the lead to an agent via atomic round-robin rotation, a Firestore transaction ensures fair distribution even under concurrent submissions
- Sends the AI-generated reply directly to the lead’s WhatsApp number via the Meta Cloud API, in the same language the lead wrote in
- Stores the complete lead record, AI analysis, and message thread in Firestore for full history
AI engine (GPT-4o)
- System prompt defines a “premium Dubai luxury real estate” tone, professional, no emojis, no slang, concise but warm
- Detects lead language automatically (ISO 639-1 codes: en, ar, ru, zh, fr, and others) and replies in that language
- Classifies intent as buy, rent, invest, or inquiry based on message content
- Tags lead temperature: hot (specific budget/area / timeline), warm (genuine interest, lacks specifics), or cold (general browsing)
- Runs at temperature 0.4 with JSON response format enforced, consistent, structured output on every call
- Falls back to a safe default reply if the AI call fails, ensuring no lead is ever left without an acknowledgement
WhatsApp integration (Meta Cloud API)
- Outbound: sends AI replies and agent messages to leads via the WhatsApp Cloud API (Meta Graph API v21.0)
- Inbound: receives replies from leads via a verified Meta webhook, matches the sender’s phone number to the correct lead record, and stores the message in the thread
- Webhook verification uses Meta’s challenge-response flow with a custom verify token
- All incoming payloads are validated via HMAC-SHA256 signature using the Meta App Secret before processing
- Phone numbers are cleaned (stripped of spaces, dashes, and plus signs) before every API call
Owner dashboard
- Displays total leads, hot leads count, follow-ups due today, and new leads this week
- Shows agent performance table: total leads assigned vs. leads closed per agent
- Lists the 10 most recent leads with full status, temperature, and intent data
- The owner can reassign leads between agents directly from the dashboard
- Visible only to users with the owner role: enforced server-side via requireRole middleware
Agent dashboard
- Shows assigned leads, active (non-closed) leads, and follow-ups currently due
- Lists the agent’s 10 most recent assigned leads
- Agents can only view and interact with leads assigned to them, enforced in every route handler
- Agents can send messages to leads, update lead status, and set follow-up dates
Subscription billing (Stripe)
- Two plans available: Basic (AED 149/month, 100 leads) and Pro (AED 399/month, unlimited leads + analytics)
- Both plans include a 7-day free trial before billing begins
- Stripe Checkout handles the payment flow; Stripe Billing Portal handles plan changes and cancellations
- Stripe webhooks update Firestore user records in real time on subscription activation, update, and cancellation
- Agents automatically inherit their owner’s subscription status: if the owner’s plan lapses, agents lose access too
Follow-up automation
- Agents set a follow-up date on any lead directly from the lead detail page
- A scheduled cron job runs every day at 8:00 AM UAE time (UTC+4) via node-cron
- Any lead with a follow-up date on or before today and a non-closed status is flagged followUpDue: true in Firestore
- The agent dashboard surfaces these flags as a follow-up due count, creating a daily task list without manual tracking
Benefits
- Instant first response at any hour: every lead gets a professional WhatsApp reply within seconds, regardless of time zone or day of the week
- Replies in the lead’s language: Arabic, Russian, Chinese, French, and English buyers all receive a response in their own language without any manual effort
- Fair, automatic agent assignment: round-robin distribution via Firestore transactions means no agent is overlooked and no manager needs to manually route leads
- Hot leads surface immediately: buyers who mention a specific budget, area, or timeline are flagged HOT so agents know exactly who to call first
- Complete conversation history: every WhatsApp message in and out is stored against the lead record, giving agents full context before they pick up the phone
- No missed follow-ups: the daily cron job ensures agents see overdue leads on their dashboard each morning without any manual diary management
- Subscription-gated access: brokerages pay per month; if a plan lapses, access is revoked automatically via Stripe webhook sync
- Multi-tenant by design: each brokerage’s data is isolated by ownerUid; no lead or agent data is ever visible across brokerage accounts
How it works (simplified)
- A lead submits a form on the brokerage’s website, the form POSTs to the public /api/leads endpoint with name, email, phone, message, and ownerUid
- Subscription and plan limits are checked: the server verifies the brokerage has an active or trialing Stripe subscription, and that the Basic plan lead cap has not been reached
- GPT-4o processes the lead: language is detected, intent classified, temperature scored, and a professional reply generated in the lead’s own language
- The lead is assigned to an agent: a Firestore transaction reads the round-robin counter, selects the next agent in the rotation, and atomically increments the counter
- The lead record is written to Firestore, all fields stored: name, contact details, message, AI analysis (language, intent, temperature), assigned agent, status, and timestamps
- The AI reply is sent via WhatsApp: if a phone number was provided, the generated reply is dispatched through the Meta Cloud API, and the WhatsApp message ID is stored
- The API returns a success response: the caller receives the lead ID, AI reply text, intent, temperature, and assigned agent ID
- The agent is notified via their dashboard: the new lead appears in their assigned leads list with full AI analysis visible
- If the lead replies on WhatsApp: the message arrives at the Meta webhook, is validated, matched to the lead by phone number, and stored in the messages collection
- Follow-up dates are tracked automatically: the daily cron job at 8 AM UAE time flags any lead whose follow-up date has arrived, surfacing it on the agent’s dashboard
System architecture (simplified)

Subscription plans
LeadFlow AI is sold as a monthly SaaS subscription in UAE Dirhams. Both plans include a 7-day free trial before the first charge. Stripe Checkout handles the payment flow; Stripe Billing Portal handles self-service plan changes and cancellations.
| Plan | Price | Lead Limit | Included Features |
|---|---|---|---|
| Basic | AED 149 / month | 100 leads / month | AI classification, WhatsApp auto-reply, agent assignment, follow-up tracking |
| Pro | AED 399 / month | Unlimited leads | Everything in Basic + analytics dashboard, agent performance tracking, priority support |
The Basic plan lead limit is enforced server-side on every lead submission. When the 100-lead monthly cap is reached, the API returns an HTTP 429 response with a clear upgrade message. Pro plan subscribers have no cap.
Dashboard overview
Owner dashboard
The owner's view is the brokerage’s operational command centre. It shows four headline KPIs (total leads, hot leads, follow-ups today, new leads this week), an agent performance breakdown, and a live feed of the most recent leads. Owners can reassign any lead to a different agent directly from this view.
Role-restricted: visible only to owner accounts
Agent dashboard
The agent's view is designed for daily use. It shows the agent’s assigned lead count, active (non-closed) lead count, and follow-ups currently due, the three numbers that define their working day. Below the KPIs is a list of their most recently assigned leads.
Agents only see leads assigned to them: enforced at the API level
Lead detail page
Each lead has a dedicated detail page showing all contact information, AI classification results (language, intent, temperature), the full WhatsApp message thread, and status controls. Agents can update the lead’s status, set or clear a follow-up date, and send new messages directly from this page.
Message thread includes AI replies, agent messages, and incoming WhatsApp replies in chronological order
Pricing page
Public-facing pricing page displays both plans with feature lists and a “Start Free Trial” call-to-action. Clicking a plan triggers Stripe Checkout in AED, with the 7-day trial clearly communicated before any payment details are entered.
Currency displayed in AED; trial communicated pre-checkout
Lead classification & AI scoring
Every inbound lead is processed by GPT-4o using a structured system prompt that enforces a consistent professional tone and returns strict JSON. The model evaluates four dimensions simultaneously and returns them in a single API call.
| Temperature | Score | Routing Action | Classification Criteria |
|---|---|---|---|
| HOT | High | Immediate agent assignment + WhatsApp reply | Mentions specific budget, area, or timeline. Clearly ready to act. |
| WARM | Medium | Assigned + follow-up date set | Shows genuine interest but lacks specifics about budget, area, or timing. |
| COLD | Low | Stored, lower priority nurture | General inquiry, browsing behaviour, or unclear intent. |
The AI prompt explicitly defines the classification rules for each temperature band, prohibits emojis and slang, and instructs the model to respond in the detected language of the lead’s message. The system prompt is fixed (not user-editable) to ensure brand consistency across all brokerages.
Project evolution & key milestones
The platform scope expanded significantly from an initial lead capture proof-of-concept. Key additions and refinements delivered across the build:
AI & classification
- GPT-4 integration upgraded to GPT-4o for higher accuracy and lower latency
- response_format: { type: 'json_object' } enforced to eliminate markdown-wrapped JSON parsing failures
- Temperature set to 0.4 after testing: lower values produced overly rigid replies; higher values produced inconsistent classification
- Fallback reply added, so no lead is ever unanswered if the OpenAI API is unavailable
- Intent classification expanded from buy/rent to include invest and inquiry as distinct categories
WhatsApp integration
- Initial outbound-only integration expanded to support two-way messaging via the Meta webhook
- Incoming message matching logic added: sender phone matched to the most recent lead record in Firestore
- HMAC-SHA256 signature validation added using the Meta App Secret for all incoming webhook payloads
- Phone number cleaning function added to handle +, spaces, and dashes from various form inputs
Agent assignment
- Initial random assignment replaced with deterministic round-robin using a Firestore atomic counter transaction
- Counter document stored per brokerage (assignment_{ownerUid}) to ensure correct rotation per team
- Graceful fallback: if no agents exist, the lead is stored unassigned rather than failing
Subscription billing
- Stripe integration added with two recurring AED products (Basic and Pro)
- 7-day free trial added to subscription_data at checkout session creation
- Stripe webhook handler added for checkout.session.completed, customer.subscription.updated, and customer.subscription.deleted events
- Subscription status synced to Firestore user record in real time on every Stripe event
- Basic plan 100-lead monthly cap enforced server-side via Firestore count() query
- Stripe Billing Portal endpoint added for self-service plan management
Security & access control
- Firebase Auth middleware added to all protected routes; public lead capture endpoint intentionally left unprotected
- requireSubscription middleware added to check subscription status for both owners and agents (agents inherit from the owner)
- requireRole middleware added for owner-only routes (owner dashboard, agent management)
- Raw body preservation implemented for Stripe and WhatsApp webhook routes (bypassing Express JSON parsing)
Follow-up system
- followUpDate field added to lead schema; agents can set it from the lead detail page
- Daily cron job added via node-cron: runs at 8:00 AM UAE time (UTC+4 = 04:00 UTC)
- Batch Firestore update sets followUpDue: true on all leads whose date has arrived, for efficient dashboard display
Data flow architecture
A reference overview of how data moves through each stage of the system and at what cadence:
| Stage | Source | Destination | Trigger / Cadence |
|---|---|---|---|
| Lead intake | Website form / API call | Express backend | On submission (real-time) |
| AI processing | OpenAI GPT-4o | Firestore lead record | Per lead, synchronous |
| Agent assignment | Firestore counters (round-robin) | Lead record (assignedAgentId) | Per lead, synchronous |
| WhatsApp reply | AI-generated reply text | Lead's phone (Meta API) | Per lead, async |
| Follow-up check | Firestore leads collection | followUpDue flag on each lead | Daily at 8:00 AM UAE time |
| Dashboard refresh | Firestore (Realtime queries) | Owner / Agent dashboard | On page load / request |
| Subscription sync | Stripe webhook events | Firestore user record | On Stripe event (real-time) |
Note
Firestore is the single source of truth for all lead records, message threads, user profiles, subscription status, and round-robin counters. The Firebase Admin SDK (backend) and Firebase Web SDK (frontend) both read from the same Firestore project, with security rules and server-side middleware providing access control.
API reference
All endpoints are prefixed with /api/. Protected routes require a Firebase ID token in the Authorization: Bearer <token> header. The lead capture endpoint (/api/leads POST) is intentionally public to allow embedding on any brokerage website without authentication.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/leads | Public lead capture: no auth required. Runs AI, assigns agent, sends WhatsApp. |
| GET | /api/leads | List leads (owners see all, agents see assigned only). Filterable by status, temperature, intent. |
| GET | /api/leads/:id | Single lead detail with access control (role-based). |
| PATCH | /api/leads/:id | Update lead status, follow-up date, or agent assignment. |
| GET | /api/leads/:id/messages | Retrieve full message thread for a lead. |
| POST | /api/leads/:id/messages | Agent sends a message, delivered via WhatsApp if phone is available. |
| GET | /api/dashboard/owner | Owner dashboard: total leads, hot leads, follow-ups today, agent performance. |
| GET | /api/dashboard/agent | Agent dashboard: assigned leads, active leads, follow-ups due. |
| POST | /api/stripe/create-checkout | Create Stripe Checkout session (Basic or Pro plan, AED, 7-day trial). |
| POST | /api/stripe/portal | Create Stripe Billing Portal session for plan management. |
| POST | /api/stripe/webhook | Receive Stripe events: checkout.session.completed, subscription.updated/deleted. |
| GET | /api/whatsapp/webhook | Meta webhook verification challenge (subscribe mode). |
| POST | /api/whatsapp/webhook | Receive incoming WhatsApp messages, match to lead by phone, store in messages collection. |
| GET | /api/health | Health check: returns server status and timestamp. |
Security
LeadFlow AI is built with multi-tenant security as a first-class requirement. Each security layer is implemented independently so that no single point of failure exposes brokerage data.
| Security Layer | Implementation |
|---|---|
| Firebase ID Token Auth | All protected routes verify Bearer token via Firebase Admin SDK. Invalid or expired tokens return 401. |
| Subscription Gate | Middleware checks owner subscription status (active / trialing) before allowing access. Agents inherit their owner’s subscription. |
| Role-Based Access Control | requireRole(‘owner’ | ‘agent’) middleware enforces role boundaries. Agents can only access their assigned leads. |
| WhatsApp Signature Validation | Incoming Meta webhooks validated via HMAC-SHA256 using App Secret. Invalid signatures return 401. |
| Stripe Webhook Verification | Stripe events verified using stripe.webhooks.constructEvent with the webhook signing secret. |
| Helmet HTTP Headers | Sets security headers (XSS, clickjacking, content-type sniffing) via the Helmet middleware. |
| CORS Restriction | Requests accepted only from the configured FRONTEND_URL. Credentials are required for authenticated calls. |
| Plan Limit Enforcement | Basic plan lead cap (100/month) enforced server-side via Firestore count query on every lead submission. |

