# Voice AI console: Overview screen

> How the Overview screen sources its month-to-date KPIs, determines which calls are active, and tracks the four steps to a number that answers.

The Overview screen is the landing screen of the Voice AI console. Before it
existed, the console opened on whichever agent happened to be first in the
roster — useful for "what is this agent doing", useless for "what is my voice
product doing", and an empty workspace for a tenant with no agents yet.

Every figure on the screen comes from a procedure another screen already uses.
Nothing is computed optimistically. A figure that cannot be sourced from a
procedure is not rendered at all, because a confident wrong number on a landing
page is worse than a missing one.

| Panel | Source procedure |
| ----- | ---------------- |
| Calls / Minutes / Spend | `cdr.monthStats` |
| On a call now, Active calls list | `telephony.listActiveCalls` |
| "N published" badge, agent step state | `agentVersions.publishedAgents` |

## What the four KPI tiles measure

| Tile | Value | Notes |
| ---- | ----- | ----- |
| **Calls this month** | `calls` from `cdr.monthStats` | A count of distinct calls, not of CDR rows. |
| **Minutes** | `duration_sec` from `cdr.monthStats`, converted to minutes | Rounded to whole minutes. At 1000 minutes and above it is abbreviated, e.g. `1.4k`. |
| **Spend** | `cost` from `cdr.monthStats` | Rendered in dollars to two decimal places. |
| **On a call now** | The number of rows returned by `telephony.listActiveCalls` | Subject to the liveness rules and the window described below. |

While a query is in flight its tile shows `…` rather than a zero, so an
in-progress load never reads as "no traffic".

The three month-to-date tiles refresh when their query refetches. The active
call count refetches every 15 seconds, and the published-agent badge every 60
seconds.

## How month-to-date is scoped and de-duplicated

`cdr.monthStats` reads the `cdrs` table in ClickHouse with two filters:

- **Tenant scope** — `tenant_id` equals the organization behind the current
  console session. `cdr.monthStats` is an org procedure and takes `orgId` as
  input; the tenant identifier is derived from it, so the tiles never blend
  traffic from another organization.
- **Month scope** — `toStartOfMonth(starting_time) = toStartOfMonth(now())`.
  This is a calendar-month-to-date figure keyed on the call's start time, not a
  rolling 30-day window. The tiles reset when the month rolls over, and a call
  that started in the previous month is attributed to that month even if it ran
  past midnight on the first.

**De-duplication.** A single call can produce more than one CDR row. The query
therefore aggregates in two stages: an inner query groups by `call_id` and takes
`max(duration)` and `max(cost)` for each call, and the outer query counts those
groups and sums the per-call maxima.

The consequences are worth knowing when you reconcile these numbers against a
raw CDR export:

- `calls` is a count of distinct `call_id` values, so re-written or duplicated
  rows for the same call do not inflate it.
- `duration_sec` and `cost` take the **largest** value seen per call rather than
  summing rows. If your pipeline writes a partial row followed by a final row,
  the final (larger) value wins; summing the rows directly would double-count.

If no rows match, the procedure returns zeros for all three fields rather than an
empty result, so the tiles render `0` rather than blanking.

## How an active call is determined and when it goes stale

"On a call now" and the **Active calls** card are both fed by
`telephony.listActiveCalls`. A call is listed only if it passes every one of the
following checks.

**1. It is inside the activity window.** The procedure treats calls started
within the last 60 minutes as candidates. Real calls finish in
seconds-to-minutes; anything older than that is almost certainly a stale index
entry left behind by a hangup path that never removed it — a carrier
disconnect, a gateway restart mid-call, or a Redis blip. An earlier 8-hour
window was a holdover from the SCAN-fallback era and surfaced calls that had
hung up hours before, which is exactly the kind of wrong number this screen
avoids.

**2. It is in the per-tenant active-call index.** Candidate call SIDs come from a
per-organization sorted set, read newest-first by start time, bounded by the
`limit` input.

**3. The session has no end marker.** For each candidate the procedure checks
whether the session's `:end` hash exists. The agent runtime writes that hash on
hangup, via the RTP silence-finalize or dialog-closure path. Its presence means
the call is over even if the release path failed to remove the index entry, so
the call is excluded.

**4. The index entry self-heals.** Candidates found to have ended are removed
from the index inline, as a best-effort cleanup. If the cleanup fails, the entry
is simply filtered out again on the next listing. This is why a stale entry you
saw once usually does not come back.

**5. The session record still agrees.** For surviving SIDs the procedure reads
the session hash and drops the row if the stored `tenant_id` does not match the
requesting organization, if the start time is unparseable, or if the start time
turns out to be older than the 60-minute cutoff after all.

Rows are returned sorted newest-first by start time, capped at `limit`. Each row
carries the call SID, tenant, agent, trunk, realm, start time in milliseconds,
and the node handling the call.

The Overview card renders the first six rows and, when there are more, offers a
button through to the monitoring screen. Each row shows the call's endpoints and
its status; a field the session did not record renders as `—` rather than as a
guess.

Because the count is the length of this filtered list, it can differ from a raw
count of index entries. That difference is the point: the index is the candidate
set, and the liveness gate is what makes the number true.

## The four steps to a number that answers

The **Get your first agent answering calls** card lists the four things that
stand between a new tenant and a real phone call. All four must be true before an
inbound call is answered.

| Step | What it covers | Opens |
| ---- | -------------- | ----- |
| **Read the quickstart** | SDKs, the framework you already run, and the install command. | Get started |
| **Build an agent** | Pick a model and a voice, or bring the framework you have. | Agents |
| **Connect a trunk** | Bring your own carrier, or terminate on TeleQuick's. | Trunks |
| **Point a number at it** | A number routed to a published agent is what makes it answerable. | Numbers |

The card's badge shows how many agents are **published**, taken from
`agentVersions.publishedAgents`. This is deliberately different from how many
agents exist: an agent object in the roster is not a live agent. The "Build an
agent" step is marked complete only when at least one agent is published, and the
badge is the number you check when a number rings but nothing picks up.

The other three steps are always offered as links rather than being ticked off,
so the card stays a navigation aid rather than a checklist that hides the
quickstart once you have used it.

Each button navigates within the console using the shell's navigation setter
rather than reloading the page.

If the console has no organization in context, the screen replaces all of the
above with a prompt to sign in on the portal, since every procedure on the screen
is org-scoped and none of them can run.

## Where to go for detail

The Overview screen is a summary. Follow these for the underlying detail:

- **Monitoring** — the full active-call list, when more than six calls are live.
  The "See all N" button goes here.
- **Trunks** — carrier connectivity, for the trunk step.
- **Numbers** — number-to-agent routing, for the final step.
- **Agents** — agent authoring and publishing, which is what moves the published
  count.
- [Telemetry](/platform/telemetry) — metrics, traces, and the CDR schema that
  backs the month-to-date tiles.
- [Telephony metrics](/glossary/metrics) — definitions for the call-quality and
  traffic numbers you will meet on the detail screens.
