Founder & Lead Engineer, RAITHub
Real estate lead routing gives every new inquiry to exactly one agent by rule: a returning contact's own agent first, then the listing agent, then an agent covering that ZIP code or suburb, then round-robin, with an SLA (a response deadline, say 5 minutes) that reassigns ignored leads. US CRMs already sell this. Build it when your rules or multi-agency setup don't fit.
This guide is for brokerage owners, team leads and PropTech founders in the US, UK and Australia who are losing leads between the website form and the agent's phone. It covers the rule order, fairness, SLAs, a TypeScript sample, and testing. BlockEstate, the multi-tenant listing and inquiry platform RAITHub built, has lead routing at its core; its MVP shipped in 6 weeks. BlockEstate has no MLS integration, and nothing here depends on one.
What is real estate lead routing, and why does it matter so much?
Lead routing is the rule set that decides which agent owns a new inquiry, and what happens if that agent does not respond. It matters because the inquiry is the moment of highest intent: a buyer who asks about a listing and hears nothing will ask someone else.
Most lost leads are not lost to a competitor's pitch. They are lost to process gaps that routing is supposed to close:
- Nobody owns it. The inquiry lands in a shared inbox and everyone assumes someone else replied.
- The wrong person owns it. A rental inquiry goes to a sales agent, or a Spanish-speaking buyer to an agent who does not speak Spanish.
- The owner is away. The listing agent is on holiday and the lead sits for three days.
- Nobody can say why. When a lead is lost, the team lead cannot see who had it, when, or by which rule.
Good routing fixes all four: one owner, the right owner, a deadline, and a record.
Should I buy lead routing in a CRM or build my own?
Buy first. Established real estate CRMs include routing, and for a single brokerage with standard rules that is enough. As one published example, Follow Up Boss lists round robin lead distribution and lead distribution based on price and ZIP code on all three of its plans, from $69 per user a month on Grow (Follow Up Boss pricing, checked 30 September 2026). Other products offer similar features at other prices.
| Your situation | Usually the better fit | Why |
|---|---|---|
| One brokerage, routing by ZIP code, price band and round-robin | Buy a CRM | These rules are standard features; configuring beats building |
| You already use a CRM and one rule is missing | A small routing service beside the CRM, through its API | You build only the missing rule, not a CRM |
| A platform hosting many agencies that must never see each other's leads | Build | Routing has to respect tenant isolation, which is your architecture, not a setting |
| Rules based on your own data: agent capacity, conversion history, off-plan project allocation | Build the routing layer | The inputs live in your system, not the CRM's |
| You are selling a lead or listing product to agents | Build | Routing is part of the product you sell |
If you are still deciding whether you need a CRM at all, real estate CRM for agents: build vs buy covers the three-year cost of a subscription and the order to build a CRM in. This post goes one level deeper, into the routing engine itself.
What routing rules do real estate teams actually use?
Most teams use the same five rules in the same order, from most specific to least. Each rule either picks an agent or passes the lead to the next one.
| Order | Rule | Picks | Why it comes here |
|---|---|---|---|
| 1 | Existing owner | The agent already working with this contact | A returning buyer should not meet a stranger; it also stops agents poaching each other's contacts |
| 2 | Listing agent | The agent on the listing the inquiry came from | They know the property and the seller expects them to handle it |
| 3 | Territory | Agents who cover the ZIP code, postcode or suburb | Local knowledge converts; a pool, not one person |
| 4 | Language or specialism | Narrows the territory pool: language, rentals vs sales, commercial | A filter on the pool, not a rule of its own, so it never leaves a lead unassigned |
| 5 | Round-robin fallback | The next available agent in the office | Every lead gets an owner, even from an uncovered area |
| Last | Pond | No agent; a shared queue a manager watches | When nobody is available, the lead is visible, not silently dropped |
Two design choices make this work in practice. First, availability is checked at every rule: an agent who is off, on leave or at capacity is skipped, so the listing agent's holiday sends the lead down to territory instead of into a void. Second, the rule that fired is saved with the assignment. "Why did this lead go to that agent?" is the first question a team lead asks, and the answer should be a column, not a guess.
How do I build round-robin lead assignment that stays fair?
Pick the available agent with the lowest load relative to their share, break ties by who was assigned longest ago, and write the assignment in the same database transaction that read the load. A plain "next in the list" pointer drifts the moment agents go on leave or two leads arrive together.
- Weighted shares. A senior agent may take double the leads, a new agent half. Store a weight per agent and compare leads-per-weight, not raw counts.
- A load window. Count assignments today or this week, not all time, so an agent back from two weeks' leave is not buried in every new lead until they "catch up".
- Deterministic ties. When loads match, the agent assigned longest ago wins, then agent ID. Deterministic means testable.
- Concurrency. Two leads arriving in the same second must not both see the same agent as "next". Lock the pool's agent rows inside the transaction, or use PostgreSQL's
FOR UPDATE SKIP LOCKED, which the PostgreSQL documentation describes as a way "to avoid lock contention with multiple consumers accessing a queue-like table" (PostgreSQL SELECT documentation). The SQL version of that pattern is in the real estate CRM post.
How do territory rules work for ZIP codes or suburbs?
Map each agent to the areas they cover, then filter the pool by the lead's area before round-robin runs. Keep the area on the listing, not typed by the buyer, whenever the inquiry comes from a listing page.
- Use codes, not names. ZIP codes in the US, postcodes in the UK and Australia. Suburb names are spelled five ways; codes are not.
- Allow overlap. Two agents covering the same postcode is normal. The territory rule returns a pool, and round-robin picks inside it.
- Plan for no match. A lead from an area nobody covers falls through to office round-robin. Report those leads weekly: they show you where to hire.
- Price bands are territories too. "Luxury over $2 million" is just another filter on the pool, and belongs in the same place as geography.
- Polygons can wait. Drawing territories on a map is appealing but rarely needed in a first release; a list of codes per agent covers most brokerages.
How do I enforce a response SLA and reassign ignored leads?
Record when each lead was assigned and when the agent first responded, then run a scheduled job every minute that reassigns any lead past its deadline to the next agent by the same rules, excluding the agent who missed it. After a set number of hops, send it to the manager's pond instead of round-robining forever.
The details decide whether agents trust it:
- Define "responded". A logged call, a sent reply or a status change, recorded by the system. Opening the lead is not a response.
- Respect working hours. A lead at 23:00 should not be reassigned three times before morning. Check the SLA only during the agent's working hours, in the agent's own time zone, using an IANA zone such as
America/New_YorkorAustralia/Sydneywith the standardIntl.DateTimeFormatAPI (MDN Intl.DateTimeFormat). - Cap the hops. Two reassignments, then the pond and an alert to the team lead. A lead bouncing between six agents is worse than one that waits for a manager.
- Tell the agent first. A warning at 80% of the deadline gives the owner a chance to act before the lead moves.
- Make the job safe to run twice. Scheduled jobs overlap and retry. Reassign only if the assignment you read is still the current one, inside a transaction.
What does lead routing look like in TypeScript?
A pure function that takes the lead and the candidate agents and returns an agent plus the rule that fired, and a second function that decides whether an assignment has breached its SLA. Pure functions are easy to test; the database transaction wraps them. This sample was written for this post as a minimal illustration. It is not BlockEstate's code, which belongs to its owner.
type Agent = {
id: string
available: boolean // on shift, not on leave, under capacity
areas: string[] // ZIP codes or postcodes covered
languages: string[]
weight: number // 1 = normal share, 2 = double share
assignedToday: number
lastAssignedAt: number // epoch ms, 0 if never
timeZone: string // IANA zone, e.g. 'America/New_York'
}
type Lead = {
id: string
area: string
language?: string
listingAgentId?: string
ownerAgentId?: string // agent already working with this contact
}
type Decision = { agentId: string | null; rule: string }
export function routeLead(lead: Lead, agents: Agent[], exclude: string[] = []): Decision {
const pool0 = agents.filter((a) => a.available && a.weight > 0 && !exclude.includes(a.id))
const find = (id?: string) => pool0.find((a) => a.id === id)
const owner = find(lead.ownerAgentId)
if (owner) return { agentId: owner.id, rule: 'existing_owner' }
const listing = find(lead.listingAgentId)
if (listing) return { agentId: listing.id, rule: 'listing_agent' }
let pool = pool0.filter((a) => a.areas.includes(lead.area))
let rule = 'territory_round_robin'
const lang = lead.language
if (lang) {
const speakers = pool.filter((a) => a.languages.includes(lang))
if (speakers.length) pool = speakers
}
if (!pool.length) {
pool = pool0
rule = 'office_round_robin'
}
if (!pool.length) return { agentId: null, rule: 'pond' }
// Weighted round-robin: lowest load per unit of weight wins,
// then whoever was assigned longest ago, then ID (deterministic).
const next = [...pool].sort(
(a, b) =>
a.assignedToday / a.weight - b.assignedToday / b.weight ||
a.lastAssignedAt - b.lastAssignedAt ||
a.id.localeCompare(b.id),
)[0]
return { agentId: next.id, rule }
}
const SLA_MS = 5 * 60 * 1000
function localHour(now: Date, timeZone: string): number {
const h = new Intl.DateTimeFormat('en-US', { timeZone, hour: 'numeric', hourCycle: 'h23' }).format(now)
return Number(h)
}
export function slaBreached(assignedAt: Date, respondedAt: Date | null, now: Date, agent: Agent): boolean {
if (respondedAt) return false
const hour = localHour(now, agent.timeZone)
const workingHours = hour >= 8 && hour < 20
return workingHours && now.getTime() - assignedAt.getTime() > SLA_MS
}
How it fits together: the inquiry handler opens a transaction, locks the candidate agents' rows, calls routeLead, inserts the assignment with its rule, and increments the agent's counter before committing. The every-minute job reads open assignments, calls slaBreached, and for each breach calls routeLead again with the previous agents in exclude, until the hop cap sends the lead to the pond.
One honest limitation. This slaBreached only pauses the check outside working hours; a lead assigned at 19:58 will breach at 08:00 the next morning, because the elapsed time includes the night. A production version counts business minutes only, and handles public holidays per office. That is exactly the kind of rule worth a test of its own.
How do you test lead routing before agents rely on it?
Unit-test every rule with fixed data and a fixed clock, then test concurrency and the SLA job against a real database. Routing bugs are quiet: nothing crashes, a lead just goes to the wrong person, and agents stop trusting the system.
- One test per rule. Existing owner beats listing agent; an unavailable listing agent falls through to territory; a language filter that matches nobody keeps the territory pool.
- Fairness over many leads. Route 1,000 synthetic leads to agents weighted 1, 1 and 2, and assert the split is 250, 250 and 500.
- Injected time. Pass
nowin, never call the clock inside the rule. Then test 07:59, 08:00, 19:59 and 20:00 in the agent's zone, including a daylight-saving change. - Concurrency. Fire 20 leads at once at a pool of 4 agents, against a real PostgreSQL, and assert no agent got more than their share.
- Idempotency. Run the SLA job twice on the same data and assert each lead moved at most once.
- Tenant isolation. On a multi-agency platform, assert an agency's lead can never be routed to another agency's agent, whatever the rules say.
This is the same QA-first method behind the platforms RAITHub has built; PropDesk, the property management platform, carries 1,024 automated tests. The method is set out in how RAITHub tests software.
What should you record for every lead assignment?
Record who got the lead, when, by which rule, who had it before, and when they first responded. That history answers disputes between agents, feeds speed-to-lead reports, and becomes the training data if you ever want lead scoring.
| Field | Example | Used for |
|---|---|---|
| lead_id, agent_id | The pair | Ownership |
| rule | listing_agent, territory_round_robin, pond | "Why did it go there?" |
| assigned_at, responded_at | Timestamps in UTC | Response time and SLA reports |
| previous_agent_id, hop | The agent who missed the SLA; 0, 1, 2 | Reassignment history and coaching |
| source | Listing page, portal, form, phone | Which channels produce leads worth paying for |
| outcome | Viewing booked, lost, closed | Conversion by rule, source and agent |
Make the table append-only: a reassignment is a new row, not an update. The pattern is in how to design a SaaS audit log.
How does lead routing work on a platform with many agencies?
Routing runs inside one agency at a time, and the agency boundary is enforced in the database, not in the routing code. Every lead and agent carries an agency ID, and the candidate query can only ever return that agency's agents.
This is where BlockEstate is the relevant proof. It is a multi-tenant platform for agents and brokerages with listing intake, agent dashboards, lead routing and a document workflow, with each brokerage isolated as a tenant (BlockEstate case study). The general patterns for enforcing that boundary are in the PostgreSQL row-level security guide, and the role side, who may reassign whose leads, is in SaaS authorisation and RBAC design.
Each agency also needs its own configuration: its SLA, its working hours and time zone, its territories and its hop cap. Treat them as data the agency admin edits, not constants in code.
Why RAITHub for this
- Lead routing has been built and shipped. BlockEstate's core is inquiry capture, lead routing, agent dashboards and a document workflow on a multi-tenant architecture, and its MVP shipped in 6 weeks on Next.js, TypeScript, PostgreSQL and Prisma.
- Routing is treated as money logic. Rules, fairness, clocks and concurrency get their own tests and a CI gate, the same standard as rent collection on PropDesk.
- Straight answers on MLS. RAITHub has no shipped MLS or IDX integration, and says so. Routing works on inquiries from your own listings, forms and portals whatever the listing source; the access question is covered in the MLS and IDX integration guide.
- Working hours that reach you. RAITHub works from Dhaka (UTC+6). For US and Canada clients there is a daily 2-hour evening overlap, Dhaka 19:00 to 21:00, which is US East mornings; UK teams get about 3 hours (4 during British Summer Time), and Sydney about 5 (4 during daylight saving). The working week is agreed per client, and everything else runs on written daily handoffs.
- Fixed scope. A free 15-minute technical audit, then a written fixed quote. You own the code, and an NDA is standard.
When you don't need us
- Your rules are ZIP code, price band and round-robin. A CRM you can buy does that today. Configure it.
- You have fewer than about five agents. A shared inbox with one named owner per day may be all the routing you need.
- The real problem is follow-up, not assignment. If leads reach the right agent and still go cold, you need drip campaigns and coaching, which bought CRMs do well.
- You need a native mobile app for agents. RAITHub builds responsive web applications and does not offer mobile app development.
- You want developers placed in your team by the hour. RAITHub offers fixed-scope builds and dedicated teams, not staff augmentation.
If you are building a platform where routing is part of the product, the SaaS development service covers multi-tenant builds, and the real estate industry page sets out what RAITHub builds for PropTech. Book the free 15-minute technical audit with your routing rules written as a list, where your leads come from, and the case your current tool gets wrong.
Last reviewed: 30 September 2026. Vendor features checked on 30 September 2026.
Frequently asked questions
What is lead routing in real estate?
Lead routing is the set of rules that assigns each new inquiry to one agent, and reassigns it if that agent does not respond in time. Typical rules, in order, are existing owner, listing agent, territory, language and round-robin, with a shared pond when nobody is available.
How does round-robin lead distribution work?
Each new lead goes to the available agent whose turn it is. A fair version compares each agent's recent load against their weight, breaks ties by who was assigned longest ago, and skips agents who are off or at capacity, so leave and busy days do not skew the split.
What is a good response SLA for real estate leads?
Set it by channel and by what your agents can actually meet. Many teams use minutes for web inquiries during working hours and pause the clock overnight. Whatever the number, measure first response as a logged action, warn the agent before the deadline, and cap the number of reassignments.
Can my CRM already do lead routing?
Probably. Established real estate CRMs include routing; Follow Up Boss, for example, lists round robin and price and ZIP code distribution on all its plans. Build your own only when your rules depend on your own data or your platform hosts several agencies.
How do you stop two leads going to the same agent at once?
Do the selection and the write in one database transaction, and lock the candidate agents' rows while you choose. In PostgreSQL, SELECT with FOR UPDATE, or FOR UPDATE SKIP LOCKED for queue-like tables, prevents two concurrent requests from both picking the same "next" agent.
Does lead routing need MLS or IDX data?
No. Routing works on inquiries from your own listings, forms and portals. MLS or IDX access matters only if listings come from an MLS feed, and that access is granted by the MLS. RAITHub has no shipped MLS integration.
How long does it take to build lead routing?
As part of a focused first release, weeks rather than months: BlockEstate's MVP, which included lead routing, agent dashboards and a document workflow, shipped in 6 weeks. A routing service beside an existing CRM is smaller again. RAITHub quotes it as fixed scope after a free 15-minute audit.
Related posts
Ready to discuss your project?
Book a free 15-minute technical audit with our engineering team.