GLOSSARY

What is round-robin assignment?

Round-robin assignment is a distribution rule that hands each new record to the next owner in a fixed cyclic order, so that over enough records everyone receives an equal, or weight-proportional, share.

Free forever plan · No credit card required · Cancel anytime

Quick definition

Round-robin assignment is a distribution rule that hands each new record to the next owner in a fixed cyclic order, so that over enough records everyone receives an equal, or weight-proportional, share.

Plain form: owner = eligible[n mod k]. Everything difficult about it is in the word "eligible".

What it means

Round-robin assignment is the oldest scheduling idea in computing applied to salespeople. Keep a list, keep a pointer, hand each new item to whoever the pointer is on, then advance the pointer and wrap around at the end. In a CRM it is usually the last rule in a lead routing chain: when no territory, segment or ownership rule applies, distribute evenly.

The appeal is that it is auditable. Any rep can count their own leads and check, which matters more than it sounds, because distribution disputes inside a sales team are corrosive and nearly impossible to settle with a black-box model. Round-robin's fairness claim is narrow and verifiable: equal counts over a long enough window.

That narrowness is also its weakness. Equal counts are only fair when the items are interchangeable, and inbound leads rarely are. Everything else on this page is about the two questions the naive version cannot answer: what happens when owners have different capacity, and what happens when the leads have different value.

The formulas, written out

Plain round-robin. With k eligible owners and n as the zero-based sequence number of the record:

owner = eligible[n mod k]

  • eligible: the ordered list of owners who can receive this record right now, after availability and capacity filters.
  • k: the length of that list.
  • n: a persisted, monotonically increasing counter.

Weighted round-robin, deficit formulation. This is the version worth implementing. For each owner i, before assigning record number T + 1:

deficit_i = ( w_i / sum of all w ) x (T + 1) - a_i

Assign to the owner with the largest deficit.

  • w_i: capacity weight of owner i.
  • a_i: how many records owner i has already received in this cycle.
  • T: total records already assigned.
  • deficit_i: how far behind their fair share owner i currently is. Negative means they are ahead.

Ties are broken by higher weight, then by a stable owner ID so the result is reproducible. This formulation is worth the extra arithmetic because it interleaves owners naturally instead of producing blocks, it handles roster changes without reshuffling anybody, and it makes the fairness claim explicit: nobody is ever more than one record away from their proportional share.

A worked example, end to end

Three reps with weights Ada 3, Bora 2, Cem 1. Total weight 6, so the target shares are 50%, 33.3% and 16.7%.

Walking the first six assignments through the deficit formula:

  1. Record 1. Deficits are 0.500, 0.333, 0.167. Ada wins. Counts (1, 0, 0).
  2. Record 2. Ada is now at 0.5 x 2 - 1 = 0.000, Bora at 0.333 x 2 - 0 = 0.667, Cem at 0.333. Bora wins. Counts (1, 1, 0).
  3. Record 3. Ada 0.500, Bora 0.000, Cem 0.500. Tie between Ada and Cem, broken by weight. Ada wins. Counts (2, 1, 0).
  4. Record 4. Ada 0.000, Bora 0.333, Cem 0.667. Cem wins. Counts (2, 1, 1).
  5. Record 5. Ada 0.500, Bora 0.667, Cem -0.167. Bora wins. Counts (2, 2, 1).
  6. Record 6. Ada 1.000, Bora 0.000, Cem 0.000. Ada wins. Counts (3, 2, 1).

After six records the split is exactly 3, 2, 1, and the order was Ada, Bora, Ada, Cem, Bora, Ada. Compare that with the naive weighted approach of repeating names in a list, which produces Ada, Ada, Ada, Bora, Bora, Cem: the same totals, but Cem waits through five assignments before seeing anything, and on a slow day that is the difference between a lead and no lead.

Extend to 40 records with plain, unweighted round-robin across five reps and everyone gets 8. Now take Cem out for three days in the middle of that period. If the rotation simply skips him, the distribution ends at 9, 9, 8, 8, 6. If the rotation is deficit-based, Cem's deficit accumulates while he is away and he receives a slightly heavier share on return, landing at 8, 8, 8, 8, 8 over a long enough window. Neither behaviour is wrong, but only one of them is a decision.

Three ways it goes wrong

