A telco I worked with ran customer support across five channels — email, web chat, a voice hotline, Facebook Messenger, and a Zalo official account — and routed everything through plain Salesforce queues. Agents cherry-picked work off those queues all day. The fast ones skimmed the easy billing cases and left the network-fault tickets to rot; the slow ones drowned. Meanwhile a customer would call the hotline about a dead internet line, sit on hold for five minutes because every "available" agent was buried in chat, and then go vent on the company's own Facebook page. That last part is what finally got the project funded.
Queue-based routing has a structural flaw: it's pull-based. The agent decides what to take, which means the work that needs attention most is the work nobody volunteers for. Omni-Channel inverts this. It's a push engine that watches who's online, what they're skilled at, and how much capacity they have left, and then assigns the next work item to the best-matched agent automatically. The agent stops shopping and starts receiving.
That sounds like a switch you flip, and technically enabling it is. But the difference between an Omni-Channel rollout that calms a contact center and one that makes it worse comes down to three things almost nobody gets right on the first pass: how you model capacity, how you keep presence honest, and how disciplined you are with skills. The rest of this is about those three.
Capacity is a weighting problem, not a counting problem
The instinct is to give every agent a capacity number — say five — and call it done. Five what, though? That's the question that decides whether the system works.
Omni-Channel capacity is measured in abstract units, and each channel consumes a configurable weight against the agent's total. For the telco we landed on these: email weight 1, chat weight 2, Facebook and Zalo weight 2 each, and voice weight 5. The logic is operational, not arbitrary. Email is asynchronous — an agent reads it when there's a gap, so it barely competes for attention. Chat is real-time but tolerant of a few seconds' delay between messages, so an agent can genuinely run two at once. A voice call is total focus; you cannot half-listen to a customer describing a fault. So a voice call at weight 5 consumes the agent's entire capacity of 5, and the engine correctly stops pushing anything else to them until the call ends.
Walk through how that plays out for one agent set to "Available - All Channels" with capacity 5 and nothing in progress. A chat arrives and consumes 2, leaving 3. An email arrives, costs 1, leaving 2. Now a voice call comes in — the engine checks, sees only 2 capacity left against a weight of 5, and routes the call to someone else entirely. A Zalo message arrives instead, costs 2, and now the agent is at zero; further work waits until they close something. That is exactly the behavior you want, and it falls out naturally once the weights reflect reality.
The classic disaster is leaving voice at the default weight of 1. During a peak, the engine cheerfully assigns five concurrent calls to a single agent, who can physically answer one. The other four customers get silently held or dropped. My rule, which I'll defend to anyone: voice weight always equals the agent's full capacity. A live call is one human, one conversation, full stop. If your telephony supports it, model voice with a tab-based capacity so one call literally occupies the whole agent rather than relying on the numbers lining up.
One more thing the capacity model has to account for is after-call work. When a call ends, the agent needs thirty to ninety seconds to write the case notes and create any follow-up before they're truly free. If the engine pushes a new call the instant the line drops, they end up taking notes for the old case while listening to the new one, and you get dirty data and a stressed agent. Set an after-call work timer on the voice routing configuration — sixty seconds is a sane default — during which the engine holds back voice but can still hand them a light email if there's room.
Presence is permission; weight is consumption
These two get conflated constantly, so it's worth being precise. Presence status controls which channels an agent is eligible to receive at all. Capacity weight controls how much each item of work costs once they're eligible. They're independent dials and you need both.
For the telco we built a small set of statuses rather than a single "Available." There's "Available - All Channels" for fully-equipped agents; "Available - Voice Only" for someone working a hotline shift; "Available - Digital Only" for an agent without a headset who handles email, chat, and social but never voice; plus the obvious "Lunch," "Training," and "Offline." When an agent flips from "All Channels" to "Voice Only," the engine immediately stops pushing email and chat their way. The status is the gate.
The presence configuration is where you set the capacity number, whether work auto-accepts, and whether agents can decline. I usually turn on auto-accept and turn off decline — letting agents reject pushed work just recreates the cherry-picking you were trying to eliminate, and it shows up as gaming in the analytics.
The nastiest presence bug only appears when telephony is involved. An agent sets "Available - All Channels" inside Salesforce, but the CTI adapter — whatever runs the actual phone line — still thinks they're logged out. Omni-Channel routes a call to them, the phone system doesn't pick it up, and the customer hears a busy tone while the dashboard insists the agent is free. The fix is a two-way presence sync between Omni-Channel and the telephony layer: going "Available" in Salesforce sets the agent "Ready" on the phone, going to "Lunch" sets them "Not Ready." Service Cloud Voice on Amazon Connect handles this sync natively; third-party CTI stacks like Genesys or Avaya need an adapter built and, more importantly, tested under failure conditions, because this is the integration that quietly breaks and nobody notices until customers complain the hotline is always busy.
Skill-based routing earns its complexity only with fallbacks
Once routing works by capacity, the next request is always "send network faults to the network specialists." That's skill-based routing: instead of pushing to anyone in a queue, the engine reads the skills a work item requires and pushes only to online agents who match all of them and have capacity.
The telco's 200 agents carried a mix of skills — internet troubleshooting, mobile, TV, billing, English, Chinese, and a VIP tier for enterprise accounts. A case like "enterprise customer reporting an internet outage" gets tagged with the skills it needs (internet plus VIP), typically by a Flow that fires on case creation and maps the case type, account tier, and contact language to a skill set. The engine then filters to agents holding every required skill before applying the routing model.
Here's where teams shoot themselves in the foot. Tag a case with internet, VIP, English, and a premium-tier skill, and you may discover only two of your 200 agents hold all four — and both are on calls. The case sits in queue for ten minutes while a VIP customer churns. Skill matching is an AND, and every skill you add shrinks the eligible pool fast. The discipline is to separate must-have skills from nice-to-have ones and build routing steps that relax the requirements over time: match the full set for the first two minutes, drop the preferred skills after that, and escalate to a supervisor at five. Without that fallback ladder, strict skill lists guarantee stuck cases.
Skill levels deserve the same caution. Yes, you can rate a skill one through ten and require level three for hard cases, but in customer service that usually just leaves your junior agents idle while three experts drown. Reserve levels for genuinely tiered expertise, and let higher levels take lower-level work rather than walling them off.
The failure I see most often, though, isn't a config mistake at all — it's process. An agent leaves, a replacement joins, the manager assigns the profile and the queue and the permission set, and forgets the skills. The new hire sits for a full day receiving nothing, the manager assumes Omni-Channel is broken, and you get a panicked call. Nothing was broken; a skill assignment was missed. The fix is operational: a written onboarding runbook that ends with "assign skills," or better, a Flow that auto-assigns a team's default skills when a new user is stamped with that team.
The principle underneath all of it
Omni-Channel isn't really a feature you configure once; it's a model of how your contact center actually works, encoded in weights, statuses, and skills. Every number in it is a claim about reality — that a voice call takes full attention, that this agent can really handle two chats, that these are the people who can fix a fiber fault. When routing misbehaves, the bug is almost never in Salesforce. It's that one of those claims was wrong, or that a human process let the config drift away from the floor. Get the model honest, keep it honest, and the engine does exactly what you'd hope.
Implementing or optimizing Service Cloud? Our Salesforce team runs discovery, designs the case process, and configures Service Cloud, Omni-Channel, and Knowledge on production engagements. Get in touch ->
See our full platform services for the stack we cover.








