Queue Metrics & CDR
Every queue call writes one row to a durable CDR timing layer in the PresenceDO, and the standard contact-center KPIs (ASA, service level, abandon rate, and AHT) are derived from those four timestamps. As with agent KPIs, there’s no separate aggregation job: the metrics are a pure read over the CDR within a time window.
The queue CDR
Section titled “The queue CDR”CREATE TABLE queue_cdr( call_id TEXT PRIMARY KEY, queue_id TEXT, agent_id TEXT, -- who answered (NULL until answered) enqueued_at INTEGER, -- entered the queue (epoch ms) connected_at INTEGER, -- answered by an agent (NULL until answered) ended_at INTEGER, -- talk ended (NULL until the call ends) disposition TEXT -- offered | answered | abandoned | timeout);One row per offered call, written and updated across the call’s life by the same single-threaded brain that runs the ACD fence:
| Lifecycle event | Effect on the row |
|---|---|
| Enqueue | Insert with enqueued_at and disposition = offered. |
| Answer (agent confirms) | Set connected_at, agent_id, disposition = answered. |
| Abandon / timeout (before answer) | Set disposition = abandoned or timeout and ended_at. |
| Hang-up (after answer) | Set ended_at (the talk-end). |
The three timestamps enqueued_at → connected_at → ended_at are all you need to derive every KPI below.
The KPIs
Section titled “The KPIs”All metrics report over [since, now] and share a service-level threshold parameter, slSec (default 20 seconds).
Volumes
Section titled “Volumes”offered = rows for the queue in the windowanswered = rows where disposition = answeredabandoned = rows where disposition ∈ { abandoned, timeout }Abandon rate
Section titled “Abandon rate”abandonRatePct = round( 100 × abandoned / offered ) // 0 if offered = 0ASA: Average Speed of Answer
Section titled “ASA: Average Speed of Answer”The mean wait before an agent answered, over answered calls:
wait = connected_at − enqueued_atasaMs = round( mean(wait over answered calls) ) // 0 if none answeredService level
Section titled “Service level”The share of all offered calls answered within slSec:
slMet = answered calls where (connected_at − enqueued_at) ≤ slSec×1000serviceLevelPct = round( 100 × slMet / offered ) // 0 if offered = 0Note the denominator is offered, not answered: an abandoned call counts against service level, which is the stricter (and standard contact-center) definition.
AHT: Average Handle Time
Section titled “AHT: Average Handle Time”The mean talk time over calls that have ended:
ahtMs = round( mean(ended_at − connected_at over answered calls with ended_at) )AHT here is talk time. It does not yet fold in the agent’s post-call wrap_up; that’s tracked separately in agent state-time and is a planned addition to AHT.
Reading the report
Section titled “Reading the report”Queue KPIs are read from the public API at GET /v1/queues (the queues scope):
{ "queues": [ { "queue": "q_support", "offered": 120, "answered": 108, "abandoned": 12, "abandonRatePct": 10, "asaMs": 8200, "serviceLevelPct": 84, "ahtMs": 254000 } ]}Roadmap
Section titled “Roadmap”- Configurable service-level target per queue (today
slSecdefaults to 20s at the report layer). - Wrap-up in AHT: folding post-call work into handle time.
- Interval reporting: half-hourly buckets for the classic SL/ASA-over-time grid.
- Full call CDR + the edge SQL database/Iceberg export for long-term retention and cross-call analysis.