Skip to content
DocsStart free

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.

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:

ColumnDefaultEffect when 1
auto_ready_on_login0Agent reaches available on login instead of landing not-ready.
auto_queue_login0Login 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.

The per-account PresenceDO tracks three orthogonal signals per agent:

  • status is what the agent wants: offline, available, or on_break (paused).
  • state is what the agent is doing: waiting, offering (a call is ringing them), in_call, or wrap_up.
  • reachable is 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.

For dashboards, the PresenceDO collapses status + state + reachability into a single bucket per agent, in priority order:

extcallincallofflineunreachableringpausedwrapupreadyidle

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.

BucketMeaning
readyAvailable, waiting, reachable, and eligible for the next call.
ringBeing offered a call (its phone is ringing).
incallOn a queue call.
wrapupPost-call after-work; not yet eligible again.
pausedOn break (on_break), with a pause reason.
extcallOn a non-queue (direct/external) call.
unreachableLogged in, but the device has no live registration.
offlineLogged out.
idleLogged in and reachable but not in any of the above.

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:

modeWho activatesAuto-loads on login?
staticAdmin-assignedYes (when auto_queue_login = 1)
dynamicAgent self-selects (passes a queues list at login)No
mandatoryAdmin-assigned, agent cannot self-deactivateYes
supervisor_managedOnly a supervisor activatesNo

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.

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.

  • 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_managed and mandatory semantics 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.