Resetting the pointer every day. This is the most common implementation bug and it is invisible in a single day's data. Five reps, seven leads per day, pointer reset to index 0 each morning. The daily sequence is 0, 1, 2, 3, 4, 0, 1, so reps 1 and 2 get two leads and reps 3, 4 and 5 get one. Over a five-day week that is 10, 10, 5, 5, 5 out of 35 leads, against a fair share of 7 each. Over 250 working days, reps 1 and 2 receive 500 leads each while reps 3, 4 and 5 receive 250. That is a doubling of pipeline input from an algorithm chosen because it was fair. The same bug appears whenever the counter is held in application memory, since every deployment restarts it.

Distributing count instead of value. Take 40 leads split evenly, 8 each across five reps. Suppose 6 of the 40 are enterprise enquiries averaging 22,000 and the other 34 average 3,200. Round-robin does not know the difference, so the enterprise leads land wherever the rotation happens to be. Expected enterprise leads per rep is 1.2, but the chance that any individual rep receives three or more is about 10%, so across a five-person team it is common for one rep to get three and another to get none. Their pipeline values are 82,000 and 25,600, a 3.2x gap produced entirely by the sequence in which forms were submitted. Bucket by segment first, then round-robin inside the bucket.

Not handling declines. A rep marks a lead "not mine" and it re-routes. If the counter is not corrected, that rep has effectively cherry-picked: they receive their full share and hand back the ones they do not want, so the others absorb the difference. If the counter is corrected too aggressively, declining becomes free and the decline rate climbs. The workable rule is to return the record to the pool, keep the decliner's count incremented, and report decline rate per rep as its own metric so the behaviour is visible.

How round-robin is actually implemented in a CRM

Four pieces, and the order of operations matters:

  • An eligibility filter evaluated at assignment time: is the owner active, inside working hours in their own timezone, not on declared leave, and below their open-lead cap? Only the survivors enter the rotation.
  • A persisted counter or deficit table, updated in the same database transaction as the assignment. Not in memory, not in a cache.
  • An atomic increment. Two webhooks arriving in the same millisecond will otherwise read the same pointer value and assign both records to the same owner. In practice this means a database sequence, a row lock, or an update that returns the new value in one statement.
  • An audit row per assignment with the owner, the counter value used, the eligible-list size and the timestamp. Without it, "the rotation is broken" is an unfalsifiable claim.

Edge cases that decide whether anyone trusts it:

  • Roster changes mid-cycle. Modulo arithmetic reshuffles when k changes, so the person who was next is no longer next and the audit trail becomes hard to explain. Deficit tracking survives this without any special handling.
  • Timezone-spread teams. A rotation that ignores working hours will assign the 03:00 lead to someone asleep. A rotation that respects working hours will systematically under-assign to whichever timezone gets the least traffic, so their deficit grows and the algorithm compensates during their day. That second behaviour is usually what you want, and it only exists if deficits are tracked across days.
  • Bulk imports. Uploading 3,000 records runs the rotation 3,000 times in a few seconds and floods every queue. Bulk operations should either bypass the rotation and distribute by an explicit split, or be rate limited.
  • Reassignment after the fact. When a manager moves a lead from Ada to Bora, do the counters move too? If not, Ada keeps the credit and Bora does the work. Decide, and apply it in the audit table rather than by silently editing counters.
  • Deactivated owners. A departed rep left in the eligible list receives one lead in every rotation and it goes nowhere. Deactivation must remove them from the list and re-route their open records in the same operation.

Why it matters

Distribution is one of the few things in a sales organisation where perceived unfairness does direct damage. A rep who believes the good leads go elsewhere works the queue with less energy, and that belief is very hard to disprove without an audit trail. Round-robin's real product is not the distribution, it is the ability to show anyone exactly why they got what they got.

The second reason is speed. A shared pool that nobody owns has no clock attached to it, and first response time degrades quietly. Assigning immediately, even imperfectly, creates an owner, an expectation and a measurable service-level agreement in the same instant.

Common mistakes

  • An in-memory pointer. Resets on deploy, and breaks entirely with more than one application instance.
  • Repeating names to express weights. Produces blocks rather than an interleave, so low-weight owners wait a long time between records.
  • Rotating before filtering. Pick the owner from the eligible list, not from the full roster with a skip afterwards, or your counter and your assignments disagree.
  • Ignoring value. Equal counts with unequal leads is a fairness claim that will not survive one quarter of commission statements.
  • No decline metric. Cherry-picking is invisible unless you measure it directly.

Related concepts

  • Lead routing: the rule chain round-robin usually sits at the end of.
  • First response time: the metric an immediate assignment protects.
  • SQL: the acceptance decision the assigned rep has to make.
  • Pipeline velocity: uneven distribution shows up here as uneven opportunity counts.
  • ICP: the segmentation that should bucket leads before the rotation runs.
  • Omnichannel CRM: the same rotation has to work across form fills, DMs and email.

