Round-robin route HubSpot leads and notify reps in Slack using n8n

medium complexityCost: $0-24/moRecommended

Prerequisites

Prerequisites
  • n8n instance (cloud or self-hosted)
  • HubSpot private app token with crm.objects.contacts.read, crm.objects.contacts.write, and settings.users.read scopes
  • Slack app with Bot Token (chat:write, users:read scopes)
  • n8n credentials configured for both HubSpot and Slack
  • A mapping of HubSpot owner IDs to Slack user IDs

Step 1: Add a HubSpot Trigger node

Create a new workflow and add a HubSpot Trigger node:

  • Authentication: Select your HubSpot credential
  • Event: Contact Created

This fires every time a new contact is created in HubSpot. The payload includes the contact ID.

Step 2: Fetch contact details

Add an HTTP Request node:

  • Method: GET
  • URL: https://api.hubapi.com/crm/v3/objects/contacts/{{ $json.objectId }}
  • Authentication: Predefined -> HubSpot API
  • Query params: properties=firstname,lastname,email,company,jobtitle,hubspot_owner_id
Skip already-assigned contacts

If the contact already has a hubspot_owner_id, it was assigned by another workflow or manually. Add an IF node to check $json.properties.hubspot_owner_id is empty before proceeding.

Step 3: Round-robin assignment with a Code node

Add a Code node. n8n's static data persists across executions, making it perfect for tracking the round-robin counter:

// Rep roster: HubSpot owner ID → Slack user ID
const reps = [
  { name: "Alice", hubspotOwnerId: "12345678", slackUserId: "U01AAAA" },
  { name: "Bob",   hubspotOwnerId: "23456789", slackUserId: "U02BBBB" },
  { name: "Carol", hubspotOwnerId: "34567890", slackUserId: "U03CCCC" },
  { name: "Dave",  hubspotOwnerId: "45678901", slackUserId: "U04DDDD" },
];
 
// Get and increment the counter using n8n static data
const staticData = $getWorkflowStaticData('global');
const currentIndex = staticData.roundRobinIndex || 0;
const assignedRep = reps[currentIndex % reps.length];
staticData.roundRobinIndex = (currentIndex + 1) % reps.length;
 
const contact = $('Fetch Contact').first().json;
 
return [{
  json: {
    contactId: contact.id,
    contactName: `${contact.properties.firstname || ''} ${contact.properties.lastname || ''}`.trim(),
    contactEmail: contact.properties.email,
    company: contact.properties.company,
    title: contact.properties.jobtitle,
    assignedRep: assignedRep,
  }
}];
Static data persistence

n8n's $getWorkflowStaticData('global') survives across executions but resets if you re-deploy the workflow from scratch. If the counter resets, the worst case is one round of uneven distribution before it balances out.

Step 4: Update contact owner in HubSpot

Add an HTTP Request node:

  • Method: PATCH
  • URL: https://api.hubapi.com/crm/v3/objects/contacts/{{ $json.contactId }}
  • Body:
{
  "properties": {
    "hubspot_owner_id": "{{ $json.assignedRep.hubspotOwnerId }}"
  }
}

Step 5: Send Slack DM to the assigned rep

Add a Slack node:

  • Resource: Message
  • Operation: Send
  • Channel: {{ $json.assignedRep.slackUserId }} (DM by user ID)
  • Message Type: Block Kit
{
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "🆕 *New Lead Assigned to You*\n*{{ $json.contactName }}* — {{ $json.title || 'No title' }} at {{ $json.company || 'Unknown company' }}\nEmail: {{ $json.contactEmail }}"
      }
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": { "type": "plain_text", "text": "View in HubSpot" },
          "url": "https://app.hubspot.com/contacts/YOUR_PORTAL_ID/contact/{{ $json.contactId }}"
        }
      ]
    }
  ]
}
DM via user ID

To send a Slack DM, use the user ID (e.g., U01AAAA) as the channel value. The bot must have chat:write scope and the user must be in the workspace.

Step 6: Activate

  1. Click Execute Workflow to test with a new contact
  2. Verify the contact's owner was set in HubSpot and the Slack DM arrived
  3. Toggle the workflow to Active

Cost

  • n8n Cloud Starter: $24/mo for 2,500 executions. Each new lead = 1 execution.
  • Self-hosted: Free. Unlimited executions.

Need help implementing this?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.