Route Salesforce leads with round-robin using n8n
Prerequisites
- n8n instance (cloud or self-hosted)
- Salesforce Connected App with OAuth credentials (Consumer Key and Consumer Secret)
- Slack app with Bot Token (
chat:writescope) - n8n credentials configured for both Salesforce and Slack
- A mapping of Salesforce User IDs to Slack User IDs
Step 1: Create a Salesforce Connected App
In Salesforce, go to Setup → App Manager → New Connected App:
- Connected App Name:
n8n Lead Router - Enable OAuth Settings: checked
- Callback URL: your n8n OAuth callback URL (shown in n8n's Salesforce credential setup)
- Selected OAuth Scopes:
Full access (full),Perform requests at any time (refresh_token, offline_access)
After saving, copy the Consumer Key and Consumer Secret. In n8n, create a Salesforce credential using these values and complete the OAuth flow.
Salesforce API access requires Enterprise, Unlimited, Developer, or Performance edition. Professional edition users need to purchase the API add-on.
Step 2: Add a Salesforce Trigger node
Create a new workflow and add a Salesforce Trigger node:
- Authentication: Select your Salesforce credential
- Resource: Lead
- Event: Created
- Poll interval: Every minute (or use Outbound Message + Webhook for real-time)
This fires every time a new Lead is created. The payload includes the Lead ID and basic fields.
For instant triggering, create a Salesforce Outbound Message (Workflow Rule or Flow) that sends to an n8n Webhook node. This eliminates the polling delay.
Step 3: Fetch full Lead details
Add an HTTP Request node:
- Method: GET
- URL:
https://YOUR_INSTANCE.salesforce.com/services/data/v59.0/sobjects/Lead/{{ $json.Id }} - Authentication: Predefined → Salesforce OAuth2
- Headers:
Content-Type: application/json
This retrieves the full Lead record including Name, Email, Company, LeadSource, and OwnerId.
Step 4: 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: Salesforce User ID → Slack User ID
const reps = [
{ name: "Alice", sfUserId: "005xx0000012345", slackUserId: "U01AAAA" },
{ name: "Bob", sfUserId: "005xx0000023456", slackUserId: "U02BBBB" },
{ name: "Carol", sfUserId: "005xx0000034567", slackUserId: "U03CCCC" },
{ name: "Dave", sfUserId: "005xx0000045678", 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 lead = $('Fetch Lead').first().json;
return [{
json: {
leadId: lead.Id,
leadName: `${lead.FirstName || ''} ${lead.LastName || ''}`.trim(),
leadEmail: lead.Email,
company: lead.Company,
leadSource: lead.LeadSource,
assignedRep: assignedRep,
}
}];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 5: Update Lead owner in Salesforce
Add an HTTP Request node:
- Method: PATCH
- URL:
https://YOUR_INSTANCE.salesforce.com/services/data/v59.0/sobjects/Lead/{{ $json.leadId }} - Authentication: Predefined → Salesforce OAuth2
- Body (JSON):
{
"OwnerId": "{{ $json.assignedRep.sfUserId }}"
}Step 6: Create a follow-up Task on the Lead
Add another HTTP Request node to create a Task reminding the rep to follow up:
- Method: POST
- URL:
https://YOUR_INSTANCE.salesforce.com/services/data/v59.0/sobjects/Task - Authentication: Predefined → Salesforce OAuth2
- Body (JSON):
{
"Subject": "New lead assigned — review within 5 minutes",
"WhoId": "{{ $json.leadId }}",
"OwnerId": "{{ $json.assignedRep.sfUserId }}",
"Status": "Not Started",
"Priority": "High",
"ActivityDate": "{{ $now.format('yyyy-MM-dd') }}"
}Step 7: 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 Salesforce Lead Assigned to You*\n*{{ $json.leadName }}* — {{ $json.company || 'Unknown company' }}\nEmail: {{ $json.leadEmail || 'N/A' }}\nSource: {{ $json.leadSource || 'N/A' }}"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": { "type": "plain_text", "text": "View in Salesforce" },
"url": "https://YOUR_INSTANCE.lightning.force.com/lightning/r/Lead/{{ $json.leadId }}/view"
}
]
}
]
}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 8: Activate
- Click Execute Workflow to test with a new Lead
- Verify the Lead's OwnerId was updated in Salesforce
- Check that the follow-up Task was created
- Confirm the Slack DM arrived
- Toggle the workflow to Active
To assign leads proportionally (e.g., senior reps get 2x more), modify the Code node. Replace the flat roster with weighted entries — repeat a rep's entry to increase their share of the rotation.
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.