How Pinlyx handles it

Pinlyx keeps rotation state in the database and increments it inside the assignment transaction, so concurrent arrivals from a web form, a Telegram bot and a WhatsApp webhook cannot collide on the same owner. Eligibility is evaluated before the rotation rather than as a skip afterwards, weights are per pipeline, and every assignment writes an audit row with the owner, the rule that produced them and the size of the eligible list at that moment.

Worked example · weighted deficits, six records

Weights 3, 2, 1. Largest deficit wins.

Deficit = (w_i / 6) x (T + 1) - a_i. The result interleaves instead of blocking, and lands exactly on 3, 2, 1.

RecordAda (3)Bora (2)Cem (1)Assigned to
10.5000.3330.167Ada
20.0000.6670.333Bora
30.5000.0000.500Ada (tie, higher weight)
40.0000.3330.667Cem
50.5000.667-0.167Bora
61.0000.0000.000Ada

Order produced: Ada, Bora, Ada, Cem, Bora, Ada. The repeated-name approach gives the same totals as Ada, Ada, Ada, Bora, Bora, Cem, and makes Cem wait.

In the database

The increment has to be atomic, or two leads share an owner.

A form submission and a WhatsApp webhook can land in the same millisecond on two application instances. If both read the pointer, both compute the same index and both write, one owner receives two records and the next owner is skipped, permanently. Increment the counter inside the same transaction that writes the assignment, using a database sequence or a returning update, and the race disappears. A counter held in application memory has this bug by construction and also resets on every deployment.

Watch out for

A daily reset doubles two reps and halves three.

Five reps, seven leads a day, pointer reset each morning: 10, 10, 5, 5, 5 across a week, and 500, 500, 250, 250, 250 across a trading year. Nobody notices because each individual day looks almost even. Any time the volume per cycle is not an exact multiple of the roster size, a periodic reset systematically favours the top of the list. Let the counter run continuously, and store it somewhere that survives a restart.

Round-Robin Assignment: FAQ

The questions that decide whether your team believes the rotation is fair.

Whenever the records being distributed are not interchangeable. Round-robin optimises for equal counts, which is only fair if every lead is worth roughly the same. If your inbound mixes ten-seat self-serve signups with thousand-seat enterprise enquiries, equal counts produce wildly unequal pipelines. The fix is not to abandon round-robin but to bucket first: segment the leads, then round-robin inside each bucket.
A version where each owner carries a capacity weight and receives a proportional share instead of an equal one. A half-time rep gets 0.5, a senior rep carrying larger deals might get 1.5. The clean way to implement it is not to repeat names in a list but to track each owner deficit, the gap between the share they should have received by now and what they actually have, and give the next record to whoever has the largest deficit.
Skip the absent owner at assignment time, and decide explicitly whether they get a catch-up on return. With a modulo-based rotation, skipping permanently removes that volume from them, which quietly punishes anyone who takes leave. With a deficit-based rotation, the deficit accumulates while they are away and they naturally receive a heavier share for a few days afterwards. If you do not want that catch-up, zero the deficit on return, but make it a decision rather than an accident.
With modulo arithmetic, everything shifts: n mod 5 and n mod 6 assign the same sequence numbers to different people, so the rotation effectively reshuffles and the counts for that period become uninterpretable. Deficit-based weighting degrades gracefully, because a new owner simply starts with a full deficit and catches up over the next few records. If you expect roster changes, and every team does, prefer the deficit formulation.
It is fair in exactly one dimension: count. It says nothing about lead quality, deal size, difficulty, or timing. A rep who receives eight leads on Friday afternoon and a rep who receives eight on Tuesday morning have not been treated equally, even though the counter says otherwise. Round-robin is a reasonable default precisely because count fairness is objective and easy to audit, but it should be paired with a value check rather than trusted on its own.
In the database, incremented atomically as part of the same transaction that writes the assignment. An in-memory counter resets to zero on every deployment and every process restart, which means the first owner in the list receives a small burst each time you ship. It also breaks the moment you run more than one application instance, because two processes hold two independent pointers.
Ready to ship

Fair by count. Provable by audit.

Pinlyx keeps rotation state in the database, filters for availability before rotating, and writes an audit row for every assignment so distribution disputes end with a query.

Free forever plan · GDPR-ready · No credit card required

We value your privacy

We use cookies to improve our site, analyze traffic, and personalize ads. You can accept all, reject non-essential, or customize your choices. Read our Cookie Policy.