Guides
Live streams
Bounded HLS and pushed-chunk video understanding with signed webhooks, source time and explicit gaps.
Give Story0 a supported HLS playlist, or append ready video chunks from your existing ingest pipeline. Story0 creates ordinary analysis jobs for bounded windows. Your application receives job.succeeded and fetches the same typed video understanding results it uses for uploaded clips.
This is windowed understanding for editorial alerts and review. Source packaging, window fill, queueing, inference and delivery all contribute to latency; a 30-second window is not a 30-second notification guarantee.
Connect a receiver
Register an HTTPS webhook endpoint in the same mode as your API key. Subscribe to job.succeeded, job.failed, stream.started, stream.gap and stream.ended. There is no per-stream arbitrary callback URL: events use your account's registered subscriptions.
Verify the signature against the raw body, durably record webhook-id, acknowledge quickly, then process the event. Duplicates and out-of-order delivery are possible. job.succeeded remains the only analysis completion event.
Start HLS understanding
Set HLS_URL to a public playlist you are entitled to process. The URL may carry a query token; Story0 does not echo its path or query in public stream responses.
jq -n --arg url "$HLS_URL" '{
source: {type: "hls", url: $url},
instruction: "Describe visible scoring attempts and reactions. Do not infer a score solely from cheering.",
playback_policy: "sound_optional",
window_seconds: 30,
start_policy: "live_edge",
max_duration_seconds: 600,
idle_timeout_seconds: 120,
reconnect_window_seconds: 120,
billing: {max_charge: {currency: "USD", amount: "1.00"}}
}' | curl https://api.story0.com/v1/streams \
-H "Authorization: Bearer $STORY0_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: match-session-001" --data-binary @-Creation returns 201 with a stream and its Location. A repeated identical idempotency key returns 200 and the original session. Source validation/capture occurs asynchronously; inspect status, error, warnings and stop_reason.
live_edge begins with the newest completed segment available on first inspection. available starts with the earliest currently available segment. Finite playlists with ENDLIST process their available recording, including the short final tail. A stream does not fetch media predating its available playlist.
Supported source matrix
| Input | Support |
|---|---|
| Public HTTP(S) HLS media playlist | Unencrypted complete MPEG-TS segments or fragmented MP4 with a complete initialization map |
| HLS master playlist | One pinned video rendition, at most 1080p and 8 Mbps; muxed audio supported |
| Fragmented MP4 | Initialization map plus media fragments from the same discontinuity epoch |
| Pushed assets | Ready, self-contained video assets uploaded through the existing media API |
| RTMP, SRT, RTSP, WebRTC | Use your existing gateway to create complete chunks, then push assets |
| Direct DASH, encrypted/DRM HLS, gap-marked segments, byte ranges, separate audio playlists, partial/delta LL-HLS | Not supported; use complete HLS segments or pushed assets |
Story0 selects a bounded rendition; it is not a broadcast host or playback CDN. Unsupported playlist features fail visibly instead of being silently ignored. A short source DVR window produces a short_dvr_window warning because queued work can miss evicted segments.
Push chunks from another stream protocol
Create a session with source: {"type": "push"}. Upload each complete chunk and wait until its asset is ready, then append it:
curl "https://api.story0.com/v1/streams/$STREAM_ID/windows" \
-H "Authorization: Bearer $STORY0_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {"asset_id": "asset_replace_with_ready_chunk"},
"sequence": 0,
"start_ms": 0,
"end_ms": 12000,
"wall_clock_start": "2026-10-28T18:00:00Z"
}'The response is 202 with a stream window and job_id; Location points to that job. Set the interval length from the asset's probed duration. Out-of-order unique sequences are accepted. Repeating the same sequence and body replays the same window; a different body or overlapping source time returns 409. Missing sequences are recorded when the stream ends, allowing normal reordered uploads to arrive first.
A raw .m4s fragment without its initialization data is not a complete pushed asset. Produce standalone MP4/WebM chunks, or combine initialization and fragments into a complete file before uploading. Your gateway is responsible for timestamps and identifying the source clock. Only send wall_clock_start when that clock is known.
An ffmpeg gateway can create complete MP4 chunks from an authorized RTSP/RTMP/SRT source:
ffmpeg -i "$INGEST_URL" \
-map 0:v:0 -map '0:a:0?' \
-c:v libx264 -preset veryfast -c:a aac \
-force_key_frames 'expr:gte(t,n_forced*10)' \
-f segment -segment_time 10 -reset_timestamps 1 \
-segment_format mp4 -segment_format_options movflags=+faststart \
'chunk-%06d.mp4'Upload only chunks the gateway has finished writing. segment_time is a target: use measured durations when computing start_ms and end_ms. WebRTC needs a gateway that can receive your WebRTC session; it is not a URL accepted by this ffmpeg example.
Process a completion
A window job contains a typed stream relation:
{
"operation": "stream_window",
"stream": {
"id": "strm_example",
"window_id": "win_example",
"sequence": 4,
"start_ms": 120000,
"end_ms": 150000,
"epoch": 0
},
"result": {"id": "ana_example", "object": "analysis", "url": "/v1/analyses/ana_example"}
}This is an excerpt, not a simulated inference result. Fetch the result after completion. Its evidence offsets are relative to the window asset:
session time in milliseconds = job.stream.start_ms + evidence.start × 1000The window resource records its source sequence bounds, epoch and optional wall-clock mapping. A discontinuity or source reset creates an epoch boundary. Unknown-duration gaps remain explicitly unknown; the observed-media clock stays monotone and does not invent missing time. Program date-time is exposed only when the playlist mapping is consistent. Request arrival and capture time are not the event's source time.
Window observations may describe the same real-world event more than once near boundaries. There is no cross-window semantic deduplication guarantee. Event IDs deduplicate deliveries; source windows deduplicate processing. Your application decides whether two observations refer to one sports highlight.
Replay, stop and recover
GET /v1/streams/{id}/windows?after_sequence=-1&limit=50 returns sequence-ordered windows, explicit gaps and next_after_sequence. With reordered pushed windows, advance a consumer checkpoint only past contiguous sequences. A late window with a lower sequence will not appear after a higher cursor. You can always refetch from -1 and deduplicate by window ID.
curl -X POST "https://api.story0.com/v1/streams/$STREAM_ID/stop" \
-H "Authorization: Bearer $STORY0_API_KEY"Stop prevents new paid admissions. Already admitted windows finish under their holds, so the session reports stopping until drained, then ended. An unadmitted partial window at explicit stop is recorded as a gap. Stop and ended states are not a cancellation of existing jobs; cancel those jobs separately if needed.
A source can be starting, running, waiting or reconnecting. Stalls, source errors, budget exhaustion, source completion and expiration end the session with a specific stop_reason. Events and window records remain available after it ends.
Limits and spending
- Window target: 10–60 seconds, default 30. HLS uses whole segments, so actual windows can vary up to 120 seconds.
- Source duration: 10–7,200 captured seconds, default 600. Wall-clock expiry is creation plus that limit plus 15 minutes.
- Idle timeout: 30–600 seconds, default 120; reconnect window: 0–600 seconds, default 120.
- One active stream per account, two after a credit purchase, and four globally per mode. Two analysis windows can be active per session.
- Each captured HLS window is limited to 32 segments and 64 MiB. Captured media expires after 24 hours, subject to active-job protection.
billing.max_chargeis mandatory. Optionalbilling.max_window_chargeapplies a second ceiling. Outstanding reservations plus settled charges cannot exceed the session cap.- Stream windows bill actual captured duration at the published stream-window price, without the ordinary 30-second minimum. Webhook retries do not repeat inference or charge again.
Backpressure waits for admitted windows. If source segments disappear while waiting, Story0 records a gap. Sustained analysis backpressure ends the session rather than claiming complete coverage. For high-value alerts, treat gaps and review.required as first-class application states.