The five KPI tiles and where each figure comes from
The KPI strip across the top is fed bystreams.analytics.overviewKpis, with
one exception — the Live streams tile, which is counted client-side from a
page of streams.liveInputs.list.
Two things to note about the Concurrent viewers tile. The number is the
analytics rollup’s
viewers_now. The across N live streams subtitle next to
it is not from the same query — it is the same client-side count that drives
the Live streams tile. The two can disagree briefly after a stream starts or
stops, because they refresh independently.
The total denominator on the Live streams tile comes from a separate,
deliberately tiny call: liveInputs.list with perPage: 1, read only for its
total. It is cached with a 60-second stale time, so a newly created input can
take a moment to move the denominator.
Windows and refresh cadence per panel
The screen does not have one clock. Each panel picks the cheapest cadence that still feels live, so panels drift relative to one another between ticks.
The practical consequence: the P50 glass-to-glass KPI tile and the P50
under the histogram are different numbers from different procedures. The tile
reads
overviewKpis.g2g_p50_ms; the histogram panel reads
qoeKpis.g2g_p50_ms over an explicit 60-minute window and refreshes on a
different timer. If you need one authoritative percentile, read the latency
panel, not the strip.
Why the live stream count can be a floor
The Live streams tile and theacross N live streams subtitle sometimes render
with a trailing +, for example 12+. That means at least twelve, not
exactly twelve.
The reason is how liveness is resolved. liveInputs.list filters on the
Postgres status column first, then reconciles each returned row against the
relay to set is_live. Because that reconcile happens after the database
filter, the screen cannot ask the server for “only live rows” and trust the
count — it fetches one page of up to 100 inputs and counts the live ones
client-side.
So the screen compares the number of rows it received against the total
reported by the same query:
totalequals the rows returned → the count is exact, no+.totalis greater than the rows returned → there are inputs the page never saw, any of which might be live. The count is a lower bound and the tile appends+.
+,
even if nothing is broken. To get an exact live count, open the Live streams
screen, which pages through the full set.
The Live now panel under the endpoint card is a preview of the same page: it
renders the first four live rows with their per-stream viewers_now and
g2g_p50_ms. “View all” goes to the full list.
Billing cycle meters when no plan pool is set
The “This billing cycle” card shows three meters. Their used values and their limits come from two different procedures, and the limits are optional.analytics.billing returns a pools object which may contain
stream_egress_gb and vod_storage_gb, each with a limit and a remaining.
analytics.usage returns raw metered counters: delivery_minutes,
egress_bytes and storage_bytes.
How each meter resolves:
- Egress and Storage (VOD) — if the matching pool exists, the meter
shows
limit − remainingas used (never below zero) against the pool’s limit. If the plan defines no such pool, the meter falls back to the metered counter fromanalytics.usage, converting bytes to GB by dividing by 1024³, and renders with a limit of zero. All three values are rounded to one decimal. - Delivery minutes — always rendered with a limit of zero. This modality has no delivery-minute pool on the Overview screen, so the meter is a counter, not a quota. It shows minutes consumed so far in the current billing cycle and nothing else.
analytics.billing refreshes every 120 s and analytics.usage every
60 s, a meter can briefly show a used value computed one way and a limit from
the previous billing tick. It reconciles on the next refresh.
Reading a dash, a plus, or an empty chart
The screen distinguishes “no data yet” from “zero” in several places. What you see tells you which query is still cold.- Em dash (
—) in a KPI tile. The underlying query has not returned yet, or returned nothing. The Live streams tile shows a dash specifically whileliveInputs.listis loading, then switches to a count. - Em dash on P50 / P95 / P99 under the histogram.
qoeKpisreported zero for that percentile, which the panel treats as “no samples” rather than “0 ms”. Percentiles only render once the 60-minute window contains latency samples. - Trailing
+on a count. A lower bound — see Why the live stream count can be a floor. - “No viewer samples yet.” The 24-hour viewer chart needs at least two buckets with a non-zero value. Fewer than two 10-minute buckets, or an all-zero series, renders this caption instead of a flat line at zero. A brand new org sees this until the relay rollup produces its second bucket.
- Flat empty histogram. When
g2gHistogramreturns no buckets, the panel draws fourteen empty bars so the axis labels stay in place. The P50 marker is hidden in that case; it only appears whenqoeKpisreports a non-zero P50. - “No active inputs — your first encoder publish will show up here.” The Live now panel has rows available but none of them are live, or the org has no inputs at all.
- “No webhook deliveries yet.”
eventDeliveries.listreturned nothing. Once deliveries exist, the panel shows the six most recent with their HTTP response code and event type; a code in the 2xx range renders as success, anything else as a failure.
The histogram bars and the P50 marker
The latency histogram plots the first fourteen buckets returned byg2gHistogram over the 60-minute window, labelled from 80 ms to 600+ ms. The
marker showing where your median sits is derived, not returned: the panel takes
qoeKpis.g2g_p50_ms, divides by 40 to map milliseconds onto a bucket index, and
clamps the result to the last drawn bucket. A median past the top of the axis
therefore parks the marker on the final bar rather than disappearing off the
edge. Read the marker as approximate and the P50 stat below it as the number.

