API reference
Every endpoint, parameter and field of the JustOnAir API, generated from openapi.json.
All endpoints live at https://api.justonair.com. Authenticate with Authorization: Bearer <API key>. Requests and responses are JSON. Times are ISO 8601 UTC; money is a decimal string in USD.
This page is generated from openapi.json. Import that file into Postman, an SDK generator or an agent framework to get the same thing as code.
Streams
Create, read, change and end streams.
Create a stream
POST /v1/streamsReserves ingest capacity for 30 minutes and returns everything needed to go live: rtmp_url + stream_key, whip_url, playback_url and embed_url. The secrets are returned only here. Send an Idempotency-Key so a retry never creates a second stream.
| Parameter | In | Type | Description |
|---|---|---|---|
Idempotency-Key | header | string | Any unique string, e.g. a UUID. A retry with the same key and body returns the first response (header Idempotent-Replayed: true) for 24 h. |
Body (optional)
| Field | Type | Description |
|---|---|---|
name | string | null | Up to 100 characters. Blank means no name. |
profile | "abr_basic" | "passthrough" | Default abr_basic. |
max_resolution | 480 | 720 | 1080 | The tallest video you will send. Default 720. 1080 needs a trusted account. Sending more than you declare ends the stream after a 20 s warning. |
record | boolean | Make an MP4 after the stream ends. Default true. |
recording_retention_days | integer | null | Days to keep the MP4, 1–3650. Omit for the account default (30). Null: keep until deleted. |
metadata | object | Your own JSON, up to 2 KB. |
player | PlayerSettings |
Responses
| Status | Body | Meaning |
|---|---|---|
| 201 | CreatedStream | Created. Store stream_key and whip_url now. |
| 400 | Error | Invalid request (invalid_request). |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 402 | Error | Credit below the 1.00 USD minimum (insufficient_credit). |
| 403 | Error | Account pending approval or suspended (account_pending, tenant_suspended). |
| 422 | Error | This Idempotency-Key was used with a different body (idempotency_key_reused). |
| 429 | Error | An account limit: limit_pending_streams, limit_creation_rate, limit_ingest_hours, limit_resolution. |
| 503 | Error | No ingest capacity right now (no_capacity). Retry shortly. |
List streams
GET /v1/streamsNewest first.
| Parameter | In | Type | Description |
|---|---|---|---|
limit | query | integer | |
starting_after | query | string | Id of the last stream on the previous page. |
status | query | "pending" | "live" | "ended" | "expired" | "cancelled" |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | StreamList | A page of streams. |
| 400 | Error | Invalid request (invalid_request). |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 403 | Error | Account pending approval or suspended (account_pending, tenant_suspended). |
Get a stream
GET /v1/streams/{id}Status, what is arriving (ingest), the recording, and cost so far. Poll it to follow a stream; every 3–5 s is fine.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | Stream | The stream. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 403 | Error | Account pending approval or suspended (account_pending, tenant_suspended). |
| 404 | Error | No such stream in your account (not_found). |
Update a stream
PATCH /v1/streams/{id}Rename it, replace its metadata, or change player settings. Works in any status.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Body. Only these fields can change, in any status. Null clears a field; metadata is replaced whole.
| Field | Type | Description |
|---|---|---|
name | string | null | Up to 100 characters. |
metadata | object | null | |
player | PlayerSettings |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | Stream | The stream. |
| 400 | Error | Invalid request (invalid_request). |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 403 | Error | Account pending approval or suspended (account_pending, tenant_suspended). |
| 404 | Error | No such stream in your account (not_found). |
End or cancel a stream
DELETE /v1/streams/{id}Live: ends it and disconnects the publisher within seconds. Pending: cancels it and releases the slot. Already finished: returns it unchanged.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | Stream | The stream, now ended or cancelled. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 403 | Error | Account pending approval or suspended (account_pending, tenant_suspended). |
| 404 | Error | No such stream in your account (not_found). |
Replace a lost stream key
POST /v1/streams/{id}/stream-keyOnly while pending. The old key and WHIP URL stop working at once.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | CreatedStream | The stream with new stream_key and whip_url. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such stream in your account (not_found). |
| 409 | Error | Live or finished (stream_not_pending). |
Viewers per minute
GET /v1/streams/{id}/viewersThe viewer curve: live estimates from the hosted player, and exact counts from the CDN logs about an hour later.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | ViewerSeries | Per-minute counts from both sources. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such stream in your account (not_found). |
Playback
Signed URLs for your own player.
Sign a playback URL
POST /v1/streams/{id}/playback-tokenA fresh signed HLS URL, for example a short one per viewer minted by your backend. Refused once the stream is finished.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Body (optional)
| Field | Type | Description |
|---|---|---|
expires_in | integer | Seconds. Default: until the stream could no longer be live, plus an hour. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | PlaybackToken | Signed URL. |
| 400 | Error | Invalid request (invalid_request). |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such stream in your account (not_found). |
| 409 | Error | The stream has ended (stream_ended). There is no replay. |
Recordings
The MP4 made after a stream ends.
Delete the recording
DELETE /v1/streams/{id}/recordingDeletes the MP4 now, or marks it to be deleted as soon as it exists.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | Stream | The stream; recording.status is deleted or deleting. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such stream in your account (not_found). |
Get a download link
POST /v1/streams/{id}/recording/download| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Body (optional)
| Field | Type | Description |
|---|---|---|
expires_in | integer |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | RecordingDownload | A signed link to the MP4. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such stream in your account (not_found). |
| 409 | Error | Not ready (recording_not_ready, with details.status). |
Account
Balance and usage.
Balance and usage
GET /v1/usage| Parameter | In | Type | Description |
|---|---|---|---|
days | query | integer |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | Usage | Balance, rates and usage per UTC day. |
| 400 | Error | Invalid request (invalid_request). |
| 401 | Error | Missing or invalid API key (unauthorized). |
Webhooks
Endpoints that get stream and recording events, and their delivery log.
List webhook endpoints
GET /v1/webhooksWithout secrets. Also returns event_types, every type an endpoint can subscribe to.
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | WebhookEndpointList | Your endpoints. |
| 401 | Error | Missing or invalid API key (unauthorized). |
Add a webhook endpoint
POST /v1/webhooksUp to 5 per account. The answer includes secret; see Webhooks for verifying signatures.
Body
| Field | Type | Description |
|---|---|---|
url | string | https URL on the public internet, up to 2000 characters. |
events | array of "stream.live" | "stream.ended" | "stream.expired" | "stream.cancelled" | "recording.ready" | "recording.failed" | "recording.deleted" | Leave out for all events, including ones added later. |
description | string | Up to 200 characters. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 201 | WebhookEndpoint | The endpoint, with its secret. |
| 400 | Error | Invalid request (invalid_request). |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 429 | Error | 5 endpoints already (limit_webhook_endpoints). |
Get a webhook endpoint
GET /v1/webhooks/{id}With its secret.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Endpoint id (whk_…). |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | WebhookEndpoint | The endpoint. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such endpoint (not_found). |
Change a webhook endpoint
PATCH /v1/webhooks/{id}| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Endpoint id (whk_…). |
Body
| Field | Type | Description |
|---|---|---|
url | string | |
events | array of "stream.live" | "stream.ended" | "stream.expired" | "stream.cancelled" | "recording.ready" | "recording.failed" | "recording.deleted" | |
description | string | null | |
enabled | boolean | False stops sending; queued deliveries fail. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | WebhookEndpoint | The endpoint. |
| 400 | Error | Invalid request (invalid_request). |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such endpoint (not_found). |
Delete a webhook endpoint
DELETE /v1/webhooks/{id}Its delivery log goes with it.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Endpoint id (whk_…). |
Responses
| Status | Body | Meaning |
|---|---|---|
| 204 | Deleted. | |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such endpoint (not_found). |
Rotate the signing secret
POST /v1/webhooks/{id}/secretThe old secret keeps signing alongside the new one for 24 hours.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Endpoint id (whk_…). |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | WebhookEndpoint | The endpoint with the new secret. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such endpoint (not_found). |
Send a test event
POST /v1/webhooks/{id}/testQueues a webhook.test; it is sent within a couple of seconds.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Endpoint id (whk_…). |
Responses
| Status | Body | Meaning |
|---|---|---|
| 202 | WebhookDelivery | The queued delivery. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such endpoint (not_found). |
Delivery log
GET /v1/webhooks/{id}/deliveriesNewest first, kept 30 days.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Endpoint id (whk_…). |
limit | query | integer | |
status | query | "pending" | "succeeded" | "failed" |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | WebhookDeliveryList | Deliveries, newest first. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No such endpoint (not_found). |
Send a delivery again
POST /v1/webhooks/{id}/deliveries/{delivery_id}/retryA failed delivery gets one more attempt now; a pending one is brought forward.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Endpoint id (whk_…). |
delivery_id | path | string | dlv_… |
Responses
| Status | Body | Meaning |
|---|---|---|
| 202 | WebhookDelivery | The delivery. |
| 401 | Error | Missing or invalid API key (unauthorized). |
| 404 | Error | No pending or failed delivery with that id (not_found). |
Embed
The hosted player’s public read.
Public stream status (hosted player)
GET /v1/embed/{id}No key needed; any web page may call it. Cached for a few seconds; the same read is served, cached at the CDN edge, at https://play.joacdn.com/api/embed/{id}. Rate limited per IP on unknown ids (60/min).
No API key needed.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 200 | Embed | Status and, while live, a signed URL. |
| 404 | Error | Unknown stream. |
| 429 | Error | Too many requests (rate_limited). |
Report a viewer (sampled)
POST /v1/embed/{id}/beatSent by the hosted player, or by your own player to be counted live. Draw u once per page load; report every beacon.interval_seconds while u is below beacon.rate from the embed read. No body, so navigator.sendBeacon(url) works from any page.
No API key needed.
| Parameter | In | Type | Description |
|---|---|---|---|
id | path | string | Stream id. |
s | query | string | Random id for this page load. |
st | query | "w" | "p" | "z" | "b" | Waiting, playing, paused or buffering. |
u | query | number | The random draw, 0 ≤ u < 1. |
Responses
| Status | Body | Meaning |
|---|---|---|
| 204 | Counted (or the stream is over and there is nothing to count). | |
| 400 | Error | Invalid request (invalid_request). |
| 404 | Error | Unknown stream. |
| 429 | Error | Too many requests (rate_limited). |
Objects
Stream
| Field | Type | Description |
|---|---|---|
id | string | Stream id. Also the folder of its video on the CDN. |
object | string | Always stream. |
name | string | null | Your label, up to 100 characters. Shown on the hosted player unless player.show_name is false. |
status | "pending" | "live" | "ended" | "expired" | "cancelled" | pending (waiting for the publisher, up to 30 min), live, ended, expired (nothing connected in time) or cancelled (deleted before going live). |
profile | "abr_basic" | "passthrough" | abr_basic: several qualities for viewers. passthrough: exactly what you send. |
record | boolean | Whether an MP4 is made after the stream ends. |
recording | Recording | |
node_ready | boolean | The ingest server is up. False for about a minute when a new server has to start. |
rtmp_url | string | null | RTMP server URL for OBS or ffmpeg. Use with stream_key. |
playback_url | string | null | Signed HLS URL for your own player. Null once the stream is finished (there is no replay). Re-signed on every read. |
playback_url_expires_at | string | null | When playback_url stops working. |
embed_url | string | The hosted player page. Put it in an iframe or share it as a link. |
renditions | array of string | Qualities viewers will get, e.g. ["source", "480p"]. |
max_ingest_resolution | integer | The tallest video you declared you will send: 480, 720 or 1080. |
max_duration_seconds | integer | The stream ends after this long live. 4 h for new accounts, 24 h for trusted ones. |
metadata | object | null | Your own JSON, up to 2 KB. Never shown to viewers. |
player | PlayerSettings | |
reservation_expires_at | string | null | While pending: when the reserved slot is released and the stream expires. |
reservation_expires_in_seconds | integer | null | The same as a countdown, computed by the server. |
created_at | string | |
went_live_at | string | null | |
ended_at | string | null | |
end_reason | string | null | Why it ended: deleted, disconnected, duration_limit, reservation_ttl, limit_bitrate, limit_resolution, insufficient_credit, simulcast_not_supported, provision_failed. |
ingest | Ingest | |
cost | Cost | |
viewers | Viewers | |
viewers_now | integer | null | Watching on the hosted player right now (the same estimate as viewers.now.watching). Null unless the stream is pending or live and someone is on the player. Also in the list, so one call shows every live audience. |
CreatedStream
A stream plus its secrets. Returned once, by the create call (and by replacing the key). Store them; they are never shown again.
| Field | Type | Description |
|---|---|---|
id | string | Stream id. Also the folder of its video on the CDN. |
object | string | Always stream. |
name | string | null | Your label, up to 100 characters. Shown on the hosted player unless player.show_name is false. |
status | "pending" | "live" | "ended" | "expired" | "cancelled" | pending (waiting for the publisher, up to 30 min), live, ended, expired (nothing connected in time) or cancelled (deleted before going live). |
profile | "abr_basic" | "passthrough" | abr_basic: several qualities for viewers. passthrough: exactly what you send. |
record | boolean | Whether an MP4 is made after the stream ends. |
recording | Recording | |
node_ready | boolean | The ingest server is up. False for about a minute when a new server has to start. |
rtmp_url | string | null | RTMP server URL for OBS or ffmpeg. Use with stream_key. |
playback_url | string | null | Signed HLS URL for your own player. Null once the stream is finished (there is no replay). Re-signed on every read. |
playback_url_expires_at | string | null | When playback_url stops working. |
embed_url | string | The hosted player page. Put it in an iframe or share it as a link. |
renditions | array of string | Qualities viewers will get, e.g. ["source", "480p"]. |
max_ingest_resolution | integer | The tallest video you declared you will send: 480, 720 or 1080. |
max_duration_seconds | integer | The stream ends after this long live. 4 h for new accounts, 24 h for trusted ones. |
metadata | object | null | Your own JSON, up to 2 KB. Never shown to viewers. |
player | PlayerSettings | |
reservation_expires_at | string | null | While pending: when the reserved slot is released and the stream expires. |
reservation_expires_in_seconds | integer | null | The same as a countdown, computed by the server. |
created_at | string | |
went_live_at | string | null | |
ended_at | string | null | |
end_reason | string | null | Why it ended: deleted, disconnected, duration_limit, reservation_ttl, limit_bitrate, limit_resolution, insufficient_credit, simulcast_not_supported, provision_failed. |
ingest | Ingest | |
cost | Cost | |
viewers | Viewers | |
viewers_now | integer | null | Watching on the hosted player right now (the same estimate as viewers.now.watching). Null unless the stream is pending or live and someone is on the player. Also in the list, so one call shows every live audience. |
stream_key | string | RTMP stream key, {id}?key={secret}. OBS: paste as Stream Key with rtmp_url as Server. One-shot: works for this stream only. |
whip_url | string | WHIP endpoint for browsers and WebRTC encoders. Contains the secret. |
Ingest
What the ingest node last measured arriving. Null until the first measurement.
| Field | Type | Description |
|---|---|---|
width | integer | null | Frame width in pixels. |
height | integer | null | Frame height in pixels. |
kbps | integer | null | Bitrate of everything received (video, audio, overhead), averaged over about 30 s. |
observed_at | string | When this was measured. |
limit_warning | string | null | Set while a limit is broken: limit_bitrate or limit_resolution. Still broken 20 s later, the stream ends with that end_reason. |
keyframe_interval_seconds | number | null | Average seconds between the publisher’s keyframes. Null for browser (WHIP) publishers. |
keyframe_warning | boolean | True above 2.5 s. Set your encoder to a 2 s keyframe interval. A warning only; never ends a stream. |
Recording
The recording: one MP4 of the top quality, made after the stream ends. Null while pending.
| Field | Type | Description |
|---|---|---|
status | "recording" | "processing" | "ready" | "deleting" | "deleted" | "failed" | "not_recorded" | "none" | "legacy" | recording while live, then processing, ready, deleting, deleted, failed, not_recorded (record: false) or none (never went live). |
bytes | integer | null | MP4 size. |
duration_seconds | number | null | MP4 length. |
retention_days | integer | null | Days kept after it is ready. Null: until you delete it. |
ready_at | string | null | When the MP4 became downloadable. |
delete_at | string | null | When it will be deleted: the retention date, or 7 days after the balance reached zero, whichever is earlier. |
delete_reason | string | null | retention or zero_balance. |
deleted_at | string | null | When it was deleted. |
Cost
What this stream has cost so far at current rates. Only on GET /v1/streams/{id}. Amounts are decimal strings.
| Field | Type | Description |
|---|---|---|
total_usd | string | Everything below. |
delivery_bytes | integer | Bytes delivered to viewers, counted up to delivery_counted_until. |
delivery_usd | string | Delivery cost. |
delivery_counted_until | string | null | CDN logs are read an hour at a time, so delivery trails reality by up to about 80 minutes. |
ingest_seconds | integer | Seconds of publishing. |
ingest_usd | string | Encoding (abr_basic) or passthrough cost. |
storage_gb_hours | number | Recording storage used. |
storage_usd | string | Recording storage cost. |
Viewers
Viewer counts. While the stream runs, now is estimated from the hosted player: exact up to about 400 viewers, within about 5% above. After the fact, the CDN logs give the exact count for every player (hosted, your own, apps), about an hour later; peak and watch_minutes use them once they exist. Only on GET /v1/streams/{id}.
| Field | Type | Description |
|---|---|---|
now | object | null | Live estimate from the hosted player. Null when nobody is on the player or the stream is over. |
peak | integer | null | Most viewers in any one minute. |
peak_at | string | null | The start of that minute. |
watch_minutes | integer | null | Total minutes watched, all viewers together. |
source | "cdn" | "player" | null | Where peak and watch_minutes come from: cdn (every player, exact) or player (hosted player estimate, until the CDN count arrives). |
cdn_counted_until | string | null | CDN logs are read up to this time. |
ViewerSeries
| Field | Type | Description |
|---|---|---|
object | string | Always viewer_series. |
stream_id | string | Stream id. Also the folder of its video on the CDN. |
player | array of object | Per minute, from the hosted player (live estimate). |
cdn | array of object | Per minute, from the CDN logs: average viewers watching at once, every player included. |
countries | array of object | Where the watch time came from, from the CDN logs (every player), largest first, up to 30. Grows hour by hour while the stream runs. |
PlayerSettings
| Field | Type | Description |
|---|---|---|
show_name | boolean | Show the stream name to viewers on the hosted player. Default true. False keeps the name out of the public embed read entirely. |
StreamList
| Field | Type | Description |
|---|---|---|
object | string | Always list. |
data | array of Stream | |
has_more | boolean | More streams after the last one. Pass its id as starting_after. |
PlaybackToken
| Field | Type | Description |
|---|---|---|
object | string | Always playback_token. |
stream_id | string | Stream id. Also the folder of its video on the CDN. |
playback_url | string | Signed HLS URL. |
expires_at | string | null |
RecordingDownload
| Field | Type | Description |
|---|---|---|
object | string | Always recording_download. |
stream_id | string | Stream id. Also the folder of its video on the CDN. |
url | string | Signed link to the MP4 on the CDN. Downloading it counts as delivery. |
bytes | integer | MP4 size. |
expires_at | string |
Embed
What the hosted player needs. Public: no key, any origin may read it.
| Field | Type | Description |
|---|---|---|
object | string | Always embed. |
id | string | Stream id. Also the folder of its video on the CDN. |
status | "pending" | "live" | "ended" | "unavailable" | pending, live, ended, or unavailable (expired or cancelled). |
title | string | null | The stream name when it is to be shown, otherwise null. |
playback_url | string | null | While live: a signed HLS URL, the same for every viewer. |
playback_url_expires_at | string | null | |
went_live_at | string | null | |
ended_at | string | null | |
poll_after_seconds | integer | null | Ask again after this many seconds (5 pending, 60 live). Null: stop asking. |
beacon | object | null | Viewer counting: a player whose random number is below rate reports every interval_seconds (see Count viewers from your own player). Null once the stream is over. |
Usage
| Field | Type | Description |
|---|---|---|
object | string | Always usage. |
balance_usd | string | Credit minus everything charged so far. |
available_usd | string | Balance minus used-but-not-yet-charged. Live streams end when this reaches zero. |
balance_depleted_at | string | null | Set while the balance is at or below zero. |
recordings_delete_at | string | null | 7 days after the balance reached zero: when recordings will be deleted unless you top up. |
rates | object | |
days | array of object |
WebhookEndpoint
| Field | Type | Description |
|---|---|---|
object | string | Always webhook_endpoint. |
id | string | whk_… |
url | string | Where events are POSTed. https only. |
description | string | null | Your own note. |
events | array of "stream.live" | "stream.ended" | "stream.expired" | "stream.cancelled" | "recording.ready" | "recording.failed" | "recording.deleted" | Event types sent to this endpoint. |
all_events | boolean | True when subscribed to everything, including event types added later. |
enabled | boolean | False: nothing is sent. |
created_at | string | |
secret | string | whsec_…, for verifying signatures. Only on create, GET /v1/webhooks/{id} and rotation. |
WebhookDelivery
| Field | Type | Description |
|---|---|---|
object | string | Always webhook_delivery. |
id | string | dlv_… |
event_id | string | evt_…, the webhook-id header. |
event_type | string | |
stream_id | string | null | |
status | "pending" | "succeeded" | "failed" | |
attempts | integer | |
max_attempts | integer | |
next_attempt_at | string | null | |
last_attempt_at | string | null | |
last_status_code | integer | null | What your server answered. |
last_error | string | null | Why the last attempt failed (answered 500, a timeout, a DNS error, …). |
last_duration_ms | integer | null | |
delivered_at | string | null | |
created_at | string | |
payload | WebhookEvent |
WebhookEvent
The body of every webhook. Headers: webhook-id, webhook-timestamp, webhook-signature (Standard Webhooks).
| Field | Type | Description |
|---|---|---|
type | "stream.live" | "stream.ended" | "stream.expired" | "stream.cancelled" | "recording.ready" | "recording.failed" | "recording.deleted" | "webhook.test" | |
timestamp | string | When it happened. |
data | object | For stream and recording events, the stream at that moment. For webhook.test, the endpoint. |
Error
| Field | Type | Description |
|---|---|---|
error | object |