Bolrach Stream API reference
Generated from the deployed API contract on 2026-09-08. 38 operations.
Use https://api.bolrach.com as your base URL. Keep bearer API keys on your server. Your application must authenticate users and check asset and upload ownership before forwarding requests. Browser video bytes go directly to signed R2 URLs.
Start with the logo branding guide for position, opacity, image validation and creator ownership. Referenced schemas are in the downloadable OpenAPI document.
Jump to an endpoint
- GET /v1/stream/health
- GET /v1/stream/docs
- GET /v1/stream/openapi.json
- GET /v1/stream/health/live
- POST /v1/stream/watermarks
- GET /v1/stream/watermarks
- GET /v1/stream/watermarks/{id}/image
- GET /v1/stream/assets/{assetId}/source
- POST /v1/stream/uploads
- GET /v1/stream/uploads/{uploadId}
- DELETE /v1/stream/uploads/{uploadId}
- POST /v1/stream/uploads/{uploadId}/parts/sign
- POST /v1/stream/uploads/{uploadId}/complete
- POST /v1/stream/uploads/{uploadId}/pause
- POST /v1/stream/uploads/{uploadId}/resume
- GET /v1/stream/assets
- GET /v1/stream/assets/{assetId}
- PATCH /v1/stream/assets/{assetId}
- POST /v1/stream/assets/{assetId}/captions
- POST /v1/stream/assets/{assetId}/playback-sessions
- DELETE /v1/stream/assets/playback-sessions/{sessionId}
- POST /v1/stream/live/channels
- GET /v1/stream/live/channels
- GET /v1/stream/live/channels/{channelId}
- POST /v1/stream/live/channels/{channelId}/keys/rotate
- POST /v1/stream/live/channels/{channelId}/playback-sessions
- POST /v1/stream/rooms
- GET /v1/stream/rooms
- POST /v1/stream/rooms/{roomId}/tokens
- GET /v1/stream/rooms/{roomId}/participants
- POST /v1/stream/rooms/{roomId}/broadcast
- DELETE /v1/stream/rooms/{roomId}
- POST /v1/stream/domains
- GET /v1/stream/domains
- GET /v1/stream/domains/{domainId}
- DELETE /v1/stream/domains/{domainId}
- POST /v1/stream/domains/{domainId}/verify
- POST /v1/stream/collect
/v1/stream/healthService health
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/docsHuman-readable documentation
Served publicly at https://docs.bolrach.com/stream.
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/openapi.jsonThis OpenAPI document
Public and unauthenticated: a spec you need a key to read is a spec nobody evaluates you on.
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/health/liveLiveness probe
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/watermarksUpload a reusable tenant-owned PNG or JPEG logo
Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Logo name"
},
"imageBase64": {
"type": "string",
"description": "Base64 PNG or JPEG, at most 1 MiB decoded"
}
},
"required": [
"imageBase64"
]
}
}
}
}Responses
{
"201": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Watermark ID used when creating an upload"
},
"name": {
"type": "string",
"description": "Logo name"
},
"bytes": {
"type": "integer",
"description": "Decoded file size"
}
}
}
}
}
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/watermarksList logos owned by this organisation
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/watermarks/{id}/imageGet a five-minute preview URL for an organisation-owned logo
Parameters
[
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/assets/{assetId}/sourceGet a tenant-owned original for provenance or archival inspection
Returns a 60-second signed R2 URL for a ready asset. Keep it server-side; never expose it as a public playback link. Deleted assets are unavailable.
Parameters
[
{
"name": "assetId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "Short-lived original download URL"
},
"bytes": {
"type": "integer",
"description": "Original size in bytes"
},
"expiresInSeconds": {
"type": "integer",
"description": "Expiry in seconds",
"example": 60
}
}
}
}
}
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/uploadsStart a resumable upload
Creates an upload session and an asset. The file is sent DIRECTLY to storage with the signed part URLs from `/parts/sign` — it never passes through this API, so upload size is limited by your plan rather than by any request timeout. Send `idempotencyKey` and a retry returns the SAME session instead of orphaning the first one and double-counting your quota.
Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"processingProfile": {
"type": "string",
"enum": [
"standard_vod",
"fast_social"
],
"default": "standard_vod",
"description": "fast_social caps landscape output at 1080p and portrait at 1920px height, retains lower bandwidth renditions, and skips synchronous VMAF telemetry. Original bytes are retained."
},
"watermark": {
"type": "object",
"properties": {
"watermarkId": {
"type": "string",
"format": "uuid",
"description": "ID returned by POST /watermarks; must belong to this organisation"
},
"position": {
"type": "string",
"enum": [
"tl",
"tr",
"bl",
"br"
],
"default": "br",
"description": "Top-left, top-right, bottom-left or bottom-right"
},
"opacity": {
"type": "number",
"minimum": 0.05,
"maximum": 0.9,
"default": 0.35
}
},
"required": [
"watermarkId"
]
},
"filename": {
"type": "string",
"description": "Original file name",
"example": "lecture-01.mp4"
},
"bytes": {
"type": "integer",
"description": "Exact size in bytes. Determines the part size and part count.",
"example": 734003200
},
"mimeType": {
"type": "string",
"description": "Source MIME type",
"example": "video/mp4"
},
"title": {
"type": "string",
"description": "Human title for the asset",
"example": "Lecture 1 — Introduction"
},
"externalRef": {
"type": "string",
"description": "Your own identifier, echoed back on the asset"
},
"idempotencyKey": {
"type": "string",
"description": "Retry-safe key. The same key always returns the same upload."
}
},
"required": [
"bytes"
]
}
}
}
}Responses
{
"201": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"uploadId": {
"type": "string",
"description": "Use this for every subsequent call"
},
"assetId": {
"type": "string",
"description": "The asset this upload will become"
},
"partSizeBytes": {
"type": "integer",
"description": "Split the file into parts of EXACTLY this size, except the last"
},
"partsExpected": {
"type": "integer",
"description": "How many parts to send"
},
"recommendedConcurrency": {
"type": "integer",
"description": "Parts to upload in parallel"
},
"expiresAt": {
"type": "string",
"description": "Session expiry (ISO 8601)"
}
}
}
}
}
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/uploads/{uploadId}Upload status and progress
Parameters
[
{
"name": "uploadId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/uploads/{uploadId}Abort an upload and release its quota
Parameters
[
{
"name": "uploadId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/uploads/{uploadId}/parts/signGet signed URLs for parts
Returns short-lived PUT URLs. Sign only the parts you are about to send; re-signing a part is safe and is how a resumed upload continues.
Parameters
[
{
"name": "uploadId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"partNumbers": {
"type": "array",
"items": {
"type": "integer"
},
"description": "1-based part numbers",
"example": [
1,
2,
3
]
}
},
"required": [
"partNumbers"
]
}
}
}
}Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/uploads/{uploadId}/completeFinish the upload
Verifies every part arrived, then starts probing and encoding. Returns an error naming the missing parts rather than completing a broken object.
Parameters
[
{
"name": "uploadId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/uploads/{uploadId}/pausePause an upload
Parameters
[
{
"name": "uploadId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/uploads/{uploadId}/resumeResume a paused upload
Parameters
[
{
"name": "uploadId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/assetsList your assets
Newest first. Filter with `?status=ready`. Upload without read is not a usable API — this exists because a real integration had no way to ask whether a video was ready.
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/assets/{assetId}Read one asset
Carries `status`, `progress_percent`, `current_stage` and, on failure, a `failure_summary` you can show a user. While still uploading it also returns `upload_id`, so an interrupted client can finish the upload it started. While processing it also returns `encoding` — whether anything is happening to THIS video yet — plus `queuePosition`, `stageLabel` and `estimatedReadyAt`. Show the customer the difference between encoding and waiting: a queue is not progress. `estimatedReadyAt` is null when there is not enough history to estimate honestly, and `etaBasis` states what it was based on. Use `stage` for anything shown to a user: it is plain English, where `current_stage` is our internal enum. `progress_percent` is reported as 0 while `encoding` is false — it tracks pipeline stages rather than encode completion, and showing 40% for a video that has not started is a bar that lies.
Parameters
[
{
"name": "assetId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/assets/{assetId}Change who can watch an asset
Sets `visibility` to one of `private`, `signed`, `unlisted` or `public`. Anything else is refused rather than ignored, so a typo cannot silently leave a video open. Narrowing visibility also removes the tokenless poster copy in the same request. That copy only exists while an asset is openly visible, and leaving it behind would mean the permission was gone but the object that granted it was not. The response carries the new `visibility` and the resulting `poster` state.
Parameters
[
{
"name": "assetId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"visibility": {
"type": "string",
"description": "private, signed, unlisted or public",
"example": "signed"
}
},
"required": [
"visibility"
]
}
}
}
}Responses
{
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Asset id"
},
"visibility": {
"type": "string",
"description": "The visibility now in force"
}
}
}
}
}
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/assets/{assetId}/captionsRequest captions for a published asset
Queues transcription and returns immediately — captions are not ready when this responds. The job is scheduled below encoding, because accessibility arriving minutes late beats publication arriving late. The asset must be `ready`; captioning something still processing is refused with `asset_not_ready`. **English only** — the installed model produces English, so any other language is refused with `language_unsupported` rather than returning English text stored under a French label. Asking twice for the same asset and language does not queue the work twice.
Parameters
[
{
"name": "assetId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"language": {
"type": "string",
"description": "Only `en` is supported today",
"example": "en"
}
}
}
}
}
}Responses
{
"201": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"assetId": {
"type": "string",
"description": "Asset id"
},
"language": {
"type": "string",
"description": "Language that was queued"
},
"requestId": {
"type": "string",
"description": "Scheduler request id"
},
"state": {
"type": "string",
"description": "Queue state at the moment of the response"
},
"captionPath": {
"type": "string",
"description": "Where the .vtt will appear once the job completes",
"example": "captions/en.vtt"
}
}
}
}
}
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/assets/{assetId}/playback-sessionsMint a viewer playback token
Returns a short-lived signed token plus ready-to-use HLS and embed URLs. The token is **bound to the hostname** you pass as `host`: a token minted for one host is refused on another, which is what stops one custom-domain tenant replaying another's token. Mint per viewer, never share one token between viewers.
Parameters
[
{
"name": "assetId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"host": {
"type": "string",
"description": "Hostname the token will be presented on",
"example": "media.bolrach.video"
},
"expiresInSeconds": {
"type": "integer",
"description": "60 to 86400. Default 3600.",
"example": 3600
},
"viewerId": {
"type": "string",
"description": "Your identifier for this viewer, for analytics and concurrency limits"
},
"allowedCountries": {
"type": "array",
"items": {
"type": "string"
},
"description": "ISO 3166-1 alpha-2. Empty means everywhere."
},
"blockedCountries": {
"type": "array",
"items": {
"type": "string"
},
"description": "ISO 3166-1 alpha-2."
},
"maxConcurrent": {
"type": "integer",
"description": "Maximum simultaneous SESSIONS this viewerId may hold. Minting beyond it returns 409 viewer_concurrency_reached. Requires viewerId. Note this caps sessions, not players: one issued token opened by several people at once counts as one session."
}
}
}
}
}
}Responses
{
"201": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"sessionId": {
"type": "string",
"description": "Revoke with DELETE /assets/playback-sessions/{sessionId}"
},
"token": {
"type": "string",
"description": "The signed viewer token"
},
"hlsUrl": {
"type": "string",
"description": "Direct HLS manifest URL, token included"
},
"embedUrl": {
"type": "string",
"description": "Drop this in an iframe"
},
"expiresAt": {
"type": "string",
"description": "ISO 8601"
},
"durationSeconds": {
"type": "integer",
"description": "Asset duration"
}
}
}
}
}
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/assets/playback-sessions/{sessionId}Revoke a playback session immediately
Parameters
[
{
"name": "sessionId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/live/channelsCreate a live channel
Returns ingest URLs and stream keys **once**. Only hashes are stored, so a lost key must be rotated rather than recovered. ⚠ The RTMP port is **1936**, not the default 1935. Use the URL exactly as returned. Live is currently PASSTHROUGH: viewers receive your source quality with no adaptive ladder.
Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Channel name",
"example": "Main Stage"
},
"slug": {
"type": "string",
"description": "URL-safe identifier. Derived from the name if omitted."
},
"record": {
"type": "boolean",
"description": "Record broadcasts into ordinary on-demand assets. Default true."
},
"dvrSeconds": {
"type": "integer",
"description": "DVR window in seconds",
"example": 7200
}
},
"required": [
"name"
]
}
}
}
}Responses
{
"201": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"channelId": {
"type": "string",
"description": "Channel id"
},
"ingest": {
"type": "object",
"properties": {
"rtmp": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "rtmp://ingest.bolrach.video:1936/live"
},
"streamKey": {
"type": "string",
"description": "Shown once"
}
}
},
"backup": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "Same URL"
},
"streamKey": {
"type": "string",
"description": "Failover key — the supported way to take over"
}
}
},
"srt": {
"type": "string",
"description": "SRT ingest URL"
}
}
},
"playback": {
"type": "object",
"properties": {
"hls": {
"type": "string",
"description": "LL-HLS playback URL"
}
}
}
}
}
}
}
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/live/channelsList live channels
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/live/channels/{channelId}Channel status
State is `idle`, `connecting` or `live`. `live` means a viewer can actually watch right now — it is asserted against the packager output, not merely against a publisher having connected.
Parameters
[
{
"name": "channelId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/live/channels/{channelId}/keys/rotateRotate a stream key
Returns the new key and the full ingest URL, so nobody has to remember the non-default port while re-pointing an encoder under pressure.
Parameters
[
{
"name": "channelId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"which": {
"type": "string",
"enum": [
"primary",
"backup"
],
"description": "Default primary"
}
}
}
}
}
}Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/live/channels/{channelId}/playback-sessionsMint a viewer token for a live channel
Same controls as an on-demand session. A live token is refused on an on-demand path and vice versa.
Parameters
[
{
"name": "channelId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/roomsCreate an interactive room
A real-time room where participants publish and subscribe to each other. Issue a token per participant with `/tokens`.
Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Room name",
"example": "Weekly standup"
},
"maxParticipants": {
"type": "integer",
"description": "Capped by your plan"
},
"record": {
"type": "boolean",
"description": "Record the room"
},
"emptyTimeoutSeconds": {
"type": "integer",
"description": "Close the room this long after the last participant leaves",
"example": 300
}
},
"required": [
"name"
]
}
}
}
}Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/roomsList rooms
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/rooms/{roomId}/tokensIssue a participant join token
Mint one per person. `role` is enforced, not advisory: a `viewer` token cannot publish media.
Parameters
[
{
"name": "roomId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"identity": {
"type": "string",
"description": "Your identifier for this participant"
},
"displayName": {
"type": "string",
"description": "Shown to other participants"
},
"role": {
"type": "string",
"enum": [
"host",
"participant",
"viewer"
],
"description": "viewer cannot publish"
},
"ttlSeconds": {
"type": "integer",
"description": "60 to 43200. Default 3600."
}
}
}
}
}
}Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/rooms/{roomId}/participantsWho is in the room
Reconciled against the media server, which is the authority — not against our own bookkeeping.
Parameters
[
{
"name": "roomId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/rooms/{roomId}/broadcastBroadcast a room to a live channel
Composites the room and publishes it to one of your live channels through the ordinary live ingest, so it gets the same recording, metering and playback as any other broadcast.
Parameters
[
{
"name": "roomId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"channelId": {
"type": "string",
"description": "Target live channel"
},
"streamKey": {
"type": "string",
"description": "That channel's stream key"
}
},
"required": [
"channelId",
"streamKey"
]
}
}
}
}Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/rooms/{roomId}End a room and disconnect everyone
Parameters
[
{
"name": "roomId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/domainsAdd a custom playback domain
Serve media from your own hostname. Returns the DNS record to create; the domain is not activated until control is proven.
Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"hostname": {
"type": "string",
"description": "e.g. video.example.com"
}
},
"required": [
"hostname"
]
}
}
}
}Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/domainsList custom domains
Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/domains/{domainId}Domain status
Parameters
[
{
"name": "domainId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/domains/{domainId}Remove a custom domain
Parameters
[
{
"name": "domainId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"200": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/domains/{domainId}/verifyCheck verification and issue a certificate
Parameters
[
{
"name": "domainId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
]Responses
{
"201": {
"description": "Success"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}/v1/stream/collectPlayer QoE beacon
Called by the player, not by your server. Authenticated by the playback token in the body — the only credential safe to put in a page.
Responses
{
"204": {
"description": "Accepted without a response body"
},
"400": {
"description": "Invalid request or plan limit exceeded"
},
"401": {
"description": "Missing or invalid API key"
},
"403": {
"description": "Not permitted for this tenant"
},
"404": {
"description": "Not found"
},
"409": {
"description": "Conflicting state or concurrency limit"
},
"429": {
"description": "Rate limited"
}
}