Agent States & Readiness
This page is the authoritative reference for the agent state machine: what an agent is at any instant, how they become eligible for calls, and how every transition is recorded durably to feed reporting. For login, the control API, and pause reasons, see Agents & Presence.
Explicit Ready
Section titled “Explicit Ready”Logging in does not make an agent available. By default an agent who logs in lands not-ready (present in the app, registered, but not offered calls) until they explicitly go available. This is deliberate: it separates “I’m here” from “I’m ready to take calls,” exactly as Amazon Connect and Five9 model the agent.
Two opt-in flags on sip_user tune login behavior:
| Column | Default | Effect when 1 |
|---|---|---|
auto_ready_on_login | 0 | Agent reaches available on login instead of landing not-ready. |
auto_queue_login | 0 | Login auto-activates the agent’s static + mandatory queue memberships (see below). |
With both 0 (the default), an agent logs in, is unreachable→reachable as their phone registers, then must send available and select queues before any call is offered.
Status, state, and reachability
Section titled “Status, state, and reachability”The per-account PresenceDO tracks three orthogonal signals per agent:
statusis what the agent wants:offline,available, oron_break(paused).stateis what the agent is doing:waiting,offering(a call is ringing them),in_call, orwrap_up.reachableis whether the agent’s device has a live SIP registration, fed independently by the SIP signaling layer register/qualify feed.
An agent is Ready (dispatchable) only when status = available and state = waiting and reachable and not on an external call and not in wrap-up. That compound predicate is enforced atomically by the reserve fence.
Live status buckets
Section titled “Live status buckets”For dashboards, the PresenceDO collapses status + state + reachability into a single bucket per agent, in priority order:
extcall › incall › offline › unreachable › ring › paused › wrapup › ready › idle
These nine buckets are the canonical state vocabulary: they are exactly what the agent-state event log records, so the live wallboard and the historical report speak the same language.
| Bucket | Meaning |
|---|---|
ready | Available, waiting, reachable, and eligible for the next call. |
ring | Being offered a call (its phone is ringing). |
incall | On a queue call. |
wrapup | Post-call after-work; not yet eligible again. |
paused | On break (on_break), with a pause reason. |
extcall | On a non-queue (direct/external) call. |
unreachable | Logged in, but the device has no live registration. |
offline | Logged out. |
idle | Logged in and reachable but not in any of the above. |
Membership modes
Section titled “Membership modes”How an agent comes to be in a queue is separate from their priority within it. The agent_queue_assignment row carries both:
{ "queue_id": "q_01j160r23gw5zkpfjme3fwzs5k", "user_id": "us_01jatt0336xwzg2zbf3q9pyfm5", "tier_level": 1, "position": 0, "delay_sec": 0, "mode": "static" }Priority combines tier_level (lower rings first), position (tiebreak within tier), and delay_sec (hold this queue’s calls N seconds before offering, a soft reserve), mirroring a Connect routing profile’s priority + delay.
Membership mode determines who activates the agent in the queue:
mode | Who activates | Auto-loads on login? |
|---|---|---|
static | Admin-assigned | Yes (when auto_queue_login = 1) |
dynamic | Agent self-selects (passes a queues list at login) | No |
mandatory | Admin-assigned, agent cannot self-deactivate | Yes |
supervisor_managed | Only a supervisor activates | No |
On login, only static and mandatory assignments are auto-seeded into the agent’s live membership, and only when auto_queue_login is set. dynamic agents pick their own queues per shift; supervisor_managed seats are never self- or auto-activated.
The agent-state event log
Section titled “The agent-state event log”Every time an agent’s derived bucket changes, the PresenceDO appends one row to a durable, append-only log in its own SQLite table, agent_event:
CREATE TABLE agent_event( seq INTEGER PRIMARY KEY AUTOINCREMENT, ts INTEGER, -- epoch ms of the transition user_id TEXT, from_status TEXT, -- previous bucket to_status TEXT, -- new bucket reason TEXT, -- e.g. pause reason call_id TEXT -- the call involved, when relevant);Writes are deduped: a row is appended only when the derived bucket actually changes, not on every internal field flip, so the log is a clean transition timeline rather than noise. It is never deleted.
This log is the foundation for everything in Reporting & Analytics: state-time, occupancy, and utilization are all computed by walking agent_event, and schedule adherence overlays it against an agent’s shift. Live wallboards consume the same transitions as a delta stream; the event log is their durable counterpart.
Roadmap
Section titled “Roadmap”- Auto not-ready outside shift: flipping an agent to not-ready when their work schedule says off-shift is designed; today the schedule drives adherence reporting only.
- Supervisor enforcement: the
supervisor_managedandmandatorysemantics are modeled in schema; supervisor-side activation/lock controls are being wired. - Per-segment activities: break/lunch sub-states (for break-level adherence) are planned on top of the same event log.