API reference
A clean JSON API to manage streams and destinations, pull recordings, clips, analytics and highlights, and drive the unified cross-platform chat — programmatically.
Base URL
https://streamrepeater.com/api
https://streamrepeater.com/api/openapi.json.
Import it into Postman, Insomnia, Swagger or any OpenAPI tool to get
an interactive client with every endpoint, parameter and example — or generate a typed SDK from it.
curl https://streamrepeater.com/api/openapi.json -o streamrepeater-openapi.json
Authentication
All endpoints are authenticated using a Sanctum Personal Access Token. You can generate or revoke these tokens from your dashboard under Settings → API tokens. Requests are rate-limited to 60 / minute per IP, and all errors are returned as JSON.
Send the token in the Authorization header as a Bearer token:
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" https://streamrepeater.com/api/channels
Your account is scoped to the workspaces you belong to. Tokens can be read-only or read/write. Mutating stream and destination endpoints require a write token and the Manage streams role in that workspace. Workspace and stream provisioning endpoints are admin-only and are documented in the admin console.
Errors & conventions
Errors return a JSON body with a message; validation failures (422) also include an errors map keyed by field. Other common statuses: 401 (missing/invalid token), 403 (token lacks the ability, role or workspace access), 404 (not found or not visible to you). Growing feeds — chat and events — accept ?after=<id> to poll only newer rows.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /tenants | List workspaces you can access. |
| GET | /channels | List streams with their tenant, destinations and relay status. |
| GET | /channels/{channel}/ingest | Ingest + playback URLs and keys for a stream. |
| GET | /channels/{channel}/status | Current channel status, live viewer count and destination relay states. |
| GET | /channels/{channel}/destinations | List a stream's outbound destinations. |
| POST | /channels/{channel}/destinations | Add a destination (protocol, url, name). |
| PUT | /channels/{channel}/destinations/{destination} | Update a destination's name, protocol, URL and optional video settings. |
| PATCH | /channels/{channel}/destinations/{destination}/toggle | Enable or disable a destination with an explicit enabled boolean. |
| DELETE | /channels/{channel}/destinations/{destination} | Disable and flag a destination for watchdog removal. |
| DELETE | /channels/{channel} | Disable and flag a channel and its destinations for watchdog removal. |
| GET | /channels/{channel}/relays | Per-destination relay status with the latest events. |
| PATCH | /channels/{channel} | Update a stream's title, enabled flag or watch-page title. |
| GET | /channels/{channel}/recordings | List the stream's recorded VOD segments. |
| GET | /channels/{channel}/clips | List clips created from the stream's recordings. |
| GET | /channels/{channel}/analytics | Last 24h of bandwidth (in/out bps) and viewer samples. |
| GET | /channels/{channel}/highlights | Auto-detected chat-spike highlight moments. |
| GET | /tenants/{tenant}/chat | Recent unified-chat messages incl. emotes (poll with ?after=<id>). |
| POST | /tenants/{tenant}/chat/reply | Send a chat message as the broadcaster (platform, message). |
| POST | /tenants/{tenant}/chat/messages/{message}/moderate | Moderate a message: delete, timeout or ban. |
| GET | /tenants/{tenant}/integrations | List connected platforms (Twitch/Kick/YouTube/…) and their status. |
| GET | /tenants/{tenant}/automations | List automation rules with their event type, conditions and actions. |
| GET | /tenants/{tenant}/automation-runs | Recent automation-rule run history (status per fired event). |
| GET | /tenants/{tenant}/events | Recent event feed (poll with ?after=<id>). |
| POST | /tenants/{tenant}/events | Fire a custom event into the rules engine (type, context). |
| GET | /tenants/{tenant}/devices | List connected LAN devices (via the Local Agent). |
| GET | /tenants/{tenant}/connectors | Local Agent fleet: version, liveness, 3D-printer status, Now Playing track and self-update rollback state. |
| GET | /channels/{channel}/lan-sources | LAN ingest sources on a channel (source URL is credential-masked). |
| GET | /tenants/{tenant}/games | Recent stream-game sessions + an all-time wins leaderboard. |
| GET | /tenants/{tenant}/raffles | Prize-draw campaigns (metadata + counts only — never the entrant data). |
| GET | /tenants/{tenant}/loops | Always-on loop sources across the workspace. |
| GET | /tenants/{tenant}/music | The royalty-free music library + this workspace's radio settings and player URL. |
| POST | /channels/{channel}/clips | Create a clip from a recording (source_start, start_offset_seconds, duration_seconds). |
| GET | /tenants/{tenant}/webhooks | List developer webhook endpoints (the signing secret is never returned). |
| POST | /tenants/{tenant}/webhooks | Create a webhook endpoint (url + events); the signing secret is returned once. |
| DELETE | /tenants/{tenant}/webhooks/{webhook} | Delete a webhook endpoint. |
| GET | /tenants/{tenant}/overlays | List overlays (each with its render/data URLs) + the Main blanket URL. |
| POST | /tenants/{tenant}/overlays | Create an overlay (name; optional width, height, widgets). |
| PATCH | /tenants/{tenant}/overlays/{overlay} | Update an overlay (name / size / widgets). |
| DELETE | /tenants/{tenant}/overlays/{overlay} | Delete an overlay. |
| POST | /tenants/{tenant}/overlays/{overlay}/set-main | Make it the Main overlay rendered by the stable blanket URL. |
| GET | /tenants/{tenant}/timers | List scheduled-message timers. |
| POST | /tenants/{tenant}/timers | Create a timer (messages, interval_minutes; optional platforms, name). |
| PATCH | /tenants/{tenant}/timers/{timer} | Update a timer (enabled, messages, interval, …). |
| DELETE | /tenants/{tenant}/timers/{timer} | Delete a timer. |
AI Streams
Create and run always-on channels made from AI-generated video, steer them while they are on air, and read why they are (or are not) generating. These endpoints need AI Streams on the workspace's plan; writes need a write token and the Manage streams role. Creating and editing use exactly the same checks as the dashboard form, and a new stream is never switched on until you call start. Provider API keys are added on the dashboard only — the API tells you which providers are connected and whether each key works, never the key. See the AI Streams help articles for how the product behaves.
| Method | Path | Token | Description |
|---|---|---|---|
| POST | /tenants/{tenant}/ai-streams |
write | Create an AI stream. Validated by the same rules as the web form: the provider must make the chosen `aspect` and `clip_seconds`, every `channel_ids` entry must be one of this workspace's channels (at least one if the workspace has any, at most the plan's chann... |
| GET | /tenants/{tenant}/ai-streams |
read | List the workspace's AI streams. Answers 200 with available=false when the plan does not include AI Streams, so a client can tell "not on your plan" from a dead token. A stream may publish into several channels: `channels` lists them in order; `channel_id`/`ch... |
| PATCH | /tenants/{tenant}/ai-streams/{aiStream} |
write | Update an AI stream. Omitted fields keep their value; the merged settings are validated exactly as on create (so changing only `provider` is refused if the stream's shape or clip length does not suit it). Sending `channel_ids` replaces the channel list; sendin... |
| DELETE | /tenants/{tenant}/ai-streams/{aiStream} |
write | Delete an AI stream, its queue and its run history. It is switched off first; the watchdog tears the publisher down on the node that runs it. Saved Video Library days are kept until retention removes them. |
| GET | /tenants/{tenant}/ai-streams/{aiStream} |
read | One stream: run state, the playout queue window, the running order (beats) and the spend estimate |
| POST | /tenants/{tenant}/ai-streams/{aiStream}/start |
write | Set the stream's desired state to on-air. Needs streams.manage. Answers 422 with a reason (no_channel / cannot_stream / channel_in_use / nothing_playable) when the stream cannot start. |
| POST | /tenants/{tenant}/ai-streams/{aiStream}/stop |
write | Clear the stream's desired state; the watchdog tears the publisher down. Needs streams.manage. |
| GET | /tenants/{tenant}/ai-streams/{aiStream}/status |
read | Why the stream is (or is not) generating — the same reasons, in the same order, as the stream page: `stopped`, `not-entitled`, `no-provider`, `no-prompt`, `no-key`, `spend-limit`, or null when nothing blocks it. Plus whether its settings can keep a channel on... |
| GET | /tenants/{tenant}/ai-streams/{aiStream}/clips |
read | Recent clips, newest first — metadata only (never a file path, vendor asset id or vendor URL). Page backwards with `before`. |
| GET | /tenants/{tenant}/ai-streams/{aiStream}/clips/{item}/download |
read | Download ONE generated clip as MP4, exactly as it was normalised for broadcast — no remux, no re-encode. Only while the bytes exist: a generated clip is deleted once it has aired, so use `playable`/`download_url` from the clips listing rather than probing. A c... |
| GET | /tenants/{tenant}/ai-streams/{aiStream}/channels |
read | Per channel: is it receiving the feed? `state` is live, connecting, dropped, busy (someone else is live), waiting, claimed (another AI stream is on it), disabled, or not_included. |
| GET | /tenants/{tenant}/ai-streams/{aiStream}/beats |
read | The running order and the Director's cursor. At the end of the list it loops (`cycles` counts the loops). A beat turned down by the content check is skipped and shows `refused_at`. |
| POST | /tenants/{tenant}/ai-streams/{aiStream}/beats |
write | Add a beat — at the end, or straight after `after_beat_id`. `clips` (1–20) is how long it lasts relative to the others, not a spend control. Send `cue: true` to make it the beat the next clip comes from. At most 50 beats. |
| PATCH | /tenants/{tenant}/ai-streams/{aiStream}/beats/{beat} |
write | Rewrite a beat (`instruction`, `clips`). Changing the wording clears a previous refusal. |
| DELETE | /tenants/{tenant}/ai-streams/{aiStream}/beats/{beat} |
write | Remove a beat. If it was the current one, the Director starts from the top of the running order. |
| POST | /tenants/{tenant}/ai-streams/{aiStream}/beats/{beat}/cue |
write | Play this beat next: moves the Director's cursor to it. The next clip commissioned (through the content check and the spend limit, as always) comes from it — unless a schedule window is in force (`schedule_window_active`), which takes priority until it ends. A... |
| GET | /tenants/{tenant}/ai-streams/{aiStream}/schedule |
read | The schedule: every entry, the one in force now, and the next 24 hours as the Director will walk them (`schedule_entry_id: null` = the running order / prompt plays). Times are wall clock in each entry's own IANA zone. |
| POST | /tenants/{tenant}/ai-streams/{aiStream}/schedule |
write | Add a schedule entry: a `weekly` block (`days` 1=Mon…7=Sun) or a one-off `date` (`on_date`), from `start_time` (HH:MM, wall clock) for `duration_minutes` (1–1440) in `timezone` (IANA). While it is in force its `instruction` replaces the running order; a one-of... |
| PATCH | /tenants/{tenant}/ai-streams/{aiStream}/schedule/{entry} |
write | Update a schedule entry. Omitted fields keep their value; changing the wording clears a previous refusal. |
| DELETE | /tenants/{tenant}/ai-streams/{aiStream}/schedule/{entry} |
write | Delete a schedule entry. That time falls back to the running order. |
| POST | /tenants/{tenant}/ai-streams/{aiStream}/topic |
write | Push a topic into the stream now — for "something just happened". Creates a one-off schedule entry that opens this minute (UTC) and lasts `minutes` (default 30, max 1440), so it takes over from the running order and the stream's weekly blocks; the beat cursor... |
| GET | /tenants/{tenant}/ai-providers |
read | The workspace's AI video providers and the state of each key: `working` (the only state that generates), `unchecked` (saved, never accepted — `out_of_credit: true` means the key works but the account is empty), `rejected`, and the providers not yet added. Neve... |
| GET | /tenants/{tenant}/ai-streams/library |
read | Days of AI-stream output saved to the Video Library (one video per stream per UTC day), newest first. Gated like recordings — the plan's cloud recording, not AI Streams — so saved days stay listed after the stream is gone, until retention removes them. Answers... |
| GET | /tenants/{tenant}/ai-streams/library/{recording}/download |
read | Download one saved day as MP4 (stream-copied, not re-encoded). Send the same bearer token. Rate limited with the costly endpoints (15/min). |
Push a topic into a live stream. The headline use: something just happened in your world — a goal, a sale, a breaking story — and you want the channel to be about it. The topic takes over for minutes (default 30), then the stream returns to its running order exactly where it left off. It does not jump the queue: clips already made play first, and the reply says how many (clips_ahead). It never starts a stopped stream and never makes a stream spend faster — the stream still asks for at most its clips ahead at a time, and every topic passes the same content check and spend limit.
curl -X POST -H "Authorization: Bearer YOUR_TOKEN_HERE" -H "Content-Type: application/json" \
-d '{"instruction":"Fans erupt as the home side scores","minutes":10,"label":"Goal! 1-0"}' \
https://streamrepeater.com/api/v1/tenants/1/ai-streams/5/topic
{
"ok": true,
"entry": { "id": 21, "kind": "date", "start_time": "19:42", "timezone": "UTC", "duration_minutes": 10 },
"on_air_now": true,
"clips_ahead": 2,
"stream_active": true,
"message": "The next clip this stream commissions will be about this topic. 2 clips already queued or being made will play first."
}
Play a particular beat next with POST …/beats/{beat}/cue, or add one and cue it in one call with {"instruction": "…", "cue": true}. Why is it quiet? GET …/status returns the same reason the stream page shows — stopped, not-entitled, no-provider, no-prompt, no-key, spend-limit, or null when nothing blocks it — with the sentence to show and what to do.
# Create a stream (never starts it), then start it
curl -X POST -H "Authorization: Bearer YOUR_TOKEN_HERE" -H "Content-Type: application/json" \
-d '{"name":"Harbour cam","channel_ids":[3],"provider":"luma","prompt":"A calm harbour at dawn","clip_seconds":8,"aspect":"16:9","buffer_target":3,"budget_limit":50,"clip_cost_estimate":0.4}' \
https://streamrepeater.com/api/v1/tenants/1/ai-streams
curl -X POST -H "Authorization: Bearer YOUR_TOKEN_HERE" https://streamrepeater.com/api/v1/tenants/1/ai-streams/5/start
Destination parameters
POST and PUT destination requests accept either a custom destination or a saved preset payload. For a custom destination, send name, protocol (rtmp, rtsp or srt) and url. Optional video_settings can include resolution (source, 1080p, 720p, 480p) and bitrate in Kbps. The toggle endpoint accepts {"enabled": true} or {"enabled": false}.
Example: list streams
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" https://streamrepeater.com/api/channels
[
{
"id": 1,
"path_name": "acme--main",
"status": "live",
"tenant": { "id": 1, "name": "Acme" },
"destinations": [
{ "id": 7, "name": "YouTube", "protocol": "rtmp",
"relay_process": { "status": "running" } }
]
}
]
Example: add a destination
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{"name":"Twitch","protocol":"rtmp","url":"rtmp://live.twitch.tv/app/KEY"}' \
https://streamrepeater.com/api/channels/1/destinations
Example: update a destination
curl -X PUT -H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{"name":"YouTube backup","protocol":"rtmp","url":"rtmps://a.rtmps.youtube.com:443/live2/KEY","video_settings":{"resolution":"720p","bitrate":3500}}' \
https://streamrepeater.com/api/channels/1/destinations/7
Example: pause a destination
curl -X PATCH -H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{"enabled":false}' \
https://streamrepeater.com/api/channels/1/destinations/7/toggle
Example: delete a destination
curl -X DELETE -H "Authorization: Bearer YOUR_TOKEN_HERE" \
https://streamrepeater.com/api/channels/1/destinations/7
{
"message": "Destination pending deletion.",
"destination": { "id": 7, "enabled": false, "pending_deletion": true }
}
Example: channel status
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" https://streamrepeater.com/api/channels/1/status
{
"channel_id": 1,
"path_name": "acme--main",
"status": "live",
"viewers": 7,
"destinations": [
{ "id": 7, "name": "YouTube", "enabled": true, "relay_status": "running" }
]
}
Example: delete a channel
curl -X DELETE -H "Authorization: Bearer YOUR_TOKEN_HERE" \
https://streamrepeater.com/api/channels/1
{
"message": "Channel pending deletion.",
"channel_id": 1,
"pending_deletion": true
}
Example: channel analytics
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" https://streamrepeater.com/api/channels/1/analytics
{
"channel_id": 1,
"window": "24h",
"samples": [
{ "sampled_at": "2026-06-26T12:00:00Z", "inbound_bps": 5200000, "outbound_bps": 10400000, "viewers": 12 }
]
}
Example: list highlights
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" https://streamrepeater.com/api/channels/1/highlights
[
{ "id": 4, "occurred_at": "2026-06-26T14:32:00Z", "messages": 47, "intensity": 60,
"sample": [ { "author": "fan123", "message": "LETS GOOO" } ] }
]
Unified chat
Read the live cross-platform chat feed, reply as the broadcaster, and moderate — the same engine as the dashboard chat panel. Replies support twitch, kick and youtube (where connected with the needed scope); moderation actions are delete, timeout (10 min) and ban. Chat endpoints require the workspace to have integrations enabled, and writes require the Manage streams role.
# Poll new messages since the last id you saw
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" \
"https://streamrepeater.com/api/tenants/1/chat?after=1024"
# Reply as your channel
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" -H "Content-Type: application/json" \
-d '{"platform":"twitch","message":"thanks for the raid!"}' \
https://streamrepeater.com/api/tenants/1/chat/reply
# Time out a message's author for 10 minutes
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" -H "Content-Type: application/json" \
-d '{"action":"timeout"}' \
https://streamrepeater.com/api/tenants/1/chat/messages/2048/moderate
Integrations & automations
Read your connected platforms, automation rules and event history — and, most usefully, fire your own events into the rules engine. A custom event runs every matching rule's actions (lights, Discord, webhooks, OBS…), so an external system — a donation page, your backend, a Zap — can drive your on-stream automations. Secrets are never returned: connection tokens and action payloads are omitted from the responses.
# Fire a custom event — runs every rule listening for "custom.donation"
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" -H "Content-Type: application/json" \
-d '{"type":"custom.donation","context":{"name":"Ada","amount":"5.00"}}' \
https://streamrepeater.com/api/tenants/1/events
{ "ok": true, "matched_rules": 2 }
# Read the recent event feed
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" "https://streamrepeater.com/api/tenants/1/events?after=900"
Create rules that listen for custom.* events in Integrations → Automations, then reference your context fields (e.g. {{ amount }}) in the action templates.
Event webhooks
Create developer webhooks from Settings -> Developer webhooks. Each delivery is a JSON POST signed with X-Streamrepeater-Signature, where the value is sha256= plus an HMAC-SHA256 of the raw request body using the endpoint's signing secret.
{
"id": "9b6f6b4d-2d13-4d4b-b3b8-54ff5d29c9ef",
"event": "stream.live",
"created_at": "2026-06-16T12:00:00+00:00",
"data": {
"channel": { "id": 1, "title": "Main Feed" },
"destination_count": 2
}
}
Available events: stream.live, stream.offline, destination.failed, recording.started and bandwidth.warning; and for AI Streams ai_stream.started, ai_stream.stopped, ai_stream.yielded (paused for a live broadcast), ai_stream.channel_unavailable, ai_stream.video_saved (at most hourly per stream), ai_provider.key_refused and ai_provider.out_of_credit — each carries a message, details and the stream or provider it is about, and repeats are collapsed the way the notification bell collapses them. The same AI events can trigger automation rules. The settings page can also send a ping test event. Endpoints can be created, listed and deleted via the API (/tenants/{tenant}/webhooks) as well as the dashboard — the signing secret is returned once on creation.
Need help integrating? Get in touch.