Route Salesforce leads with round-robin using Flow Builder

medium complexityCost: $0 (included)

Prerequisites

Prerequisites
  • Salesforce org with Flow Builder access (Enterprise, Unlimited, or Developer edition)
  • System Administrator profile or "Manage Flows" permission
  • Slack workspace with Salesforce for Slack app installed (for notifications)
  • A list of Salesforce User IDs for your rep rotation

Why Salesforce Flow?

Flow Builder runs natively inside Salesforce with zero external dependencies — no middleware, no polling delays, no extra subscriptions. A before-save Record-Triggered Flow assigns the Lead owner before the record is even committed, which means the rep sees correct ownership from the moment the Lead appears. The trade-off is setup complexity: you need a Custom Setting for the counter, a Custom Metadata Type for the rep roster, and two separate flows (before-save for assignment, after-save for Slack).

How it works

  • Custom Setting stores a persistent round-robin counter that survives across transactions without consuming SOQL queries
  • Custom Metadata Type holds the rep roster (Salesforce User ID, Slack User ID, active/inactive flag) — editable without deployments
  • Before-save Record-Triggered Flow fires on Lead creation, reads the counter, calculates the next rep via MOD formula, sets OwnerId, and increments the counter — all before the record commits
  • After-save Record-Triggered Flow sends a Slack DM to the assigned rep with lead details (HTTP callouts aren't allowed in before-save context)
  • Alternative path — Omni-Channel with a Lead Queue provides workload-based routing out of the box on Enterprise+ editions

Step 1: Create a Custom Setting for the counter

Go to Setup → Custom Settings → New.

  • Label: Round Robin Counter
  • API Name: Round_Robin_Counter__c
  • Setting Type: Hierarchy
  • Visibility: Public

Add a custom field:

  • Field Label: Last Index
  • API Name: Last_Index__c
  • Type: Number (0 decimal places)
  • Default Value: 0

After saving, click Manage and create an org-level default record with Last_Index__c = 0.

Why a Custom Setting?

Custom Settings are accessible in flows without a SOQL query counting against governor limits. They persist across transactions, making them ideal for storing a simple counter.

Step 2: Create a Custom Metadata Type for the rep roster

Go to Setup → Custom Metadata Types → New.

  • Label: Round Robin Rep
  • API Name: Round_Robin_Rep__mdt

Add these fields:

FieldAPI NameType
Rep OrderRep_Order__cNumber (0 decimals)
User IDUser_Id__cText (18 chars)
Slack User IDSlack_User_Id__cText (15 chars)
Is ActiveIs_Active__cCheckbox (default: true)

Create records for each rep in the rotation:

LabelRep OrderUser IDSlack User IDIs Active
Alice Smith0005xx0000012345U01AAAAtrue
Bob Jones1005xx0000023456U02BBBBtrue
Carol Chen2005xx0000034567U03CCCCtrue
Dave Kim3005xx0000045678U04DDDDtrue
Skip reps who are out of office

Uncheck Is_Active__c for reps who are unavailable. The flow will skip inactive reps automatically.

Step 3: Create the before-save flow (assignment)

Go to Setup → Flows → New Flow → Record-Triggered Flow.

  • Object: Lead
  • Trigger: A record is created
  • Optimize for: Fast Field Updates (Before Save)

Get the current counter

Add a Get Records element:

  • Object: Round_Robin_Counter__c
  • Store: First record → varCounter

Get active reps

Add a Get Records element:

  • Object: Round_Robin_Rep__mdt
  • Filter: Is_Active__c equals true
  • Sort: Rep_Order__c, Ascending
  • Store: All records → colReps

Calculate assignment with a Formula

Add a Formula resource:

  • Name: frmNextIndex
  • Type: Number
  • Formula: MOD({!varCounter.Last_Index__c}, {!colReps.Size})

This gives you the index of the next rep to assign.

Get the specific rep

Add an Assignment element to loop through the collection and pick the rep at the calculated index, or use a Loop element with a counter variable that breaks when it matches frmNextIndex.

Update the Lead owner

Add an Assignment element:

  • Set: {!$Record.OwnerId} = the selected rep's User_Id__c

Since this is a before-save flow, the field update happens automatically — no separate Update Records element needed.

Increment the counter

Add an Update Records element:

  • Object: Round_Robin_Counter__c
  • Filter: Use the record ID from varCounter
  • Set: Last_Index__c = {!varCounter.Last_Index__c} + 1
Before-save flows can't make HTTP callouts

You cannot send Slack messages from a before-save flow. The assignment logic must be in a before-save flow for performance (no extra DML), but the Slack notification needs a separate after-save flow or Platform Event.

Step 4: Create the after-save flow (Slack notification)

Create a second Record-Triggered Flow:

  • Object: Lead
  • Trigger: A record is created
  • Optimize for: Actions and Related Records (After Save)
  • Entry condition: OwnerId is not null

Send Slack notification

If you have Salesforce for Slack installed, add a Send Slack Message action:

  • Slack Workspace: Select your connected workspace
  • Channel or User: The assigned rep's Slack User ID (you'll need to look this up from the Custom Metadata Type using the Lead's OwnerId)
  • Message: Include the lead's Name, Email, Company, and a link back to the Lead record

Alternatively, add an HTTP Callout action (available in Flow Builder as an External Service or Apex invocable):

  • URL: https://slack.com/api/chat.postMessage
  • Method: POST
  • Headers: Authorization: Bearer xoxb-YOUR-BOT-TOKEN
  • Body: Channel set to the rep's Slack User ID, with lead details in the message text

Step 5: Activate and test

  1. Activate both flows
  2. Create a test Lead manually or via the API
  3. Verify the Lead's OwnerId was set to the expected rep
  4. Check Slack for the notification DM
  5. Create a few more Leads and confirm the rotation cycles through all reps
Single active assignment rule conflict

If your org already has an active Lead Assignment Rule, it will run after your before-save flow. The assignment rule could overwrite the OwnerId your flow just set. Either deactivate the existing rule or add logic to your rule that preserves flow-assigned owners.

Alternative: Queue + Omni-Channel

If you're on Enterprise+ edition, Salesforce Omni-Channel provides workload-based routing out of the box:

  1. Create a Lead Queue and add your reps as members
  2. Set your assignment rule to route all Leads to the queue
  3. Enable Omni-Channel and configure a Routing Configuration with "Least Active" or "Most Available" routing
  4. Reps receive Leads in their Omni-Channel widget based on current workload

This is the officially supported path for workload-balanced routing, but it requires Enterprise edition, Omni-Channel setup, and reps to be logged into the Omni-Channel widget.

Troubleshooting

Common questions

Can I do weighted round-robin (senior reps get more leads)?

Yes. Add a Weight__c number field to the Custom Metadata Type and repeat the rep entry in the collection that many times. A rep with weight 2 appears twice in the rotation, getting double the leads. Alternatively, use a more complex formula that maps counter ranges to reps.

What happens if I deactivate a rep mid-cycle?

The MOD formula adjusts automatically to the new collection size on the next Lead creation. However, the counter value stays the same, so one rep might get skipped or doubled for a single assignment. Reset the counter to 0 after roster changes for a clean restart.

Can I route Leads to queues instead of individual users?

Yes. Store the Queue ID (starts with 00G) in the User_Id__c field of the Custom Metadata Type. Salesforce accepts queue IDs in the OwnerId field for Leads. The Slack notification step would need adjustment since queues don't have Slack User IDs — post to a channel instead.

Cost

  • Flow Builder: Included in all Salesforce editions with Flow access (Enterprise+)
  • Custom Settings and Custom Metadata Types: Included, no additional cost
  • Omni-Channel: Included in Enterprise and Unlimited editions

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.