Round-robin route HubSpot leads and notify reps in Slack using an agent skill
low complexityCost: Usage-based
Prerequisites
Prerequisites
- Claude Code or another agent that supports the Agent Skills standard
- HubSpot private app token stored as
HUBSPOT_TOKENenvironment variable - Slack Bot Token stored as
SLACK_BOT_TOKENenvironment variable
Overview
This approach creates an agent skill that redistributes and rebalances lead assignments. Unlike the always-on webhook approaches, this is useful for bulk reassignment -- when a rep leaves, when you add someone new to the team, or when assignments have drifted out of balance.
Step 1: Create the skill
Create .claude/skills/round-robin-leads/SKILL.md:
---
name: round-robin-leads
description: Redistribute unassigned HubSpot leads across reps via round-robin and notify in Slack
disable-model-invocation: true
allowed-tools: Bash(python *)
---
Assign unassigned HubSpot contacts to reps via round-robin and DM each rep in Slack.
Usage:
- `/round-robin-leads` — assign all currently unassigned contacts
- `/round-robin-leads --rebalance` — reassign ALL contacts evenly (use when team changes)
Run: `python $SKILL_DIR/scripts/assign_leads.py $@`Step 2: Write the script
Create .claude/skills/round-robin-leads/scripts/assign_leads.py:
#!/usr/bin/env python3
"""Round-robin assign HubSpot contacts and notify reps via Slack."""
import os, sys, requests
from slack_sdk import WebClient
HUBSPOT_TOKEN = os.environ.get("HUBSPOT_TOKEN")
SLACK_TOKEN = os.environ.get("SLACK_BOT_TOKEN")
if not all([HUBSPOT_TOKEN, SLACK_TOKEN]):
print("ERROR: Set HUBSPOT_TOKEN and SLACK_BOT_TOKEN")
sys.exit(1)
HEADERS = {"Authorization": f"Bearer {HUBSPOT_TOKEN}", "Content-Type": "application/json"}
slack = WebClient(token=SLACK_TOKEN)
REPS = [
{"name": "Alice", "hubspot_owner_id": "12345678", "slack_user_id": "U01AAAA"},
{"name": "Bob", "hubspot_owner_id": "23456789", "slack_user_id": "U02BBBB"},
{"name": "Carol", "hubspot_owner_id": "34567890", "slack_user_id": "U03CCCC"},
{"name": "Dave", "hubspot_owner_id": "45678901", "slack_user_id": "U04DDDD"},
]
rebalance = "--rebalance" in sys.argv
# Fetch contacts
filters = [] if rebalance else [
{"propertyName": "hubspot_owner_id", "operator": "NOT_HAS_PROPERTY"}
]
contacts = []
after = None
while True:
body = {
"properties": ["firstname","lastname","email","company","jobtitle"],
"limit": 100,
}
if filters:
body["filterGroups"] = [{"filters": filters}]
if after:
body["after"] = after
resp = requests.post("https://api.hubapi.com/crm/v3/objects/contacts/search", headers=HEADERS, json=body)
resp.raise_for_status()
data = resp.json()
contacts.extend(data.get("results", []))
after = data.get("paging", {}).get("next", {}).get("after")
if not after:
break
if not contacts:
print("No contacts to assign")
sys.exit(0)
print(f"Assigning {len(contacts)} contacts across {len(REPS)} reps...")
# Round-robin assign
assignments = {r["name"]: [] for r in REPS}
for i, contact in enumerate(contacts):
rep = REPS[i % len(REPS)]
props = contact["properties"]
name = f"{props.get('firstname','')} {props.get('lastname','')}".strip()
requests.patch(
f"https://api.hubapi.com/crm/v3/objects/contacts/{contact['id']}",
headers=HEADERS,
json={"properties": {"hubspot_owner_id": rep["hubspot_owner_id"]}}
).raise_for_status()
assignments[rep["name"]].append(name)
# Notify each rep with their new leads
for rep in REPS:
leads = assignments[rep["name"]]
if not leads:
continue
lead_list = "\n".join(f"• {l}" for l in leads[:20])
if len(leads) > 20:
lead_list += f"\n...and {len(leads) - 20} more"
slack.chat_postMessage(
channel=rep["slack_user_id"],
text=f"You've been assigned {len(leads)} leads",
blocks=[
{"type": "section", "text": {"type": "mrkdwn",
"text": f"📋 *{len(leads)} Leads Assigned to You*\n{lead_list}"}}
]
)
print(f"Done. Assignments: {', '.join(f'{r['name']}: {len(assignments[r['name']])}' for r in REPS)}")Step 3: Run it
# Assign all unassigned contacts
/round-robin-leads
# Rebalance all contacts evenly (after team change)
/round-robin-leads --rebalanceWhen to use this approach
- A rep leaves or joins and you need to rebalance the existing book of business
- You have a backlog of unassigned leads that need bulk routing
- You want a one-time cleanup before turning on an always-on round-robin automation
Need help implementing this?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.