13 KiB
ingress-api Specification
Purpose
Read access to ingress definitions: the ingresses list and its summary numbers, every query backed by an index.
Requirements
Requirement: Ingresses list endpoint
Edition: CE. GET /api/v1/ingresses SHALL list the ingress definitions of the activity range with the common query
contract (from, to, tz, limit 1-100 default 25, opaque cursor), the filters status, inputType and q, the
ranked sort keys createdAt (default), totalDuration, sessions and lastSession (order default desc), and the
list envelope { items, nextCursor, prevCursor, total? } with total under the DASHBOARD_LIST_TOTAL_MAX_RANGE rule.
Scenario: Default list
- GIVEN 30 ingresses created in the last 24 hours and 4 older ones that neither started a session in that range nor are publishing
- WHEN the client requests
GET /api/v1/ingresseswith a valid token - THEN the response is
200with the 25 most recently created ingresses of the last 24 hours,nextCursorset,prevCursor: nullandtotal: 30
Scenario: Cursor walk
- GIVEN 60 ingresses in range and
limit=25 - WHEN the client follows
nextCursortwice and thenprevCursoronce - THEN the pages hold ingresses 1-25, 26-50, 51-60 and 26-50 with no gaps or duplicates, each page ordered by
createdAt descwith_id descas tie-breaker
Scenario: Ranked cursor walk
- GIVEN 60 ingresses in the activity range, two of them publishing, and
sort=totalDuration&limit=25 - WHEN the client follows
nextCursortwice - THEN the pages hold ingresses 1-25, 26-50 and 51-60 of the ranking at the first page's
asOf, with no gaps or duplicates
Requirement: Ingress list item
Edition: CE. Each item of the ingresses list SHALL be the public projection of the ingress document: identity,
definition facts, lifecycle and the aggregate session counters. schemaVersion, expiresAt, participantName,
url and any stream key SHALL never be part of a list item.
Scenario: Fields of an item
- WHEN an item is read
- THEN it has
ingressId,name?,inputType(rtmp|whip|url),roomName,participantIdentity,status(inactive|buffering|publishing|error|complete|deleted),enabled,reusable,createdAt,deletedAt?,sessions,totalDurationMs,lastSessionAt?andhasError, and nourl,streamKey,participantName,schemaVersionorexpiresAt
Scenario: Deleted ingress within retention
- GIVEN an ingress deleted yesterday whose document has not expired
- WHEN it is returned
- THEN
statusisdeletedanddeletedAtis set
Scenario: Error flag
- GIVEN a reusable ingress whose last session ended in error and which is now
inactive - WHEN it is returned
- THEN
statusisinactiveandhasErroristrue
Requirement: Ingress status filter
Edition: CE. The ingresses list SHALL accept status as one of inactive, buffering, publishing, error,
complete, deleted or all (default all) and reject any other value with 400 VALIDATION_ERROR.
Scenario: Publishing only
- WHEN the client sends
?status=publishing - THEN every item has
status: "publishing"and the activity range still applies
Scenario: Unknown status
- WHEN the client sends
?status=active - THEN the response is
400with codeVALIDATION_ERRORanddetails.issues[0].pathequal to["status"]
Scenario: Old ingress publishing now
- GIVEN an ingress created 30 days ago that is publishing now
- WHEN the client sends
?status=publishing&from=now-1h&to=now - THEN the ingress is in
items
Requirement: Input type filter
Edition: CE. The ingresses list SHALL accept inputType as exactly one of rtmp, whip or url, SHALL return every
input type when the parameter is absent, and SHALL reject any other value with 400 VALIDATION_ERROR.
Scenario: WHIP only
- GIVEN an RTMP, a WHIP and a URL ingress created in range
- WHEN the client sends
?inputType=whip - THEN
itemsholds only the WHIP ingress
Scenario: Unknown input type
- WHEN the client sends
?inputType=srt - THEN the response is
400with codeVALIDATION_ERRORanddetails.issues[0].pathequal to["inputType"]
Requirement: Time range and sort key
Edition: CE. The ingresses list SHALL apply from/to as an activity range, the union of the ingresses created in it
(createdAt_id), those with lastSessionAt in it (lastSessionAt_id) and those publishing now (status: publishing)
with createdAt ≤ to (status_createdAt). It SHALL accept sort only as createdAt, totalDuration, sessions or
lastSession (else 400 VALIDATION_ERROR) and order as asc or desc (default desc).
Scenario: Oldest first
- WHEN the client sends
?order=asc - THEN items are ordered by
createdAt ascwith_id ascas tie-breaker
Scenario: Unsupported sort key
- WHEN the client sends
?sort=lastSessionAt - THEN the response is
400with codeVALIDATION_ERRORanddetails.issues[0].pathequal to["sort"]
Scenario: Used in the range
- GIVEN an inactive ingress created 20 days ago whose last session started 2 hours ago
- WHEN the client sends
?from=now-24h&to=now - THEN the ingress is in
items
Scenario: Publishing for days
- GIVEN an ingress created 30 days ago whose session started 3 days ago and is publishing now
- WHEN the client sends
?from=now-1h&to=now - THEN the ingress is in
items
Scenario: Idle in the range
- GIVEN an inactive ingress created 30 days ago whose last session started 10 days ago
- WHEN the client sends
?from=now-24h&to=now - THEN the ingress is not in
items
Scenario: Created after the range
- GIVEN an ingress created today that is publishing now
- WHEN the client sends a range that ended yesterday
- THEN the ingress is not in
items
Scenario: Listed once
- GIVEN an ingress created 1 hour ago whose only session started 50 minutes ago and is publishing now
- WHEN the client sends
?from=now-24h&to=now - THEN the ingress appears once in
itemsand counts once intotal
Requirement: Name prefix search and id lookup
Edition: CE. The ingresses list SHALL treat q as a case-insensitive prefix over the ingress name when it has at least
two characters, combined with the activity range, status and inputType, SHALL reject a one-character q with
400, and SHALL treat a value matching ^IN_[A-Za-z0-9]+$ as an exact id lookup that ignores from, to, status,
inputType and sort and returns at most one item.
Scenario: Prefix search
- GIVEN ingresses named
Cam-1,cam-2andmy-camcreated in range - WHEN the client sends
?q=CA - THEN
itemsholdsCam-1andcam-2ordered bycreatedAt desc, and notmy-cam
Scenario: One character
- WHEN the client sends
?q=c - THEN the response is
400with codeVALIDATION_ERROR,details.issues[0].pathequal to["q"]and a message stating the two-character minimum
Scenario: Exact id outside the range
- GIVEN a deleted ingress
IN_ab12cdcreated 40 days ago - WHEN the client sends
?q=IN_ab12cd&from=now-24h&status=publishing - THEN the response is
200with that single ingress initems,total: 1and both cursorsnull
Scenario: Prefix of an ingress used in the range
- GIVEN an ingress named
Studiocreated 20 days ago whose last session started 1 hour ago - WHEN the client sends
?q=stu&from=now-24h&to=now - THEN the ingress is in
items
Requirement: Ingresses summary endpoint
Edition: CE. GET /api/v1/ingresses/summary SHALL accept from, to and tz and answer 200 with
{ publishingNow, ingressesInRange, byInputType, sessionsInRange, totalDurationMs, approximate }, where
ingressesInRange and byInputType count the ingresses the list selects for the range, the two session numbers are
hour-aligned sums of the global hourly rollups and approximate is true when the range bounds are not on hour
boundaries.
Scenario: Numbers of a range
- GIVEN 2 ingresses publishing now and created in the last 24 hours, 3 more created in the last 24 hours (5 in all: 3 RTMP, 2 WHIP), 12 sessions started and 6 hours of sessions ended in the hourly buckets of that range
- WHEN the client requests
GET /api/v1/ingresses/summary?from=now-24h&to=now - THEN the response is
{ publishingNow: 2, ingressesInRange: 5, byInputType: { rtmp: 3, whip: 2, url: 0 }, sessionsInRange: 12, totalDurationMs: 21600000, approximate: true }
Scenario: Hour-aligned range
- WHEN the client requests
?from=2026-10-13T00:00:00Z&to=2026-10-13T12:00:00Z - THEN
sessionsInRangeandtotalDurationMsare the sums ofingress.sessionsStartedandingress.durationMsof the twelve global hourly buckets andapproximateisfalse
Scenario: Publishing count ignores the range
- GIVEN an ingress created 30 days ago that is publishing now
- WHEN the client requests the summary with
from=now-1h - THEN
publishingNowcounts it andingressesInRangecounts it too, as the list shows it
Scenario: Reused ingress counted by activity
- GIVEN an idle ingress created 30 days ago whose last session started 2 hours ago
- WHEN the client requests the summary with
from=now-24h - THEN
ingressesInRangecounts it andpublishingNowdoes not
Scenario: Ingresses selected for the range
- WHEN the client requests the summary of a range
- THEN
ingressesInRangeandbyInputTypecount the ingresses created in the range, those with a session started in it and those publishing now and created beforeto
Requirement: Index-backed ingress queries
Edition: CE and PRO. Every query of this capability SHALL run on an index of the shared contract
(dashboard-ce/typings/contract/indexes.ts), or on _id for the hourly rollups the long-range summary reads: no
winning plan contains a COLLSCAN, and totalDocsExamined is bounded
by the page size plus the documented residual filters, and for the list by the ingresses of the three branches of the
activity range.
Scenario: Explain plans on the generated database
- GIVEN a database generated by the data generator at scenario M
- WHEN the list (every filter combination and sort) and summary queries are explained
- THEN each winning plan uses an index of the shared contract and none contains
COLLSCAN
Scenario: Activity range over 30 days
- GIVEN a database generated at scenario M for 30 days
- WHEN the list is explained with each sort key over 30 days for
createdAtand over 7 days for the others - THEN the three branches use
createdAt_id,lastSessionAt_idandstatus_createdAt,totalDocsExaminedis at most the ingresses the branches select, and the p95 of a page is within 200 ms
Requirement: Ranked ingress sorts
Edition: CE. Every ingress sort SHALL rank in memory as api-conventions defines: createdAt ranks createdAt over
any range; totalDuration ranks totalDurationMs plus asOf − lastSessionAt while publishing, sessions ranks
sessions and lastSession ranks lastSessionAt, these three accepted when the range is within
DASHBOARD_LIST_TOTAL_MAX_RANGE and answered 400 with code SORT_RANGE_TOO_WIDE beyond it.
Scenario: Total duration with a running session
- GIVEN in the activity range: an inactive ingress with
totalDurationMsof 3 hours, an inactive ingress with 20 minutes, and an ingress publishing withtotalDurationMsof 1 hour whose last session started 90 minutes beforeasOf - WHEN the client sends
GET /api/v1/ingresses?sort=totalDuration - THEN
itemsholds the 3-hour ingress, the publishing ingress (2 h 30 min atasOf) and the 20-minute ingress, in that order
Scenario: Most used first
- GIVEN ingresses in the activity range with
sessions12, 1 and 5 - WHEN the client sends
?sort=sessions&order=desc - THEN
itemsholds the ingresses with 12, 5 and 1 sessions, in that order
Scenario: Latest session first
- GIVEN ingresses in the activity range whose last sessions started 2 hours, 10 minutes and 6 hours ago
- WHEN the client sends
?sort=lastSession - THEN
itemsholds them in the order 10 minutes, 2 hours and 6 hours ago
Scenario: Creation order over any range
- WHEN the client sends
?sort=createdAt&from=now-90d&to=now - THEN the response is
200with the ingresses of the activity range newest first
Scenario: Metric sort beyond the limit
- GIVEN the default
DASHBOARD_LIST_TOTAL_MAX_RANGEof 7 days - WHEN the client sends
?sort=sessions&from=now-30d&to=now - THEN the response is
400with codeSORT_RANGE_TOO_WIDE