Skip to main content

Omnichannel capacity and reservation model

A blended agent answers voice calls, live chats, and ticket follow-ups on the same shift. Two questions follow from that, and they need two different services to answer:
  • “Which agents are saturated right now?” — answered by a read-only gauge that scores each agent’s load across every channel.
  • “Can this channel give the agent one more interaction?” — answered before every assignment by an atomic slot reservation that voice routing and digital routing both claim against.
This page explains both layers, how the slots are counted, where the reservations live, and what happens when the infrastructure holding them is degraded. Everything here is read-only velocity plus per-tenant configuration — no outbound voice or messaging path is involved; the model governs inbound assignment only.

1. The two-layer model

The supervisor wallboard shows a single blended view: per agent, the voice presence on ACD queues plus the open digital conversations they own, folded into one weighted score with an overloaded flag. That gauge is computed from live database rows whenever the wallboard polls — it never mutates anything, and it never blocks an assignment. Read-side computation answers the observability question, but it has a blind spot on the control question. Two places assign work: the voice ACD dispatcher and the digital inbox router. If each only reads a gauge before deciding, both can decide “this agent has headroom” in the same instant and both assign — landing the agent one interaction over their ceiling. The gauge could describe that overshoot but never prevent it. So there is a second layer: a short-lived slot reservation. A channel that wants to assign work to an agent first asks to reserve one slot. If the agent is already at their blended ceiling, the reservation is refused and the channel routes the work elsewhere (the next agent, an overflow queue, or back to the unassigned pool). Once the assignment row is durable, the hold is released — the row itself then represents the load. Voice dispatch and digital routing point at the same reservation store, so neither can grant a slot the other is already holding.

2. The slot model

Every agent has a tenant-configurable ceiling of concurrent blended slotsmax_concurrent_slots on the capacity configuration, defaulting to 3 (the common omnichannel-desk shape: one voice call plus two chats, or three digital interactions). A slot is a plain unit of work; both channels count the same way:
  • Voice slot (1) — occupied when the agent’s worst-case ACD presence is busy (on a live call) or wrapup (in the after-call window). Presence is org-wide: an agent who is available on one queue but busy on another is counted as busy.
  • Digital slots (one per open conversation) — every open conversation assigned to the agent on a non-voice channel (chat, email, SMS, WhatsApp, web chat) occupies one slot.
The load an agent already carries — base load — is read live from the assignment rows at the moment of the reservation: the worst-case ACD member state plus the open-conversation count. Live holds (unexpired reservations) are counted on top of base load. The capacity ceiling and the weighting are tenant settings; the counts themselves always come from live rows, never from cached aggregates.

3. The atomic reserve and release

A reservation is an ephemeral pre-commit intent: “I am about to assign this agent a chat” or “voice dispatch is about to offer this agent a call.” It needs an atomic check-and-set against concurrent claimants, which it gets from a small script that runs inside the platform’s cache layer as one indivisible step: prune expired holds, count the live ones, and grant the new hold only when base load + live holds + 1 ≤ the ceiling. Because counting and claiming happen in a single step, two channels racing for the last slot cannot both succeed — whichever arrives second is refused and re-routes. Each successful reserve returns a reservation id. The caller then:
  1. Writes the real assignment (the ACD member state transition, or the conversation assignment).
  2. Releases the reservation, even on failure — a release with a null id is a no-op, so the release can live safely in a finally block.
A crashed or leaked caller can never strand a slot: every hold carries a short time-to-live (default 30 seconds, caller-overridable between 5 and 300 seconds) and expires on its own. Expired holds are pruned lazily on the next reserve.

4. Fail-open posture

If the cache layer that stores holds is unreachable, a reserve cannot distinguish “headroom” from “possible overshoot” — so it responds in the direction that keeps inbound work flowing: the assignment is allowed, but the response carries enforced: false and a redis_unavailable reason, and no hold exists. Blocking every assignment platform-wide on an infrastructure blip is worse than the overshoot the hold was preventing. There is one deliberate over-count built in. Between a successful reserve and the moment the assignment row lands, the slot is visible twice — once as a live hold and once (very briefly) as both a hold and a durable row. The count can only ever be a little too high, never too low, so the worst case of that overlap window is a momentary under-allocation.

5. Where each consumer plugs in

The same reservation gate sits in front of both assignment paths:
  • Voice ACD dispatch — before offering a queued inbound call to an agent, the dispatcher reserves a voice slot; a refusal sends the call to the next eligible agent or holds it in queue.
  • Digital inbox routing — before binding a new conversation to an agent, the router reserves a digital slot; a refusal falls back to the next candidate, an overflow queue, or the unassigned pool.
  • Supervisor surfaces — the wallboard’s blended gauge computes and renders load without touching reservations; a read-only peek endpoint reports an agent’s base load, live holds, and remaining headroom for diagnostics. GET /api/v1/voice/supervisor/omnichannel-capacity/agents/:agentUserId/slots.
The gauge and the gate read the same two signals (ACD presence, open assigned conversations), so the number the supervisor sees is the same count the routing gate enforces — one definition, no drift.
Everything on this page governs inbound assignment: which agent an inbound call or conversation goes to. Outbound voice and SMS termination are unaffected — outbound MT continues to exit exclusively via the Devotel wholesale softswitch.

6. Configuration surface and operator reads

All knobs are tenant-owned settings on your organization, exposed in the capacity configuration object. The defaults below apply until you override: For a supervisor diagnosing a specific agent, the peek endpoint returns four numbers — base slots, held slots, their sum, and the ceiling — plus the list of live holds with their expiry times, so you can tell assignment pressure (a clipboard of short holds) from materialized load (durable rows). The reservation store and all base-load reads are scoped to your tenant; one tenant’s holds and reads can never see or release another’s.