JustOnAir docs
View as Markdown

Stream lifecycle

The five statuses a stream moves through, the timers that move it, and why a stream ends.

A stream is one broadcast. Its key works once: when the stream is over, create a new one for the next broadcast.

Statuses

Text
            create
              │
          ┌───▼────┐   30 min, nothing connected   ┌─────────┐
          │pending │──────────────────────────────▶│ expired │
          └───┬────┘                                └─────────┘
              │ publisher accepted      DELETE     ┌───────────┐
              │            └──────────────────────▶│ cancelled │
          ┌───▼────┐                                └───────────┘
          │  live  │
          └───┬────┘
              │ DELETE, publisher gone 60 s, max duration,
              │ a limit broken, or credit at zero
          ┌───▼────┐
          │ ended  │
          └────────┘
StatusMeaningCan go live?
pendingCreated; capacity reserved; waiting for the publisher.Yes, for 30 minutes
liveA publisher is connected (or dropped less than 60 s ago).It is
endedIt was live and is over. The recording is being made.No
expiredNothing connected within 30 minutes.No
cancelledDeleted before it went live.No

The timers

TimerLengthWhere to see it
Waiting for the publisher30 minutes from creationreservation_expires_at, reservation_expires_in_seconds
Reconnecting after a drop60 seconds, same keystatus stays live meanwhile
Maximum duration4 hours (new accounts), 24 hours (trusted)max_duration_seconds
Ingest limit warning20 seconds before the stream is endedingest.limit_warning

There is no way to reserve a stream for a future time. Create it when the publisher is about to start.

Why a stream ended

end_reason on an ended, expired or cancelled stream:

end_reasonWhat happenedWhat to do
deletedYou called DELETE /v1/streams/{id}.—
disconnectedThe publisher left and didn't come back within 60 s.Normal end for OBS "Stop Streaming".
duration_limitIt reached max_duration_seconds.Create a new stream to continue.
reservation_ttlNothing connected within 30 minutes.Create the stream closer to the start.
limit_bitrateOver 7.5 Mbps arrived for 20 seconds after the warning.Set the encoder to at most 6000 Kbps.
limit_resolutionVideo larger than max_resolution for 20 seconds after the warning.Scale the output down, or create the stream with a higher max_resolution.
insufficient_creditYour credit reached zero.Top up, then create a new stream.
simulcast_not_supportedA browser sent several video layers.Publish one video track.
provision_failedNo ingest server could be started. Rare.Create a new stream.

What is arriving

While live, ingest shows what the ingest server measures, updated every few seconds:

JSON
"ingest": {
  "width": 1280,
  "height": 720,
  "kbps": 2710,
  "observed_at": "2026-09-29T10:32:14.120Z",
  "limit_warning": null,
  "keyframe_interval_seconds": 2,
  "keyframe_warning": false
}

kbps counts everything received, audio and overhead included, so a 2500 Kbps video setting reads a little higher. keyframe_warning is only advice: the stream keeps running.

Following a stream

Add a webhook and we tell your server when the stream goes live, ends and has its recording. Or poll GET /v1/streams/{id} every 3–5 seconds while you care, and stop when the status is ended, expired or cancelled. The player page does its own polling through the CDN, so viewers add no load to your API calls.

The same read has viewers: how many are watching now (from the hosted player), and the peak and total watch time (exact, from the CDN logs, about an hour later). See Hosted player.