Lead Routing
Lead routing is the CRM logic that assigns inbound leads to the right rep, queue, or workflow. Done well, it cuts speed-to-first-contact to under a minute and keeps territory and capacity rules fair under load.
What lead routing is
Lead routing is the assignment logic in a CRM that decides which sales rep, account team, or queue receives a new lead. It runs the moment a lead enters the system — from a form submission, a chat, a partner referral, or a marketing event — and it controls how fast the lead becomes a real conversation.
Speed-to-lead is the metric every revenue team tracks here. Studies consistently show conversion drops sharply if the first response takes longer than a few minutes. A well-engineered routing layer keeps the assignment-and-notification path under a minute end-to-end.
Routing patterns
The standard production patterns are:
- Round-robin. Each new lead goes to the next rep in a sorted list. Simple, fair on average, blind to capacity.
- Round-robin with capacity. Skip reps who are over their lead cap or out-of-office. Adds fairness without sacrificing simplicity.
- Territory routing. Assign by region, country, or named account list. Common for outbound-heavy teams with named territories.
- Account-based routing. Match the lead’s company against the account-mapping table. If the account already has an owner, route to them. If not, run the next rule.
- Score-thresholded routing. Use a lead score from a CDP or model to pick a queue (high score → AE, low score → SDR/MQL queue, no score → nurture).
Real businesses combine these. A typical production rule chain looks like: matched-account → territory → score → round-robin within the matching pod. The studio’s CRM development practice treats this rule chain as the heart of the routing service.
Implementation patterns
Three things matter most in a production routing service:
- Make the rule chain explicit. Configurable in code or data, not hidden in a workflow tool’s GUI. Auditable per-lead.
- Run the chain idempotently. A retry must not double-assign. Same input, same outcome.
- Emit an audit event for every assignment. Who got the lead, why, what rule fired, and when. This is what lets ops debug “why did this lead go to me.”
A subtle but important pattern: route asynchronously, but commit synchronously. The form submit returns 200 the moment the lead is persisted; the assignment happens in a worker that completes within seconds. This decouples form latency from rule complexity.
Code-shape example
A clean rule-chain implementation:
interface Lead {
id: string;
email: string;
companyDomain: string | null;
countryCode: string | null;
source: string;
score: number | null;
receivedAt: string;
}
interface RoutingResult {
ownerId: string;
rule: string;
reason: string;
}
interface RoutingRule {
name: string;
match: (lead: Lead) => Promise<RoutingResult | null>;
}
const matchedAccount: RoutingRule = {
name: "matched_account",
async match(lead) {
if (!lead.companyDomain) return null;
const account = await accounts.findByDomain(lead.companyDomain);
if (!account?.ownerId) return null;
return {
ownerId: account.ownerId,
rule: "matched_account",
reason: `domain ${lead.companyDomain} belongs to account ${account.id}`,
};
},
};
const byTerritory: RoutingRule = {
name: "territory",
async match(lead) {
if (!lead.countryCode) return null;
const territory = await territories.findByCountry(lead.countryCode);
if (!territory) return null;
const owner = await territory.pickOwner(); // round-robin within the pod
return owner
? { ownerId: owner.id, rule: "territory", reason: territory.name }
: null;
},
};
const byScore: RoutingRule = { /* ... */ } as RoutingRule;
const fallback: RoutingRule = { /* round-robin across all reps */ } as RoutingRule;
export async function routeLead(lead: Lead): Promise<RoutingResult> {
for (const rule of [matchedAccount, byTerritory, byScore, fallback]) {
const result = await rule.match(lead);
if (result) {
await audit.log({
leadId: lead.id,
rule: result.rule,
ownerId: result.ownerId,
reason: result.reason,
occurredAt: new Date().toISOString(),
});
return result;
}
}
throw new Error("no rule matched (impossible if fallback is configured)");
}
Every rule is its own function, the chain is data, and the audit log captures the path. Ops can query “why did lead X go to rep Y” by looking at one row.
How we implement it at Dashhold
On every CRM engagement that includes routing, we ship:
- Rule chain in a single config file (TypeScript or YAML) checked into the repo. Compliance reviewable.
- Audit log in a dedicated table. Every assignment plus every retry plus every failed match.
- Idempotency on the routing event. The lead’s id is the idempotency key. Re-routing the same lead requires an explicit
force: trueflag. - Ops queue when no rule matches. Instead of a 500, unmatched leads land in an unassigned queue with a reason. A human resolves it; the rule chain learns from the pattern.
- Metrics for fairness. Per-rep counts per day, per-pod fairness, time-to-assignment p95. The team tunes the chain based on data.
Common pitfalls
- Rules in the workflow tool’s GUI. Salesforce Process Builder, HubSpot Workflows — these are tempting because they are no-code, but they are nearly impossible to audit and debug. Move rules into code or data.
- No audit log. Without it, the “why did this go to me” question becomes a 30-minute meeting.
- Synchronous rule chains in the form-submit handler. The form gets slow as the chain gets complex. Decouple persist + route.
- No capacity check. Round-robin alone gives leads to reps on PTO. Add capacity from day one.
- Re-running the chain on every lead update. Don’t. Once a lead is assigned, only re-route on explicit triggers.
- Silent failures. If no rule matches, the lead vanishes. Always have a fallback queue.
Where lead routing fits
Lead routing is part of every CRM, custom or platform. On Salesforce, it lives in Apex triggers, flow rules, and Lead Assignment Rules. On HubSpot, it lives in workflows. In a custom CRM, it is usually a service behind the form-ingest path. In every case, it is a hot spot for performance and a source of fairness debates inside the revenue team. Get the rule chain explicit and auditable, and most of the heat goes away.
See also
- Custom CRM — when the routing motion is unusual enough to justify a custom build
- Custom CRM vs Salesforce — comparison guide for the platform decision