Route HubSpot leads by territory and company size using Zapier
Prerequisites
- Zapier account (Professional plan or higher for Code by Zapier and Paths)
- HubSpot account connected to Zapier via OAuth
- Slack workspace connected to Zapier
- Enriched company data in HubSpot (state, country, employee count)
Why Zapier?
Zapier's HubSpot trigger fires on new contact creation with near real-time polling (1-2 minutes on Professional, up to 15 minutes on Starter). Paths handle the branching logic — existing account owner vs. new territory assignment — without code, and Code by Zapier handles the territory matching when you need more than simple filters. If your team already uses Zapier for other automations, this keeps everything in one platform.
The main trade-off is cost. Paths and Code by Zapier both require the Professional plan ($29.99/mo minimum), and each routed lead consumes 6-8 tasks. At 100 leads/month, that's 600-800 tasks — within the Professional plan's 2,000 task limit, but high-volume teams may need the Team plan ($69.99/mo, 50,000 tasks).
How it works
- HubSpot trigger fires when a new contact is created
- Get Company step fetches the associated company's data (employee count, state, existing owner)
- Paths branch: if the company already has an owner, assign to that owner; otherwise, apply territory rules
- Code by Zapier implements the three-tier routing hierarchy (enterprise override, territory match, fallback)
- HubSpot action updates the contact's owner
- Slack action DMs the assigned rep
Step 1: Set up the HubSpot trigger
Create a new Zap. Choose HubSpot as the trigger:
- Trigger event: New Contact
- Additional Properties to Retrieve:
firstname,lastname,email,jobtitle,company,state,country,numberofemployees,hubspot_owner_id,associatedcompanyid
HubSpot's Zapier trigger returns contact properties. To get company data (employee count, state), either ensure those fields are synced to the contact record, or add a second step to look up the associated company.
Step 2: Look up the associated company (optional)
If company data isn't on the contact record, add a HubSpot action:
- Action event: Get Company
- Company ID:
{{associatedcompanyid}} - Additional Properties:
numberofemployees,state,country,hubspot_owner_id
Step 3: Check for existing account owner
Add a Paths step with two paths:
Path A: Existing account owner
- Condition: Company
hubspot_owner_id→ Exists - Route to Step 5 (assign contact to existing owner)
Path B: No existing owner
- Condition: Company
hubspot_owner_id→ Does not exist - Continue to territory routing logic
Step 4: Route by territory and size with Code by Zapier
On Path B, add a Code by Zapier step:
- Language: JavaScript
- Input Data:
state->{{state}}(from company or contact)country->{{country}}employees->{{numberofemployees}}
const state = (inputData.state || '').toUpperCase();
const employees = parseInt(inputData.employees || '0');
// Territory mapping
const territories = {
'NY': { ownerId: '111111', slackId: 'U01AAAA', rep: 'Alice' },
'MA': { ownerId: '111111', slackId: 'U01AAAA', rep: 'Alice' },
'CT': { ownerId: '111111', slackId: 'U01AAAA', rep: 'Alice' },
'CA': { ownerId: '222222', slackId: 'U02BBBB', rep: 'Bob' },
'WA': { ownerId: '222222', slackId: 'U02BBBB', rep: 'Bob' },
'FL': { ownerId: '333333', slackId: 'U03CCCC', rep: 'Carol' },
'GA': { ownerId: '333333', slackId: 'U03CCCC', rep: 'Carol' },
'TX': { ownerId: '333333', slackId: 'U03CCCC', rep: 'Carol' },
};
// Enterprise override
const ENTERPRISE_THRESHOLD = 1000;
const enterpriseRep = { ownerId: '444444', slackId: 'U04DDDD', rep: 'Dave (Enterprise)' };
const defaultRep = { ownerId: '555555', slackId: 'U05EEEE', rep: 'Eve (Catch-all)' };
let assigned;
let reason;
if (employees >= ENTERPRISE_THRESHOLD) {
assigned = enterpriseRep;
reason = `Enterprise (${employees} employees)`;
} else if (territories[state]) {
assigned = territories[state];
reason = `Territory: ${state}`;
} else {
assigned = defaultRep;
reason = `Fallback (state: ${state || 'unknown'})`;
}
output = [{
ownerId: assigned.ownerId,
slackId: assigned.slackId,
repName: assigned.rep,
reason: reason,
}];Step 5: Update the contact owner in HubSpot
Add a HubSpot action:
- Action event: Update Contact
- Contact: Use the contact ID from the trigger step
- Contact Owner:
{{ownerId}}(from Path A: company's existing owner, from Path B: Code step output)
Step 6: Send a Slack DM
Add a Slack action:
- Action event: Send Direct Message
- User:
{{slackId}} - Message Text:
🆕 *New Lead Routed to You*
*{{firstname}} {{lastname}}* at {{company}} ({{numberofemployees}} employees)
📍 {{state}}
Routing: {{reason}}
<https://app.hubspot.com/contacts/YOUR_PORTAL_ID/contact/{{hs_object_id}}|View in HubSpot>Step 7: Test and publish
- Test each path with contacts from different territories
- Test with an enterprise-size company to verify the size override
- Test with a contact whose company already has an owner
- Turn the Zap On
Limitations
- Paths require the Professional plan or higher.
- Code by Zapier requires the Professional plan.
- Polling delay: 1-15 minutes depending on plan.
- Complex Zap: This is a 6-8 step Zap, which uses more tasks per execution.
Troubleshooting
Common questions
How many Zapier tasks does each routed lead consume?
Each lead uses 6-8 tasks: trigger (1) + Get Company (1) + Paths (1) + Code by Zapier (1) + Update Contact (1) + Slack DM (1), plus any optional filter steps. At 100 leads/month, that's 600-800 tasks. The Professional plan includes 2,000 tasks/month.
Can I avoid Paths and Code by Zapier to stay on the Starter plan?
Partially. You can replace Paths with a Filter step and use Zapier's built-in field mapping instead of Code by Zapier. But the territory matching logic becomes a chain of Filter + Update Contact steps (one per territory), which is fragile and hard to maintain. For more than 3-4 territories, Code by Zapier is significantly cleaner.
How fast does Zapier's HubSpot trigger fire?
Professional plan: 1-2 minute polling. Team plan: 1-minute polling. Starter plan: up to 15 minutes. For true real-time routing (sub-minute), use the Code approach with a HubSpot webhook.
What happens if the Get Company step fails because there's no associated company?
The Zap will error and stop. Add a Filter step after the trigger to check that associatedcompanyid exists before the Get Company step. For contacts without company associations, route directly to the territory logic using only the contact's self-reported state — or route to a fallback rep for manual triage.
Cost
- Professional plan: $29.99/mo minimum. Each new lead = 6-8 tasks.
- Team plan: $69.99/mo if you need higher task volume or faster polling.
Looking to scale your AI operations?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.