adminSessions.list({ orgId }). That procedure reads the TeleQuick
presence store in Redis and joins it to the dim_agent dimension, so
each row carries both the live state (which changes second to second)
and the durable agent identity (name, email, external id). The screen
polls the procedure and re-renders; it never mutates anything.
The query is scoped to one org and is disabled until an org is
selected. There is no cross-org roster.
What a session row represents
One row is one agent, not one call. The row is keyed onagentId,
so an agent appears exactly once no matter how many calls they have
handled in the period you are looking at.
That has two consequences worth knowing before you read the screen:
- A row is a current snapshot. It has no history. If you need the sequence of states an agent moved through, this screen is the wrong tool — see Related.
- At most one call id is attached to a row (
currentCallId). The roster describes the call the agent is on now.
Session states
state is the field that drives the colour and the badge on the right
of each row. The screen’s state filter enumerates the full set of
values the procedure returns:
ringing and on_call are the only two states that render as a
live pill (the animated variant). That is deliberate: those are the
two states where the agent is attached to a call leg and where the
value is expected to change without you doing anything.
Note which states pair with which companion field, because it tells
you where the state came from:
ringingandon_callcarry acurrentCallId. They describe call activity. They are not modes an agent picks from a UI — an agent becomesringingbecause a call was offered to them.auxcarries anauxReason(the free-form label the agent or your integration supplied, e.g. a break or training code). This is a selected state, which is why it has a reason attached.acw(after-call work) carries neither. It is the wrap-up window that follows a call, and it is distinct fromaux: an agent inacwis finishing the previous interaction, an agent inauxhas deliberately made themselves unavailable.
stateSinceMs — how
long the agent has been where they are.
Row fields
These are the fields the roster actually reads off each row.
The primary label degrades in a fixed order:
displayName, then
externalAgentId, then the raw agentId. An agent showing a raw id
usually means the presence entry has no matching dim_agent row yet.
stateSinceMs is a Unix timestamp in milliseconds. The screen renders
it as an age relative to the browser’s clock: seconds below a minute,
minutes below an hour, then hours. A missing or zero stateSinceMs
renders as —. Because the age is computed client-side, a skewed
workstation clock skews every age on the screen at once — if all the
ages look implausible, suspect the clock before the roster.
How offline is detected
Presence is not a flag that an agent turns off on the way out. It is a Redis entry that has to be kept alive. The gateway writes the entry as the agent’s session reports in, and the entry expires on its own if those reports stop. Sooffline on this screen means there is no live presence entry for
this agent, and the identity you see is coming from the dim_agent
side of the join. Three different real-world situations collapse into
that one value:
- the agent signed out cleanly,
- the agent’s client or softphone died and the presence entry lapsed,
- the network between the agent and the gateway dropped long enough for the entry to lapse.
stateSinceMs: an agent who has been offline for seconds is
interesting, an agent who has been offline for hours is just not
working today.
Expiry also explains a lag you will observe. An agent who yanks their
network cable does not flip to offline at that instant. They flip
when their presence entry lapses, and only then does the next poll show
it. Treat offline as “we stopped hearing from this session”, not as
“this session ended at this moment”.
The empty state on the screen — “Agents appear here when they sign
in” — is the other side of the same coin: an agent with no presence
entry and nothing to join against contributes no row at all.
Refresh and polling behaviour
This screen polls.adminSessions.list is re-fetched on a fixed
8-second interval and the roster re-renders from whatever the latest
response says.
There is no push-based invalidation here. The original operator console
invalidates this query from a QUIC push when presence changes; this
Voice AI port has no realtime hook available, so it drops the push path
and uses a tighter poll instead. The practical difference:
- Worst-case staleness on this screen is one poll interval, not one round trip from the presence change.
- The ages on the rows advance only when a poll lands, because
stateSinceMsis re-rendered from the response rather than ticked locally. An age that appears to “jump” several seconds is the poll landing, not a data problem. - The poll runs whenever the screen is mounted and an org is selected. Leaving the screen open leaves the poll running.
Why the counters ignore the filters
The header readsN online · N ready · N on call · N aux. Those four
numbers are computed from the full response, before the search box
and the state filter are applied. Filtering the table down to one
agent does not change them.
That is intentional — the counters are the org-wide picture and the
table below is your lens onto it — but it means the numbers do not add
up the way you might expect:
onlinecounts every row whose state is anything other thanoffline. Soringingandacwagents are included inonline.ready,on call, andauxare exact-state counts.- There is no
ringingoracwcounter. Those agents are visible inonlinebut in none of the three breakdown numbers, soready + on_call + auxis normally less thanonline. The difference is yourringingplusacwpopulation. offlinerows are counted internally but not surfaced in the header strip.
ringing or acw head-count, select that value in the
state filter and count the rows.
The filters themselves behave as follows. The state dropdown is an
exact match on state. The search box is a case-insensitive substring
match against displayName, email, and externalAgentId joined
together — so a query can span fields, and searching by raw agentId
will not match. The two filters combine with AND.
Related
- Telemetry — the metrics, trace, and CDR
streams, for the per-call record behind a
currentCallId. - Telephony Metrics — AHT, ASA, and the other contact-centre numbers that aggregate the states on this screen.
- Authentication — how the org scope on this query is established.