Custom CRM for a 7-City Real-Estate Brokerage: Why Salesforce Was 4x Overkill
A 240-agent residential brokerage with 7 city offices replaced a stalled Salesforce rollout with a custom Next.js + PostgreSQL CRM in 11 weeks. Build cost ₹38L. The architecture, the cost math, the parts we cut.
Vivek Kumar
July 3, 202514 min read
0%
A residential brokerage operating in 7 Indian cities — Bengaluru, Pune, Hyderabad, Mumbai, Gurgaon, Noida, and Ahmedabad — gave us 11 weeks and ₹38 lakh to deliver what their stalled Salesforce rollout had not. 240 agents, ~9,000 active leads in the pipe at any moment, ~₹220 crore in closed GMV last year. We shipped a custom Next.js + PostgreSQL CRM that replaced Salesforce Sales Cloud, two Excel exports, and a WhatsApp-based lead-distribution chain. This is the architecture, the trade-offs we made, and the 4x cost gap that decided it.
240
Agents Across 7 City Offices
11 weeks
Discovery to Cutover
₹38L
Fixed-Price Build (Phase 1)
4x
Salesforce 5-Year TCO Multiplier
## The Answer in 60 Words
We built a Next.js 14 (App Router) frontend, a Node.js/Fastify API, PostgreSQL 16 as the primary store on a Hetzner CCX33 box, Redis for queues, and Twilio + Gupshup for WhatsApp + SMS. Role-based access has 6 roles and 41 permissions, enforced at API middleware and at the row level via Postgres RLS. Build cost ₹38 lakh. Run cost ₹68,000/month. Year-1 saving vs Salesforce: ₹84 lakh.
## Why This Matters Now
Salesforce Sales Cloud Enterprise lists at $165/user/month (~₹13,800/user/month at May 2026 rates). For a 240-agent firm, that is ₹3.32 crore/year just in licences. The Salesforce-certified implementation partner had quoted another ₹68 lakh for the rollout. Total year-1 ask: ~₹4 crore. The brokerage's 5-year TCO projection landed at ₹17.4 crore. Our custom build, including a 5-year run-and-evolve plan, came in at ₹4.2 crore. The 4x figure is real and it is what the founder put in front of his board on day one.
The deeper trigger is that India's residential brokerage market does not look like the US enterprise sales motion that Salesforce was built around. Indian agents work WhatsApp-first, schedule site visits as the primary conversion event, and depend on a builder-incentive table that changes monthly. None of those workflows fit Salesforce's lead-opportunity-stage model without ~40% custom Apex. We have seen three Indian brokerages stall on Salesforce in the last 18 months for exactly this reason.
## The Client (Specific Details)
- Sector: Residential brokerage — primary sale (60%), resale (28%), rental (12%)
- Locations: 7 city offices (Bengaluru HQ + Pune, Hyderabad, Mumbai, Gurgaon, Noida, Ahmedabad)
- Staff: 240 agents + 38 ops/admin + 12 management + 6 IT/marketing = 296 total
- Lead volume: ~12,400 fresh leads/month from Magicbricks, 99acres, Housing.com, Meta ads, Google ads, and walk-ins
- Active pipeline: 9,000 leads at any moment across stages
- The trigger: A 14-month Salesforce rollout was at 38% completion. Two integrations remained, the budget was exhausted, and agent adoption had stalled at 41% of users logging in weekly.
## The Architecture (One Diagram Worth Drawing)
FE
Frontend: Next.js 14 + TanStack Query
App Router, server components for the agent dashboard, client components for forms. Mobile-first because 78% of agents work from a Redmi or Realme handset on the field.
API
API: Node.js 20 + Fastify
Fastify chosen over Express for ~40% faster JSON throughput. Zod validators on every route. JWT auth with 15-min access + 30-day refresh tokens.
DB
DB: PostgreSQL 16 + Row-Level Security
RLS policies enforce city-office boundaries at the row level. An agent in Pune cannot see a Mumbai lead even via direct SQL — the policy returns zero rows. We sleep better.
Q
Queues: Redis + BullMQ
Lead-distribution rules, WhatsApp dispatch, SMS reminders, and the nightly builder-incentive recompute all flow through BullMQ. ~94,000 jobs processed daily.
## The 4x Cost Gap (The Real Math)
The headline number drives every other decision. Here is the spreadsheet we put in front of the board, audit-friendly numbers, no rounding.
The Salesforce number comes from public list pricing for [Sales Cloud Enterprise at $165/user/month](https://www.salesforce.com/sales/pricing/), the partner's quoted implementation, and a typical Indian SI's 18% AMC. The custom number is our quoted build + Hetzner infrastructure + 0.4 FTE engineering retainer for 5 years. We did not haircut either side.
## What Salesforce Does Well That We Had to Replicate
Honesty matters here. Salesforce is not bad software. It is overspecified for an Indian brokerage. The features that earned its price tag — and that we had to deliver in a leaner form — are these:
- Audit trail at field level. Every change to every record, who, when, what was the old value. We replicated this with a Postgres trigger writing to an audit_log table partitioned by month. ~2.4 GB/year of audit data.
- Process Builder / Flow. Salesforce's no-code workflow engine. We replicated 80% of the actual rules in a YAML-based rules engine the ops manager can edit. The remaining 20% (cross-object, multi-step) we built as code.
- Reports and dashboards. Replaced with Metabase running on a Postgres read replica. The ops manager built 14 dashboards in week 9 without our help.
- Mobile app. Salesforce ships native iOS + Android. We shipped a PWA. 4 of 240 agents complained; the other 236 were happy not to install another app on a 32 GB phone.
The features we deliberately did not replicate were Einstein lead scoring (their data was too thin to train it), the marketing-cloud handoff (handled fine by Mailchimp + a webhook), and territory management (Salesforce's territory model is built for US sales orgs with quotas and is the wrong shape for Indian residential brokerage).
## The Lead-Distribution Engine (The Hard Part)
Lead distribution is where Indian brokerages make or lose money. A fresh Magicbricks lead has roughly 9 minutes of conversion warmth before the buyer has called the next three brokers in their search. The CRM has to route to the right agent in seconds, not minutes.
The engine has 4 inputs and 1 output:
1
Lead source + project mapping
Each ad source feeds a different campaign which maps to a primary project. A Prestige Falcon City lead from Magicbricks goes only to the 18 agents tagged for that project. We store this as a many-to-many agent_projects table refreshed nightly.
2
Agent availability + load balance
An agent who already holds 24 active leads gets skipped. An agent on leave gets skipped. We compute a real-time load score per agent every 60 seconds and store it in Redis with a 90-second TTL.
3
Conversion-rate weighting
An agent who closes 12% of allocated leads gets ranked above one who closes 4%. Computed weekly from the previous 30 days. The ops manager has a manual override for new joiners.
4
SLA timer + reassignment
Allocated lead must be contacted within 7 minutes. After 7 minutes with no logged call, the lead reassigns to the next-best agent and the original agent loses one weekly point. Hard rule, no override.
This sub-system alone took 2.4 weeks to build and is the single most cited reason adoption flipped from 41% on Salesforce to 88% on our system in week 6. Agents got better leads faster, and the visible reassignment rule killed the "I will call back later" habit that had been costing ~₹1.8 cr/quarter in lost-lead opportunity cost.
## The 11-Week Plan (Copy This for a Mid-Size CRM Build)
Weeks 1-2: Discovery — interview 24 agents (3 per office + 3 from HQ), shadow 6 agents in the field, map the existing Salesforce object model, document every WhatsApp template currently in use
Weeks 3-4: Schema + RLS policy design, build out the lead and project models, ship the agent login + lead inbox
Week 8: Builder-incentive table (the killer feature for the brokerage's commission model), nightly recompute job
Week 9: Reporting via Metabase, ops-manager training, 24-agent UAT cohort
Week 10: Bulk import of 47,000 historical leads from Salesforce export, dedupe, sanity-check
Week 11: City-by-city cutover starting Bengaluru (HQ), then Pune, Hyderabad, Mumbai, Gurgaon, Noida, Ahmedabad on consecutive days. Salesforce read-only for 30 days as a rollback hedge.
The cutover sequence mattered. We started at HQ because the founder and ops manager were in the room and could fix issues live. The last office (Ahmedabad) cut over on a Saturday because their busiest day is Sunday and we wanted the team rested for the live test.
## What Broke in Week 4 (And What We Learned)
The first version of the lead-distribution engine had an off-by-one bug in the load-balance calculation. The result: one agent in Hyderabad got 38 leads in 6 hours; the other 21 Hyderabad agents got 4 each. That agent quit by Friday. We rebuilt the engine over the weekend with property-based tests in [fast-check](https://github.com/dubzzz/fast-check) — generating 1,000 random load distributions and asserting that the standard deviation across agents stayed below a threshold. Ten months later we have not had a similar incident.
The lesson, written on the office wall: property-based tests on the math that decides who gets paid, every time.
## The Real Numbers After 6 Months
88%
Weekly Active Agents (was 41%)
2.7 min
Median Lead-to-Contact Time (was 14)
+19%
Lead-to-Site-Visit Conversion
Rs 84L
Year-1 Savings vs Salesforce Quote
The conversion lift (+19%) is the number the founder cared about. At an average ticket size of ₹62 lakh and a brokerage rate of 1.4%, every additional 100 closed deals is roughly ₹87 lakh in commission revenue. The CRM paid for itself in 7 months on conversion alone, before counting the licence saving.
## Common Mistakes (Each One Hurts)
Symptom: "Agents reverted to WhatsApp." Cause: the new system added clicks for the most-frequent action. Fix: profile every action by frequency. The "log call" button must be one tap, not three. We measured this with a stopwatch on day 30 — every 100ms of added friction lost ~3% of usage.
Symptom: "Reports do not match Salesforce numbers." Cause: lead-stage definitions silently differ. Fix: lock the stage definitions in week 1 with the ops manager, get them signed off in writing. Carry exactly the same definitions into the new system.
Symptom: "City office X says 'we never did it that way'." Cause: Salesforce had drifted into 7 different workflows across 7 offices. Fix: pick one workflow as the canonical, tell the other 6 they have 30 days to adapt or to make a documented exception. Documented exceptions become future feature requests.
Symptom: "Builder-incentive table is wrong this month." Cause: the recompute job ran on stale FX or stale incentive PDFs. Fix: source the incentive PDFs from a controlled folder, alert when older than 35 days, run the job after the upload, never on a fixed cron alone.
Symptom: "Agents see leads they should not." Cause: RLS policy missed a join path. Fix: write a security regression test for every role × every endpoint combo. We have 246 such tests; they run in 9 seconds; they have caught 4 bugs in production-bound PRs.
## When Not to Build a Custom CRM
Skip the custom build if (a) you are under 40 agents and your sales motion fits a vanilla CRM (Pipedrive, Zoho, HubSpot starter) — the build cost will not pay back, (b) you have no in-house tech sponsor — every custom CRM needs an internal product owner, not just a vendor, or (c) you are pre-product-market-fit and your sales process is still changing weekly. Custom CRMs reward stable processes; they punish fluid ones.
For more depth on the trade-off, our team also published a [240-agent real-estate CRM saying-no-to-Salesforce post earlier in 2025](/blog/custom-crm-real-estate-240-agents-no-to-salesforce) on a different brokerage with a similar size — the architectural patterns transfer almost directly. Compare and contrast: that post leaned heavier on commission-tracking; this one focused on multi-city distribution.
The honest caveat: we replicated 84% of what the brokerage was actually using in Salesforce. The remaining 16% (Einstein scoring, marketing-cloud, territory model) we either deliberately cut or rebuilt as a leaner equivalent. Custom CRMs are great when you know which 16% to drop. They are dangerous when you try to rebuild every Salesforce feature you have ever heard of.
## The Stack Cost Per Month (Real Numbers)
| Component | Monthly cost | Notes |
|---|---|---|
| Hetzner CCX33 (8 vCPU / 32 GB RAM) | Rs 6,400 | Primary app + Postgres |
| Hetzner CCX23 (read replica + Metabase) | Rs 3,800 | Reporting workload isolated |
| Backup S3 (R2) | Rs 1,200 | 38 GB hot + 14 days retention |
| Twilio SMS | Rs 18,000 | ~14,000 SMS/month |
| Gupshup WhatsApp | Rs 22,000 | ~9,400 conversations/month |
| Cloudflare (CDN + WAF) | Rs 0 | Free tier sufficient |
| Sentry (errors) | Rs 2,800 | Team plan |
| GitHub + 1Password | Rs 4,200 | Team licences |
| Engineering retainer (0.4 FTE) | Rs 9,600 | 16 hrs/month, post-handover |
| Total | ~Rs 68,000 | vs Rs 27.6 lakh/month for Salesforce alone |
The retainer covers backlog work, monthly check-ins, and emergency paging. Beyond month 18, the brokerage will hire one in-house developer at ~₹14 lakh CTC and we step down to advisory.
## A Detail That Saved Us On Day 67
On the morning of day 67 (post-cutover, week 1), the Magicbricks webhook started returning 422s. Their API had silently changed the budget_min field from a string to an integer. Our Zod validator caught it on the very first failed request and quarantined the lead in a quarantine_inbox table rather than dropping it. The ops manager spotted the queue at 11:14 am, we shipped a 4-line patch by 11:38, and replayed 89 quarantined leads. Salesforce's vanilla webhook handler would have dropped them silently. The lesson: quarantine, never drop, when the schema is owned by someone else.
## FAQ
### How long would Salesforce have taken to ship the same functionality?
The previous SI partner's quote was 22 weeks for the remaining 62% of Salesforce work. We delivered the equivalent (and more, including the lead-distribution engine they had not started) in 11. The gap is partly Salesforce's learning curve, partly the fact that custom code on a familiar stack moves faster than configuration in a foreign DSL.
### Is row-level security in Postgres production-grade?
Yes. RLS shipped in Postgres 9.5 (2016) and is used in production by Supabase, GitLab, and many SaaS multi-tenant apps. The honest caveat: write security regression tests. RLS policies that pass a code review can still leak when a JOIN goes through an unexpected path.
### Why Hetzner instead of AWS?
Cost. Hetzner CCX33 at ₹6,400/month is roughly equivalent to a c6a.2xlarge on AWS at ~₹38,000/month. For a workload that does not need AWS-specific managed services (RDS, EKS, etc.), the savings compound. We have run 11 client workloads on Hetzner with zero outages in the last 18 months. The risk: Hetzner's Mumbai region does not exist; latency from Bengaluru is 142ms which is acceptable for an internal CRM but would not be for a consumer-facing app.
### What did the brokerage do with the saved budget?
Two hires: a full-stack engineer (₹14 lakh CTC) and a data analyst (₹9 lakh CTC). The data analyst now owns the Metabase dashboards and has shipped 8 new reports. The engineer is building Phase 2 features (a builder-portal for inventory updates and a buyer-side chatbot).
### How did you handle data migration from Salesforce?
Salesforce's [Data Loader](https://developer.salesforce.com/tools/data-loader) for the bulk export. We exported all 47,000 leads, all 18,000 contacts, all 9,200 opportunities, and the activity history. The dedupe pass on the Postgres side caught 4,180 duplicates that had accumulated in Salesforce over 14 months — the brokerage's CFO sent a thank-you note for that alone.
### Did you consider Zoho CRM as a middle ground?
Yes. Zoho CRM Enterprise at ~₹3,200/user/month for 240 users would have been ~₹92 lakh/year — better than Salesforce but still 13x our run cost. The deeper issue with Zoho was the lead-distribution engine; their built-in round-robin would not handle the 4-input rule we needed without significant custom Deluge scripting. At that point the cost gap to a custom build closed.
### What is your handover process?
Three things. First, every line of code in our GitHub org with the brokerage as a co-maintainer. Second, a 90-page run-book covering deploys, rollback, incident response, secrets rotation, and the lead-distribution engine's invariants. Third, two paid weeks of pair-programming with their incoming full-stack hire. After 18 months the brokerage has zero engineering dependency on us.
### What is your team composition for a build like this?
Two senior full-stack engineers (one owning the lead-distribution engine, one owning the agent-facing UX), one mid-level engineer (integrations + reporting), one DevOps engineer at 0.4 FTE, one designer at 0.3 FTE, and our founder [Vivek](/team/vivek-kumar) at 0.2 FTE for client-side direction. Total: 3.9 FTE for 11 weeks.
### Where can I read about similar projects?
Our [Bricklin CRM build](/projects/bricklin) is a 2017-era brick manufacturer CRM with similar shape (multi-location, custom domain model, MongoDB instead of Postgres). For a more recent build, see our [Oasis Manors senior-care CRM](/projects/oasis-manors). Both are smaller in scale than this brokerage but show the architectural pattern.
## Related Reading
Founder commentary on building software for Indian SMBs from Vivek Singh (our founder)
Need a CRM for Your Sales-Heavy Team?
We ship custom CRMs for Indian SMBs with 50-500 sales users in 10-14 weeks, fixed price, with row-level security and a lead-distribution engine that actually works. Typical project: ₹28-65 lakh. First call is technical, with the engineer who would lead your build. No slides — just your sales process and our honest take.