Manage cloud-hosted 24/7 video streams, playlists, destinations, schedules, media and workspaces.
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Make a stream also publish to one of the workspace's destinations (list_destinations), enabled. Up to 5 per stream and one YouTube channel at most (DESTINATION_LIMIT, YOUTUBE_DESTINATION_LIMIT, DESTINATION_DUPLICATE). A second destination needs multistream on the workspace (MULTISTREAM_NOT_ENABLED). Refused while the stream is live (STREAM_LIVE), and for a destination the stream's quality or codec cannot reach (DESTINATION_INCOMPATIBLE). Returns the stream's whole destination list after the change, as list_stream_destinations does.
Stream id (stream_...), from list_streams.
Destination id (dest_...), from list_destinations or list_stream_destinations.
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Make a stream also publish to one of the workspace's destinations (list_destinations), enabled. Up to 5 per stream and one YouTube channel at most (DESTINATION_LIMIT, YOUTUBE_DESTINATION_LIMIT, DESTINATION_DUPLICATE). A second destination needs multistream on the workspace (MULTISTREAM_NOT_ENABLED). Refused while the stream is live (STREAM_LIVE), and for a destination the stream's quality or codec cannot reach (DESTINATION_INCOMPATIBLE). Returns the stream's whole destination list after the change, as list_stream_destinations does.
| Argument | Type | Description |
|---|---|---|
| streamId* | string | Stream id (stream_...), from list_streams. |
| destinationId* | string | Destination id (dest_...), from list_destinations or list_stream_destinations. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId",
"destinationId"
],
"properties": {
"streamId": {
"type": "string",
"description": "Stream id (stream_...), from list_streams."
},
"destinationId": {
"type": "string",
"description": "Destination id (dest_...), from list_destinations or list_stream_destinations."
}
},
"additionalProperties": false
}Use this for every playlist change: add media, remove items, reorder, or switch between sequential and shuffle, as one ordered batch. All-or-nothing — if any operation is invalid, nothing is written. A stream that is NOT currently live picks the change up on its next start, with nothing else to call. A stream that IS live keeps playing the arrangement it started with until swap_live_playlist replaces it. Media ids (obj_...) come from list_uploads; item ids (plitm_...) come from get_playlist. Operations apply in sequence, each seeing the result of the previous, so an index in a later operation must account for the earlier ones. Pass basedOnVersion (the `version` from get_playlist) to be told about a concurrent edit instead of overwriting it.
| Argument | Type | Description |
|---|---|---|
| operations* | array | Operations in the order they should apply. |
| playlistId* | string | Playlist id (pl_...) to edit, from list_playlists (for a stream, pass its streamId there). |
| basedOnVersion | integer | The playlist `version` from get_playlist this edit was computed against: a CONFLICT refusal if it has changed since. Omitted: the edit applies to the playlist as it is now. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"playlistId",
"operations"
],
"properties": {
"operations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"addObjects": {
"type": "object",
"required": [
"position",
"objectIds"
],
"properties": {
"index": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "With position INDEX: the 0-based position the first added item takes."
},
"position": {
"enum": [
"START",
"END",
"INDEX"
],
"type": "string",
"description": "Where in the playlist the items go: the start, the end, or the position given in `index`."
},
"objectIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Media ids (obj_...) to add, in the order they should play."
}
},
"description": "Add media to the playlist."
},
"removeItems": {
"type": "object",
"required": [
"itemIds"
],
"properties": {
"itemIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Playlist item ids (plitm_...) to remove — from get_playlist, NOT media ids."
}
},
"description": "Remove items already in the playlist."
},
"reorderItems": {
"type": "object",
"required": [
"moves"
],
"properties": {
"moves": {
"type": "array",
"items": {
"type": "object",
"required": [
"itemId",
"position"
],
"properties": {
"index": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "With position INDEX: the 0-based position the item moves to."
},
"itemId": {
"type": "string",
"description": "Playlist item id (plitm_...) to move."
},
"position": {
"enum": [
"START",
"END",
"INDEX"
],
"type": "string",
"description": "Where in the playlist the items go: the start, the end, or the position given in `index`."
}
}
},
"description": "Ordered moves; each one applies to the result of the previous."
}
},
"description": "Move items already in the playlist."
},
"updateSettings": {
"type": "object",
"properties": {
"mode": {
"enum": [
"sequential",
"shuffle"
],
"type": "string",
"description": "Play order: sequential plays the items as arranged, shuffle randomises each loop. Omitted: the mode is unchanged."
}
},
"description": "Change playlist-level settings."
}
},
"description": "A single operation. Set EXACTLY ONE of addObjects, removeItems, reorderItems, updateSettings — an entry with two of them set is rejected."
},
"description": "Operations in the order they should apply."
},
"playlistId": {
"type": "string",
"description": "Playlist id (pl_...) to edit, from list_playlists (for a stream, pass its streamId there)."
},
"basedOnVersion": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "The playlist `version` from get_playlist this edit was computed against: a CONFLICT refusal if it has changed since. Omitted: the edit applies to the playlist as it is now."
}
},
"additionalProperties": false
}Make a playlist play in a saved order. This is the step that changes playback, and unlike a playlist edit it needs no swap_live_playlist — a running stream picks the new order up for its next items. The version in use is pinned at assign time and never advances on its own, so call this again after refine_playlist_order to adopt an improvement. Check get_playlist_order(id, playlistId) first: assignment succeeds even when items the order names are missing, returning dependencyWarnings, and those items simply never get picked. Only a rule that cannot run over the playlist at all is refused. Returns dependencyWarnings, the playlist (id, version, compatibility status), the order and the version now pinned.
| Argument | Type | Description |
|---|---|---|
| orderId* | string | Order id (oseq_...) to apply. |
| versionId | string | A version id of this order to pin instead, from get_playlist_order. Omitted: the order's newest usable version (latestReadyVersionID). |
| playlistId* | string | Playlist (pl_...) that should play in this order. |
| acknowledgeMissingRefs | array | Item names from a previous run's dependencyWarnings that the user has accepted as missing, so they stop being reported. Pass them back verbatim. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"playlistId",
"orderId"
],
"properties": {
"orderId": {
"type": "string",
"description": "Order id (oseq_...) to apply."
},
"versionId": {
"type": "string",
"description": "A version id of this order to pin instead, from get_playlist_order. Omitted: the order's newest usable version (latestReadyVersionID)."
},
"playlistId": {
"type": "string",
"description": "Playlist (pl_...) that should play in this order."
},
"acknowledgeMissingRefs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Item names from a previous run's dependencyWarnings that the user has accepted as missing, so they stop being reported. Pass them back verbatim."
}
},
"additionalProperties": false
}Disarm a scheduled stream and return it to an unscheduled, inactive state, clearing any daily or weekly repeat. Use this to stop a recurring stream coming back; it has no effect on a stream that is currently live (stop_stream does that).
| Argument | Type | Description |
|---|---|---|
| streamId* | string | Stream id (stream_...) whose schedule to cancel. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId"
],
"properties": {
"streamId": {
"type": "string",
"description": "Stream id (stream_...) whose schedule to cancel."
}
},
"additionalProperties": false
}Answer 'would start_stream work right now?' without changing anything. Runs the same pre-flight configuration checks a start does and returns ready plus every blocking issue — typically a missing or archived destination, an empty video playlist, media too big for the stream's storage, or, for a stream playing a scene, a scene that is missing, not published or invalid. It does NOT check whether uploads have finished processing: a playlist takes only completed uploads, but their media facts (duration, sound) arrive about 15-25 s after an upload completes, so wait that long after uploading before starting. Credits and YouTube live eligibility are deliberately NOT part of it (both are re-checked at the moment the stream activates), so ready=true means correctly configured, not guaranteed to go live — pair it with get_billing_overview when a ready stream still will not start. Call it before start_stream and before relying on a schedule_stream window. Returns streamReadiness: ready, and issues[] each with code, error, suggestion, path and stage.
| Argument | Type | Description |
|---|---|---|
| streamId* | string | Stream id (stream_...) to check. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId"
],
"properties": {
"streamId": {
"type": "string",
"description": "Stream id (stream_...) to check."
}
},
"additionalProperties": false
}Step 2 of uploading a local file: call it once the bytes have been PUT to the url create_upload returned — by you, or by the user — which starts processing. Until this is called the media is unusable, and calling it before the PUT finishes leaves a broken record. Nothing to call for a url import: import_upload_from_url completes on its own.
| Argument | Type | Description |
|---|---|---|
| id* | string | The media id (obj_...) create_upload returned. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "The media id (obj_...) create_upload returned."
}
},
"additionalProperties": false
}Acts on the scene on air, as its operator would: goFrame (take frame/<target> to air, with a transition), next (cue frame/<target> so the take is instant; no target clears the cue), setControl (set one of its controls; scene_get control/* lists them), setData (replace a table's rows), skip (a playlist source to its next item). Viewers see it at once. The arguments are checked first, then against what is published (a frame, control, table or playlist source it doesn't have, a toggle given "yes": INVALID_INPUT); only then is it refused while nothing plays it (SCENE_OFFLINE). A SCENE_TIMEOUT may or may not have applied: send an actionId (a new uuid per action, kept by you) and retry with the SAME actionId and arguments — it is applied at most once. Without an actionId, never retry an action that isn't idempotent (skip, a button) blind: read get_scene_state first. Needs a stream playing the published scene (set_stream_scene, start_stream); it changes what is on air, never the draft. Answers streamId, online, frame (on air after the action) and next.
| Argument | Type | Description |
|---|---|---|
| op* | string | The action: take a frame to air, cue the next frame, set a control, replace a table's rows, or skip a playlist source to its next item |
| rows | array | setData: the new rows, each an object of column → value; they replace every row the table has now |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| value | any | setControl: the value (a toggle true or false, a text a string, a number a number, a select one of its options; none for a button) |
Use this to reuse an arrangement on a second stream instead of rebuilding it: copies one playlist's contents into another playlist of the same kind. The target must be EMPTY — this never merges or overwrites. The target stream picks the contents up on its next start; if it is already live, swap_live_playlist puts them on the air.
| Argument | Type | Description |
|---|---|---|
| sourcePlaylistId* | string | Playlist id (pl_...) to copy from. |
| targetPlaylistId* | string | Playlist id (pl_...) to copy into; must be empty. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"sourcePlaylistId",
"targetPlaylistId"
],
"properties": {
"sourcePlaylistId": {
"type": "string",
"description": "Playlist id (pl_...) to copy from."
},
"targetPlaylistId": {
"type": "string",
"description": "Playlist id (pl_...) to copy into; must be empty."
}
},
"additionalProperties": false
}An empty playlist owned by a scene, for one of its playlist sources to play (a stream's own two playlists come with the stream). Fill it with apply_playlist_operations. Each call creates another. Returns the new playlist: id (pl_...), name, sceneId, kind, mode, version, createdAt.
| Argument | Type | Description |
|---|---|---|
| kind* | string | The media it holds: video items for a picture source, audio items for a sound source. |
| name* | string | A name to tell it apart in list_playlists, e.g. 'Lower-third clips'. |
| sceneId* | string | The scene that owns it (scn_...), from list_scenes. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"sceneId",
"kind",
"name"
],
"properties": {
"kind": {
"enum": [
"video",
"audio"
],
"type": "string",
"description": "The media it holds: video items for a picture source, audio items for a sound source."
},
"name": {
"type": "string",
"minLength": 1,
"description": "A name to tell it apart in list_playlists, e.g. 'Lower-third clips'."
},
"sceneId": {
"type": "string",
"description": "The scene that owns it (scn_...), from list_scenes."
}
},
"additionalProperties": false
}Describe a playback order in plain language and have one written and validated. Pass the playlist it is for as playlistId: the order is written against that playlist's REAL items, so leaving it out produces a rule aimed at nothing. Creating does NOT apply it — assign_playlist_order does. Read `status` on the result: READY = usable; INVALID = written but it failed validation, and `diagnostics` say why (refine it, this is not a transport error); MESSAGE = a clarifying question or refusal, nothing was produced; PENDING = still running, poll get_playlist_order. The answer, under createOrderSequencer, also has sequencer (the order: id oseq_..., name, latestReadyVersionID) and version (the version written, or null).
| Argument | Type | Description |
|---|---|---|
| name* | string | Short name for the order, e.g. 'Sunday service running order'. |
| intent* | string | What the order should do, in plain language — e.g. 'always start with the welcome clip, then alternate a talk and a song, and never play the same song twice in a row'. |
| playlistId | string | The playlist (pl_...) this order is for. Omitting it produces a rule with no items to reason about. |
| description | string | A note about the order shown to the user beside its name; it does not change how the order plays. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"name",
"intent"
],
"properties": {
"name": {
"type": "string",
"description": "Short name for the order, e.g. 'Sunday service running order'."
},
"intent": {
"type": "string",
"description": "What the order should do, in plain language — e.g. 'always start with the welcome clip, then alternate a talk and a song, and never play the same song twice in a row'."
},
"playlistId": {
"type": "string",
"description": "The playlist (pl_...) this order is for. Omitting it produces a rule with no items to reason about."
},
"description": {
"type": "string",
"description": "A note about the order shown to the user beside its name; it does not change how the order plays."
}
},
"additionalProperties": false
}Create a destination for any platform that gives out an RTMP ingest URL and stream key (Twitch, Facebook, Kick, X, TikTok, Telegram, a self-hosted server, or YouTube when its channel is not connected here). Take rtmpUrl from get_connection_hints(platform).ingestUrl rather than guessing it — a wrong host is a stream that silently never connects. The key is stored encrypted and never read back. Use create_youtube_destination instead when the channel is connected through Streamloop: only then can titles, privacy and thumbnails be managed from here. Attach the new destination to a stream with add_stream_destination.
| Argument | Type | Description |
|---|---|---|
| name* | string | Display name, e.g. 'Twitch — main channel'. |
| rtmpKey* | string | Stream key from the platform. Stored encrypted. |
| rtmpUrl* | string | Ingest URL. Take it from get_connection_hints(platform).ingestUrl or from the platform's own streaming settings — never invent one. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"name",
"rtmpUrl",
"rtmpKey"
],
"properties": {
"name": {
"type": "string",
"description": "Display name, e.g. 'Twitch — main channel'."
},
"rtmpKey": {
"type": "string",
"description": "Stream key from the platform. Stored encrypted."
},
"rtmpUrl": {
"type": "string",
"description": "Ingest URL. Take it from get_connection_hints(platform).ingestUrl or from the platform's own streaming settings — never invent one."
}
},
"additionalProperties": false
}Creates a scene with an empty draft, nothing published. Build it with scene_set (frame/<id> for each frame, source/… for what it reads), then publish_scene. Each call creates another scene. Answers the scene: id (scn_…), name, version 0, and its timestamps.
| Argument | Type | Description |
|---|---|---|
| name* | string | A name to tell the scene apart in list_scenes and the dashboard |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "A name to tell the scene apart in list_scenes and the dashboard"
}
},
"additionalProperties": false
}Makes the key an encoder (OBS, vMix, a phone app) sends a live feed with, for one of the scene's rtmp or srt sources: RTMP, RTMPS and SRT URLs with the key, shown in this answer only — give them to the user. Make the source first (scene_set source/rtmp/<id>). The source's status is waiting until an encoder connects with this key, and live while it publishes. The key's own id (ingkey_…) is what revoke_scene_ingest_key takes. Answers id, sourceId, key, keyPrefix, status, live, rtmpUrl and rtmpsUrl.
| Argument | Type | Description |
|---|---|---|
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| sourceId* | string | The id of an rtmp or srt source in the scene's draft: the <id> of source/rtmp/<id> or source/srt/<id> (scene_get source/* lists them) |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"sourceId"
],
"properties": {
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"sourceId": {
"type": "string",
"minLength": 1,
"description": "The id of an rtmp or srt source in the scene's draft: the <id> of source/rtmp/<id> or source/srt/<id> (scene_get source/* lists them)"
}
},
"additionalProperties": false
}Use this to set up a new loop or a stream for a scene. The stream is created empty and CANNOT start until it has (1) a destination — add_stream_destination with one from list_destinations, or get_connection_hints for how to make one — plus (2) either a playable video playlist (at least one item) or an attached published scene (set_stream_scene). Creating a stream also creates its two empty playlists (one video, one audio): for a loop, find them with list_playlists(streamId) and fill them with apply_playlist_operations — a playlist loop needs nothing published before the first start. Media facts (duration, sound) of an upload arrive about 15-25 s after it completes: a start right after uploading can be refused (state invalid) until then. When it runs is schedule_stream's job. Returns the new stream under createStream: id (stream_...), name, state, quality, framerate, createdAt.
| Argument | Type | Description |
|---|---|---|
| name | string | Display name for the stream. |
| quality | string | Output resolution sent to every destination; the account's preferred quality when omitted. |
| framerate | string | Frames per second the stream is sent at. |
| storageBackend | string | Media storage: standard (included) or large, for big playlists, billed per GiB-month. |
| streamDuration | integer | Stop after this many seconds of streaming; 0 = run until stopped. |
| runBackupStream | boolean | Also publish to YouTube's backup ingest for redundancy. Doubles the streaming cost while live. |
Step 1 of uploading a LOCAL file: reserves the media record and returns `uploadUrl`, a presigned PUT you can use immediately. This server does not relay bytes, but YOU probably can — if the client can run a command or make an HTTP request, do the PUT itself: curl -X PUT -H 'Content-Type: <the mimeType you passed>' --data-binary @<file> '<uploadUrl>' The Content-Type header MUST equal the mimeType given here, or the signature is rejected. The url is valid for about 11 hours and takes the whole file in one request (up to 5 GB; a bigger file, up to 15 GB, must come in through import_upload_from_url). If the client cannot make requests, hand `uploadUrl` to the user. Then call complete_upload — media that is never PUT and completed stays unusable. If the file is already reachable over https (including a file the user attached in this conversation), skip all of this and use discover_external_media + import_upload_from_url instead. Returns object (the media record: id obj_..., name, type, mimeType, fileSize, uploaded false, createdAt), uploadUrl and next (the next step).
| Argument | Type | Description |
|---|---|---|
| name* | string | File name, e.g. 'intro.mp4'. |
| type* | string | The kind of media the file holds. |
| width | integer | Width of a video or image, in pixels. |
| height | integer | Height of a video or image, in pixels. |
| duration | integer | Length of a video or audio file, in seconds. Omitted: measured from the file once it is processed. |
| fileSize |
Turn an already-consented YouTube channel into a destination a stream can publish to, with managed broadcasts (update_stream's `youtube` settings, get_stream_live_stats). Takes the channel `id` from list_youtube_channels — NOT the YouTube channel URL or handle. If that list is empty, the account has not consented yet: start_youtube_connection first.
| Argument | Type | Description |
|---|---|---|
| name | string | Display name for the destination. Defaults to the channel title. |
| channelId* | string | The `id` of a channel from list_youtube_channels. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"channelId"
],
"properties": {
"name": {
"type": "string",
"description": "Display name for the destination. Defaults to the channel title."
},
"channelId": {
"type": "string",
"description": "The `id` of a channel from list_youtube_channels."
}
},
"additionalProperties": false
}Deletes the scene, its draft and every published version, for good. Refused (SCENE_IN_USE) while a stream plays it. Its ingest keys are revoked and its cameras removed.
| Argument | Type | Description |
|---|---|---|
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| confirm | boolean | Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene"
],
"properties": {
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"confirm": {
"type": "boolean",
"description": "Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes."
}
},
"additionalProperties": false
}Deletes a saved secret for good: its value cannot be read back, and a table that names it ("$secret:<name>") stops getting it. Only set_scene_secret with the key again restores it. Answers deleted (the name).
| Argument | Type | Description |
|---|---|---|
| name* | string | The secret's name, as list_scene_inputs lists it |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| confirm | boolean | Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"name"
],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "The secret's name, as list_scene_inputs lists it"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"confirm": {
"type": "boolean",
"description": "Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes."
}
},
"additionalProperties": false
}Permanently delete a stream, its playlists and its schedule. Irreversible, and it cannot be undone by re-creating the stream: uploads survive but the arrangement does not. Stop the stream first if it is live. To keep a stream but take it off the air, use stop_stream (plus cancel_stream_schedule). Requires confirm=true and the streamloop:destructive scope.
| Argument | Type | Description |
|---|---|---|
| id* | string | Stream id (stream_...) to delete. |
| confirm | boolean | Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Stream id (stream_...) to delete."
},
"confirm": {
"type": "boolean",
"description": "Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes."
}
},
"additionalProperties": false
}Permanently remove a media file from the library: its stored bytes and every file derived from it are deleted. Irreversible. An upload that never finished (no complete_upload after the PUT, an import that failed) is dropped too: nothing was stored. Refused, with nothing changed, while a playlist or a recent stream still uses it (IN_USE) — remove it from those playlists first with apply_playlist_operations — and for an image (UNSUPPORTED_TYPE). Requires confirm=true and the streamloop:destructive scope.
| Argument | Type | Description |
|---|---|---|
| id* | string | Media id (obj_...) to remove. |
| confirm | boolean | Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Media id (obj_...) to remove."
},
"confirm": {
"type": "boolean",
"description": "Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes."
}
},
"additionalProperties": false
}Puts the draft back to the published version: every change made since it was published is lost (nothing on air changes). draftRevision (required) is the draft you reviewed: if it changed after that, nothing is discarded (CONFLICT). dryRun: true lists what would be lost and discards nothing — show the user that first. Refused (NOT_PUBLISHED) when nothing is published.
| Argument | Type | Description |
|---|---|---|
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| dryRun | boolean | true: only say what would be lost. Omitted or false: the draft is discarded |
| draftRevision* | string | The draftRevision of the answer you reviewed (every scene_get and scene_set answer has one) |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"draftRevision"
],
"properties": {
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"dryRun": {
"type": "boolean",
"description": "true: only say what would be lost. Omitted or false: the draft is discarded"
},
"draftRevision": {
"type": "string",
"minLength": 1,
"description": "The draftRevision of the answer you reviewed (every scene_get and scene_set answer has one)"
}
},
"additionalProperties": false
}Look at any media url and report what could be imported from it: each asset with its quality options, sizes and duration. REQUIRED BEFORE import_upload_from_url, which takes the opaque asset and quality ids only this tool hands out — they cannot be constructed. Works for a media page AND for a direct file link, which is what makes it the right tool for a FILE THE USER ATTACHED in this conversation: pass the attachment's https url and import from it, with no local file and no PUT. Reads the url only; nothing is imported and no credits are spent. Import only the user's own or otherwise licensed content. Returns externalLinkContents: provider, and items[] each with id (assetId), name, mime, size, durationSeconds and qualities[] (id = qualityId, label, width, height, fps, estimatedSize).
| Argument | Type | Description |
|---|---|---|
| url* | string | Public media URL to inspect. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "Public media URL to inspect."
}
},
"additionalProperties": false
}Who this token belongs to: the signed-in account's id, email, name, language and the default run-length new streams inherit. Use it to confirm which account an agent is acting for. It says nothing about which workspace is in use (get_selected_workspace) or about credits (get_billing_overview). Returns me: id, email, firstName, lastName, avatarURL, role, locale, defaultStreamDuration, createdAt.
No arguments.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {},
"additionalProperties": false
}Use this to answer 'can this keep streaming, and for how long' — and whenever a stream fails to start or stops with no configuration problem, since check_stream_readiness deliberately does not look at credits. One call returns the credit balance, credits left, whether the account is in good standing, how fast credits are being spent right now, which streams are live and what they are costing, and the per-minute rate of each output quality (so a remaining-time estimate needs no second call). Read-only: buying credits is not possible from here, so if the balance is short, point the user at the Streamloop dashboard. Returns me.billing (balance, creditsLeft, goodStanding, eligibleForFreeCredits), spendSummary, activeUsage and pricingTiers.
No arguments.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {},
"additionalProperties": false
}Use this FIRST when the user names a platform they want to stream to ("put this on YouTube", "also send it to Twitch") and there is no destination for it yet. Returns, for that platform: the connection methods, the exact `ingestUrl` to pass to create_rtmp_destination (or null when the platform issues one per session), what each method can and cannot do afterwards, the Streamloop tools to call in order, where in the platform's own UI the user finds the stream key, and the behaviour that otherwise ends a 24/7 loop early. NEVER guess an ingest url — a wrong host is a stream that silently never connects. Static guidance: it reads nothing, changes nothing and costs nothing. YouTube in particular has TWO answers and they are not equivalent: a managed connection (Google consent, and only then can titles, privacy, thumbnails and viewer counts be managed from here) or a plain stream key (video only).
| Argument | Type | Description |
|---|---|---|
| platform* | string | The service the user wants to stream to; other covers any service that issues an RTMP url and stream key. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"platform"
],
"properties": {
"platform": {
"enum": [
"youtube",
"twitch",
"facebook",
"x",
"kick",
"tiktok",
"linkedin",
"telegram",
"other"
],
"type": "string",
"description": "The service the user wants to stream to; other covers any service that issues an RTMP url and stream key."
}
},
"additionalProperties": false
}Use this before editing a playlist (item ids for remove and reorder come from here, and so does the `version` the edit tools take) and after swapping one onto a live stream (to see the swap finished). Returns the draft items in play order, what a running stream is playing right now, whether the draft differs from that, how it differs, and any custom playback order attached: id, name, kind, mode, version, liveVersion, hasUnpublishedChanges, publishStatus, items, liveItems, diff, orderSequencer and its version and status.
| Argument | Type | Description |
|---|---|---|
| id* | string | Playlist id (pl_...), from list_playlists. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Playlist id (pl_...), from list_playlists."
}
},
"additionalProperties": false
}One saved order: its description, how many playlists use it, and its newest usable version with any validation problems. Pass playlistId as well to get the answer that actually matters before assigning — whether this order can run over THAT playlist (`assignable`), which items it expects but cannot find, and a dry-run of the first items it would pick, in order. Do that instead of assigning to find out. Returns orderSequencer (id, name, description, assignedPlaylistsCount, latestReadyVersion with validationStatus and diagnostics) and, with playlistId, againstPlaylist (assignable, missingRefs, dependencyWarnings, blockingDiagnostics, version.dryRun.picks).
| Argument | Type | Description |
|---|---|---|
| id* | string | Order id (oseq_...), from list_playlist_orders. |
| playlistId | string | Also check this playlist (pl_...): compatibility plus a dry-run of the picks. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Order id (oseq_...), from list_playlist_orders."
},
"playlistId": {
"type": "string",
"description": "Also check this playlist (pl_...): compatibility plus a dry-run of the picks."
}
},
"additionalProperties": false
}What a scene has on air. Answers streams: each stream that plays it, with its id (stream), name and state and, under onAir while it runs, the frame/<id> on air (frame) and the one cued next, the controls' values, each source's status and what its playlists play. With no stream playing it, streams is empty and summary says how to put it on air.
| Argument | Type | Description |
|---|---|---|
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene"
],
"properties": {
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
}
},
"additionalProperties": false
}Which workspace this connection is working in right now, and where that came from: `explicit` (a select_workspace call), `header` or `env` (the client pinned it in its own configuration), or `token-hint` (the default the login carries). Call it when a result is unexpectedly empty, to confirm you are looking in the right workspace before telling the user something does not exist.
No arguments.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {},
"additionalProperties": false
}One stream's full configuration: current state, output quality and framerate, the destinations it publishes to (each enabled or not, with its delivery state), and its scheduled window including recurrence and repeat weekdays. Use it before changing a stream. For whether it could start right now, call check_stream_readiness instead (this tool reports what is configured, not what is missing); for live viewer numbers, get_stream_live_stats.
| Argument | Type | Description |
|---|---|---|
| id* | string | Stream id (stream_...), from list_streams. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Stream id (stream_...), from list_streams."
}
},
"additionalProperties": false
}How a live stream is doing ON YOUTUBE: watch URL, broadcast status, concurrent viewers, views and likes. Only meaningful while the stream is live to a YouTube channel connected through Streamloop — a custom RTMP destination reports nothing here, since those numbers live with that platform. Returns the stream as node: id, name and youtubeLiveStats (watchUrl, lifecycle, concurrentViewers, totalViews, likeCount, fetchedAt, and mirrorStatus / mirrorLastError for the YouTube broadcast sync); youtubeLiveStats is null when there is no connected YouTube broadcast. For whether Streamloop itself considers the stream healthy, read state from get_stream.
| Argument | Type | Description |
|---|---|---|
| id* | string | Stream id (stream_...), from list_streams. Ask about a stream that is live to a connected YouTube channel. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Stream id (stream_...), from list_streams. Ask about a stream that is live to a connected YouTube channel."
}
},
"additionalProperties": false
}Use this for questions about the past — 'how many hours did I stream last month', 'is my spend going up' — not about the present. Returns minutes streamed, credits spent and session counts bucketed over a period and split by output quality. get_billing_overview has the current balance and burn rate instead. Returns usageAnalytics: totalMinutes, totalCredits, sessionCount, byQuality[] (quality, minutes, credits) and buckets[] (date, minutes, credits, sessions).
| Argument | Type | Description |
|---|---|---|
| period* | string | The span to report on. It sets the buckets (hourly for DAY, daily for WEEK and MONTH, monthly for QUARTER and YEAR) and, without startDate, the window: the last day, week, month, quarter or year. |
| endDate | string | End of the window as an RFC3339 instant. Omitted: now. |
| startDate | string | Start of the window as an RFC3339 instant, e.g. 2026-09-01T00:00:00Z. Omitted: one period before now. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"period"
],
"properties": {
"period": {
"enum": [
"DAY",
"WEEK",
"MONTH",
"QUARTER",
"YEAR"
],
"type": "string",
"description": "The span to report on. It sets the buckets (hourly for DAY, daily for WEEK and MONTH, monthly for QUARTER and YEAR) and, without startDate, the window: the last day, week, month, quarter or year."
},
"endDate": {
"type": "string",
"description": "End of the window as an RFC3339 instant. Omitted: now."
},
"startDate": {
"type": "string",
"description": "Start of the window as an RFC3339 instant, e.g. 2026-09-01T00:00:00Z. Omitted: one period before now."
}
},
"additionalProperties": false
}One media file in detail: duration, resolution, framerate, whether it has video and audio (probed from the file itself, not from its name), a temporary download URL, a thumbnail, and — for a URL import — how far the download has got and why it failed. Use it to confirm media is ready to stream, and to read the duration a schedule window has to fit. Returns id, name, type, mimeType, fileSize, uploaded, duration, width, height, fps, hasVideo, hasAudio, downloadUrl, thumbnailUrl, meta and externalSource.
| Argument | Type | Description |
|---|---|---|
| id* | string | Media id (obj_...), from list_uploads. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Media id (obj_...), from list_uploads."
}
},
"additionalProperties": false
}Import chosen assets from a media url — a page, a direct file link, or a file the user attached in this conversation — straight into the workspace, with no local file and no PUT. Each selection must echo an assetId and qualityId from discover_external_media for the SAME url; invented ids are rejected. One selection produces one media item, so the same asset can be imported twice with different ranges to get two clips. Downloading is asynchronous: poll get_upload until it reports the import finished. Attachment urls usually expire, so import promptly rather than storing the link for later. Returns one media record per selection (id obj_..., name, type, uploaded, externalSource with its download state).
| Argument | Type | Description |
|---|---|---|
| url* | string | The SAME url passed to discover_external_media. |
| selections* | array | One entry per media item to create. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"url",
"selections"
],
"properties": {
"url": {
"type": "string",
"description": "The SAME url passed to discover_external_media."
},
"selections": {
"type": "array",
"items": {
"type": "object",
"required": [
"assetId",
"qualityId"
],
"properties": {
"assetId": {
"type": "string",
"description": "`items[].id` from discover_external_media."
},
"qualityId": {
"type": "string",
"description": "`items[].qualities[].id` from discover_external_media."
},
"endSeconds": {
"type": "number",
"description": "Seconds into the source where the clip ends, after startSeconds. Omitted: the end of the source.",
"exclusiveMinimum": 0
},
"preciseCut": {
"type": "boolean",
"description": "Cut exactly at the offsets (re-encodes, markedly slower) instead of snapping to the nearest keyframe, which may include a few extra seconds at the start."
},
"startSeconds": {
"type": "number",
"minimum": 0,
"description": "Seconds into the source where the clip starts. Omitted: the beginning of the source."
}
}
},
"minItems": 1,
"description": "One entry per media item to create."
}
},
"additionalProperties": false
}Find the places this workspace can publish to — connected YouTube channels and custom RTMP endpoints — and their ids, which is what add_stream_destination needs. Check `status`: only an `active` destination can carry a stream; `needs_reauth` and `revoked` mean the YouTube connection must be re-consented (start_youtube_connection) before a stream using it will start. Returns a page of rows (id, name, kind, status, rtmpURL, ytChannelTitleCached, archivedAt, lastUsedAt, createdAt) plus nextCursor and total.
| Argument | Type | Description |
|---|---|---|
| id | string | Look up a single destination by id (dest_...). |
| kind | string | Filter by kind: a connected YouTube channel, or a custom RTMP endpoint. |
| name | string | Case-insensitive substring to match destination names. |
| sort | string | Order rows by creation time; newest first when omitted. |
| after | string | The `nextCursor` from a previous page of this same tool. |
| first | integer | How many rows to return on this page; 20 when omitted. |
| status | string | Only destinations in this state. Streams publish only to active ones; needs_reauth and revoked ones need start_youtube_connection again. |
Find saved custom playback orders (order sequencers) and their ids. Use it before creating one — an order the user already has can be assigned to another playlist instead of rewritten. Pass streamId to see only orders that can run over that stream's playlist. `latestReadyVersionID` null means nothing usable has been produced yet. Which order a given playlist is using is on get_playlist, not here. Returns a page of rows (id, name, description, latestReadyVersionID, retiredAt, createdAt, assignedPlaylistsCount) plus nextCursor and total.
| Argument | Type | Description |
|---|---|---|
| name | string | Case-insensitive substring to match the name. |
| sort | string | Order rows by creation time; newest first when omitted. |
| after | string | The `nextCursor` from a previous page of this same tool. |
| first | integer | How many rows to return on this page; 20 when omitted. |
| streamId | string | Only orders that can run over this stream's playlist (stream_...). |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"name": {
"type": "string",
"description": "Case-insensitive substring to match the name."
},
"sort": {
"enum": [
"newest",
"oldest"
],
"type": "string",
"description": "Order rows by creation time; newest first when omitted."
},
"after": {
"type": "string",
"description": "The `nextCursor` from a previous page of this same tool."
},
"first": {
"type": "integer",
"maximum": 100,
"description": "How many rows to return on this page; 20 when omitted.",
"exclusiveMinimum": 0
},
"streamId": {
"type": "string",
"description": "Only orders that can run over this stream's playlist (stream_...)."
}
},
"additionalProperties": false
}Use this to get from a stream to something you can edit: pass streamId and you get the two playlists that stream owns — one video, one audio, created empty with it. Pass sceneId for the playlists a scene's sources play. Also the way to find a playlist id before any other playlist tool. Returns a page of rows (id, name, streamID, sceneId, kind, mode, version, createdAt) plus nextCursor and total; get_playlist has the items.
| Argument | Type | Description |
|---|---|---|
| kind | string | Only playlists of this media kind: the picture track or the background audio track. |
| sort | string | Order rows by creation time; newest first when omitted. |
| after | string | The `nextCursor` from a previous page of this same tool. |
| first | integer | How many rows to return on this page; 20 when omitted. |
| sceneId | string | Only playlists belonging to this scene (scn_...). |
| streamId | string | Only the playlists of this stream: its id (stream_...), from list_streams or create_stream. |
| createdAfter | string | Only items created at or after this RFC3339 time, e.g. 2026-05-01T00:00:00Z. |
Lists a scene's ingest keys (id ingkey_…, which source, live or not, never the key itself), its secrets (names only) and its IP cameras: what create_scene_ingest_key and set_scene_secret made. Answers ingestKeys (id, sourceId, keyPrefix, status, live, rtmpUrl, rtmpsUrl), secrets (name, updatedAt) and cameras (id, sourceId, url without credentials, transport, the last connection test).
| Argument | Type | Description |
|---|---|---|
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene"
],
"properties": {
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
}
},
"additionalProperties": false
}Lists a scene's published versions, newest first. Answers items (version, publishedAt, publishedBy), nextCursor and hasMore. Read one with scene_get and version.
| Argument | Type | Description |
|---|---|---|
| after | string | The `nextCursor` from a previous page of this same tool. |
| first | integer | How many rows to return on this page; 20 when omitted. |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene"
],
"properties": {
"after": {
"type": "string",
"description": "The `nextCursor` from a previous page of this same tool."
},
"first": {
"type": "integer",
"maximum": 100,
"description": "How many rows to return on this page; 20 when omitted.",
"exclusiveMinimum": 0
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
}
},
"additionalProperties": false
}Lists the workspace's scenes, newest first: id, name, the version last published (0: never) and when. Read one's draft with scene_get (path "" lists what it holds). Answers items (id, name, version, publishedAt, createdAt, updatedAt), nextCursor and hasMore.
| Argument | Type | Description |
|---|---|---|
| after | string | The `nextCursor` from a previous page of this same tool. |
| first | integer | How many rows to return on this page; 20 when omitted. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"after": {
"type": "string",
"description": "The `nextCursor` from a previous page of this same tool."
},
"first": {
"type": "integer",
"maximum": 100,
"description": "How many rows to return on this page; 20 when omitted.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}Where a stream publishes, in the order they were added: each destination, whether it is enabled, and while the stream runs its delivery state (connecting, live, reconnecting, failed with a reason). Returns the stream id and destinations[]: destinationID, enabled, disabledReason, state, stateReason, stateSince, and the destination's id, name, kind and status.
| Argument | Type | Description |
|---|---|---|
| streamId* | string | Stream id (stream_...), from list_streams. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId"
],
"properties": {
"streamId": {
"type": "string",
"description": "Stream id (stream_...), from list_streams."
}
},
"additionalProperties": false
}Find streams and their ids — the starting point for every other stream tool. Each row carries the name, current state, scheduled start and whether that schedule repeats, which is enough to tell a one-off from a daily loop without a second call. Filter by name, run state and creation window. Returns a page of rows plus nextCursor and total. Use get_stream for one stream's full configuration.
| Argument | Type | Description |
|---|---|---|
| name | string | Case-insensitive substring to match the name. |
| sort | string | Order rows by creation time; newest first when omitted. |
| after | string | The `nextCursor` from a previous page of this same tool. |
| first | integer | How many rows to return on this page; 20 when omitted. |
| status | string | Only streams in this run state: active ones are live or starting, scheduled ones are waiting for their window, inactive ones are neither. |
| createdAfter | string | Only items created at or after this RFC3339 time, e.g. 2026-05-01T00:00:00Z. |
| createdBefore | string | Only items created at or before this RFC3339 time. |
Find media and their ids (obj_...) — what apply_playlist_operations adds to a playlist. Removed media is not listed. `uploaded` false means the media is still arriving or processing and cannot be streamed yet. Returns a page of rows (id, name, type, mimeType, fileSize, uploaded, createdAt) plus nextCursor and total; get_upload has one item's duration, resolution and import progress.
| Argument | Type | Description |
|---|---|---|
| name | string | Case-insensitive substring to match the name. |
| sort | string | Order rows by creation time; newest first when omitted. |
| type | string | Only media of this kind. |
| after | string | The `nextCursor` from a previous page of this same tool. |
| first | integer | How many rows to return on this page; 20 when omitted. |
| createdAfter | string | Only items created at or after this RFC3339 time, e.g. 2026-05-01T00:00:00Z. |
| createdBefore | string | Only items created at or before this RFC3339 time. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"name": {
"type": "string",
"description": "Case-insensitive substring to match the name."
},
"sort": {
"enum": [
"newest",
"oldest"
],
"type": "string",
"description": "Order rows by creation time; newest first when omitted."
},
"type": {
"enum": [
"audio",
"video",
"image"
],
"type": "string",
"description": "Only media of this kind."
},
"after": {
"type": "string",
"description": "The `nextCursor` from a previous page of this same tool."
},
"first": {
"type": "integer",
"maximum": 100,
"description": "How many rows to return on this page; 20 when omitted.",
"exclusiveMinimum": 0
},
"createdAfter": {
"type": "string",
"description": "Only items created at or after this RFC3339 time, e.g. 2026-05-01T00:00:00Z."
},
"createdBefore": {
"type": "string",
"description": "Only items created at or before this RFC3339 time."
}
},
"additionalProperties": false
}Every workspace this account belongs to, with the account's role in each and which one is currently in use. Streams, media, playlists, destinations and credits all belong to ONE workspace and are never shared between them, so when a user says something exists and it is not in a list, check here before concluding it is missing — the answer is usually that another workspace is selected. Pass an `id` from here to select_workspace, or give the user the id to pin in their client configuration. `role` matters for workspace administration, which happens in the dashboard. Returns workspaces[] (id, identityId, name, slug, personal, role, active), selectedWorkspaceId and selectionSource (explicit, header, env or token-hint).
No arguments.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {},
"additionalProperties": false
}The YouTube channels this workspace has Google consent for, with live channel data (title, handle, country, subscriber count, connection status). These are NOT destinations yet: pass a channel's `id` to create_youtube_destination to get something a stream can publish to. Use list_destinations to see which channels already have one. An empty result means no Google account is connected — start_youtube_connection.
No arguments.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {},
"additionalProperties": false
}Publishes the draft exactly as it was at draftRevision (required) as the scene's next version: what streams playing it play from then on. All of it is checked first; a refusal (SCENE_INVALID) lists each problem — fix them with scene_set and publish again. Edits made after draftRevision are not published (the answer says so, stale: true). If someone published after the version your draft is based on, it is refused (STALE_PUBLISH: publishedVersion and what would be reverted) — read the draft again; only with the user's say-so send overwritePublished: <that version>. When that version is a revert (publishedVersionIsRevert), reading again changes nothing: overwritePublished, or discard_scene_draft to keep it.A scene on air is refused with CONFIRM_REQUIRED naming the streams — tell the user, then send confirm: true. An unchanged draft publishes nothing; dryRun checks it and lists what would change. Answers summary (what happened and what to do next), version, publishedAt, changes (what changed, as resource paths), notes, and the streams playing it.
| Argument | Type | Description |
|---|---|---|
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| dryRun | boolean | Only check, and say what would change |
| confirm | boolean | true: you accept what a CONFIRM_REQUIRED refusal named — a stream has this scene on air and viewers see the change at once. Send it only after telling the user. |
| draftRevision* | string | The draftRevision of the answer you reviewed (every scene_get and scene_set answer has one) |
Continue the conversation about an existing order — 'never end on an instrumental', 'put the announcement second instead' — producing a new version. Use this rather than creating a second order for the same purpose. Pass the playlist currently being composed against so the rewrite sees its real items. Interpret `status` exactly as for create_playlist_order. A new version does NOT reach playlists already using this order: they stay on the version they were assigned, until assign_playlist_order is called again. Returns, under refineOrderSequencer, the same status, diagnostics, sequencer and version as create_playlist_order.
| Argument | Type | Description |
|---|---|---|
| id* | string | Order id (oseq_...) to refine. |
| message* | string | The change to make, in plain language. |
| playlistId | string | The playlist (pl_...) being composed against on this turn, so the rewrite sees its real items. Omitted: the items of a playlist this order is assigned to; with none, it writes positional rules only. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id",
"message"
],
"properties": {
"id": {
"type": "string",
"description": "Order id (oseq_...) to refine."
},
"message": {
"type": "string",
"description": "The change to make, in plain language."
},
"playlistId": {
"type": "string",
"description": "The playlist (pl_...) being composed against on this turn, so the rewrite sees its real items. Omitted: the items of a playlist this order is assigned to; with none, it writes positional rules only."
}
},
"additionalProperties": false
}Take a destination off a stream (the destination itself stays in the workspace). Refused while the stream is live (STREAM_LIVE): disable it with set_stream_destination_enabled instead. Returns the stream's whole destination list after the change, as list_stream_destinations does.
| Argument | Type | Description |
|---|---|---|
| streamId* | string | Stream id (stream_...), from list_streams. |
| destinationId* | string | Destination id (dest_...), from list_destinations or list_stream_destinations. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId",
"destinationId"
],
"properties": {
"streamId": {
"type": "string",
"description": "Stream id (stream_...), from list_streams."
},
"destinationId": {
"type": "string",
"description": "Destination id (dest_...), from list_destinations or list_stream_destinations."
}
},
"additionalProperties": false
}Changes the scene's display name, the one list_scenes and the dashboard show. Its draft, published versions and the streams playing it are untouched. Answers the scene: id, name, version.
| Argument | Type | Description |
|---|---|---|
| name* | string | The new name |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 120,
"minLength": 1,
"description": "The new name"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
}
},
"additionalProperties": false
}Publishes an earlier version's document as the next version (the versions in between stay; the draft is untouched — publishing the draft later is refused with STALE_PUBLISH until you send overwritePublished, and discard_scene_draft makes the draft this version too). expectedVersion (required) is the latest published version you saw (list_scene_versions, list_scenes): if someone published since, nothing changes (STALE_PUBLISH: publishedVersion and what the revert would also take off air). On air it is refused with CONFIRM_REQUIRED like a publish: tell the user, then send confirm: true.
| Argument | Type | Description |
|---|---|---|
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| confirm | boolean | true: you accept what a CONFIRM_REQUIRED refusal named — a stream has this scene on air and viewers see the change at once. Send it only after telling the user. |
| version* | integer | The version to publish again |
| expectedVersion* | integer | The latest published version you saw (0: none) |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"version",
"expectedVersion"
],
"properties": {
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"confirm": {
"type": "boolean",
"description": "true: you accept what a CONFIRM_REQUIRED refusal named — a stream has this scene on air and viewers see the change at once. Send it only after telling the user."
},
"version": {
"type": "integer",
"maximum": 9007199254740991,
"description": "The version to publish again",
"exclusiveMinimum": 0
},
"expectedVersion": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "The latest published version you saw (0: none)"
}
},
"additionalProperties": false
}Revokes an ingest key for good: the encoder using it is cut off at once. It cannot be restored; a new key for the same source: create_scene_ingest_key. Answers revoked (the key's id).
| Argument | Type | Description |
|---|---|---|
| keyId* | string | The key's id (ingkey_…), from list_scene_inputs |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| confirm | boolean | Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"keyId"
],
"properties": {
"keyId": {
"type": "string",
"minLength": 1,
"description": "The key's id (ingkey_…), from list_scene_inputs"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"confirm": {
"type": "boolean",
"description": "Set true to actually execute this irreversible action. Without it the tool returns a confirmation prompt and makes no changes."
}
},
"additionalProperties": false
}Read the scene's draft (or a published version, with version): a resource (with its revision and children, the sub-paths to read next), a type such as element/Text or source/data (its props), a mask ending in /* (a list), or "" (the types). as: "image" on a frame or layer → a picture of it. (In a scene's draft; scene: its id.) Answers summary and value (the resource, with its revision and children), or image for as "image"; plus draftRevision (version, for a published version).
| Argument | Type | Description |
|---|---|---|
| as | string | image: a picture of a frame (the whole of it, at most 1280 px wide) or of one layer (at full design size, to look closely; origin: where its top-left is in the frame), with what drew and the issues: text outside title-safe (5 % in from each edge: x 96–1824, y 54–1026 at 1920×1080), text too small, a bound table with no rows. json: a frame as JSON (its layers as a node tree) instead of code |
| at | number | Picture: ms since the frame went on air (default: entrances settled) |
| data | string | Picture: bindings read live values (the default: tables as last read) or sample data (each table's own rows). Either way state.* is the state the show starts with — what onStart sets, else each field's default or 0 / "" / false — and controls are their defaults: to see another state, set a field's default or what onStart returns |
| path* | string | type[/kind]/id[/…], a type, a mask ending in /*, or "" for the types |
| limit | integer |
Fetch a URL once, changing nothing in the show: HTTP status, content type, an outline of the JSON (keys, their values, array lengths) or the page's title and text, and the rows a table would get from it with connector, pick and headers — or, with connector mcp, an MCP server's tools, or one tool's rows with tool and args. Use it before creating a URL or MCP table, and to find the pick path or selector. (In a scene's draft; scene: its id.)
| Argument | Type | Description |
|---|---|---|
| url* | string | The http(s) address to read: a JSON API, a CSV or spreadsheet file, a Google Sheet, a web page, or with connector mcp an MCP server's endpoint |
| args | object | mcp, with tool: the arguments to call that tool with, shaped as its input schema (a probe without tool lists each tool's schema). Omitted: none |
| pick | string | json, mcp: a path into the document or the tool's result, e.g. chart.result[0].meta — an array there is the rows, an object one row; html: a CSS selector, each match a row |
| tool | string | mcp: the tool to call; without it, the server's tools are listed |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| sheet | string | xlsx, sheets: which tab |
Delete whole resources (frames, layers, sources, components). All or nothing: anything in use by something not removed in the same call is refused, with what uses it. (In a scene's draft; scene: its id.) Removing a resource needs its revision in `revisions` (path → the revision you read; "*": whatever is there, on purpose); without it the answer is REVISION_REQUIRED. It changes the draft only: viewers see nothing until publish_scene, and discard_scene_draft brings back what was published. Answers summary, removed (the paths) and draftRevision.
| Argument | Type | Description |
|---|---|---|
| paths* | array | The resources to delete, by path as get answers them: frame/intro, frame/intro/title (one layer), source/data/headlines, component/LowerThird |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| revisions | object | path → the revision you read it at (or its frame's; "*": whatever is there); nothing is removed if one changed since (CONFLICT). Over the MCP every path needs one (REVISION_REQUIRED) |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"paths"
],
"properties": {
"paths": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"description": "The resources to delete, by path as get answers them: frame/intro, frame/intro/title (one layer), source/data/headlines, component/LowerThird"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"revisions": {
"type": "object",
"properties": {},
"description": "path → the revision you read it at (or its frame's; \"*\": whatever is there); nothing is removed if one changed since (CONFLICT). Over the MCP every path needs one (REVISION_REQUIRED)",
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
}
}
},
"additionalProperties": false
}Create, replace or change one resource; get <type> shows what each takes, with an example. value is the whole resource, as get answers it: a frame { code: "<JSX>" } or, as JSON, { name, root }; a component { description, props, tsx }; a source or table its fields. edits change part of it: [{ old, new }] replaces text in its code (copy old from what get answers: code is stored formatted), or an object is merged into its fields. One layer changes alone at its own path: frame/<id>/<layer> with edits { props: { … } }. Every set is checked as the studio checks it; a refused set changes nothing: read the error, fix, send again. The answer has the path it is stored at, its new revision, what changed, and draftRevision (the draft as it is now: what publish takes). (In a scene's draft; scene: its id.) Changing a resource that exists needs its `revision`, from the answer that read it ("*" overwrites whatever is there, on purpose); without it the answer is REVISION_REQUIRED. A new path needs none.
| Argument | Type | Description |
|---|---|---|
| path* | string | What to write, e.g. frame/intro, source/data/headlines, component/LowerThird |
| edits | any | Part of the resource: text replacements in its code (a frame's code, a component's tsx, a script), or an object merged into its fields (a JSON merge patch; null removes a field) |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| value | object | The whole resource: a field left out is cleared (to change part of it, send edits) |
| revision | any | The revision get or the last set answered (of this path, or of the frame a layer is in): the set is refused (CONFLICT, with currentRevision) if it changed since. Send it whenever the path exists — "*" replaces whatever is there, knowingly; over the MCP a write to an existing path without one is refused (REVISION_REQUIRED). A new path needs none |
Set the window a stream runs in, once or on a repeat, and arm it. The stream must already be startable — a destination plus either a playable video playlist or an attached published scene (check_stream_readiness) — otherwise the window arrives and the start fails. repeat=daily re-arms the window exactly 24h later; repeat=weekly re-arms +7 days, or on the weekdays given in `days`. Both repeats REQUIRE scheduleEndAt and a window shorter than 24h; a one-off (repeat=none, the default) may be open-ended. `days` are UTC weekdays of the start instant, so a 9pm Monday Eastern window repeats on TUESDAY — the result echoes the committed window in UTC with its UTC weekday so this is visible. Replaces any existing window; cancel_stream_schedule disarms it. Returns the stream (id, name, state, scheduleStartAt, scheduleEndAt, scheduleRepeat, scheduleRepeatDays) and committedWindow: the window in UTC, its UTC start weekday, and the times exactly as you sent them.
| Argument | Type | Description |
|---|---|---|
| days | array | Only with repeat=weekly: the UTC weekdays the window starts on (distinct). Omit for the same UTC weekday as scheduleStartAt, every 7 days. |
| repeat | string | How the window recurs: once when omitted, every 24h, or every 7 days (or on `days`). A repeat needs scheduleEndAt. |
| streamId* | string | Stream id (stream_...) to schedule. |
| scheduleEndAt | string | When the stream should stop. Needed for a daily or weekly repeat, less than 24h after the start; omit for a one-off that runs until stopped. An instant in the user's own time zone, with that zone's offset: 6am US Eastern daylight time is 2026-09-22T06:00:00-04:00. The user's clock numbers are not UTC. |
Switch which workspace this connection works in, for every later tool call on this connection (other connections, even ones sharing the same login, are unaffected). Takes an `id` from list_workspaces; membership is checked here and again by the API on every request, so this grants no access by itself. Use it when a stream, upload or destination the user describes is not in this workspace. Overrides a workspace the client pinned in its own configuration (the X-Workspace-Id header, or MCP_WORKSPACE_ID on stdio) for the rest of this connection. Call it again at any time to switch back.
| Argument | Type | Description |
|---|---|---|
| workspaceId* | string | A workspace `id` (wksp_...) from list_workspaces. The organization id is also accepted. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"workspaceId"
],
"properties": {
"workspaceId": {
"type": "string",
"description": "A workspace `id` (wksp_...) from list_workspaces. The organization id is also accepted."
}
},
"additionalProperties": false
}Saves an API key or token a scene's tables send (sealed; never shown again). A table names it as "$secret:<name>" in a header value or a URL's query. A name that exists already has its value replaced, and tables using it send the new one. Only a key the user gave you. Answers name and updatedAt.
| Argument | Type | Description |
|---|---|---|
| name* | string | The name tables refer to it by, as "$secret:<name>", e.g. weatherKey |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| value* | string | The API key or token itself, exactly as the user gave it |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"pattern": "^[A-Za-z0-9_.-]{1,64}$",
"description": "The name tables refer to it by, as \"$secret:<name>\", e.g. weatherKey"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"value": {
"type": "string",
"minLength": 1,
"description": "The API key or token itself, exactly as the user gave it"
}
},
"additionalProperties": false
}Stop or resume sending to one of a stream's destinations without removing it. Disabling works while the stream is live — that output goes off the air at once — except for its last enabled destination (LAST_DESTINATION: stop_stream instead). Enabling is refused while live (STREAM_LIVE). Returns the stream's whole destination list after the change, as list_stream_destinations does.
| Argument | Type | Description |
|---|---|---|
| enabled* | boolean | true: send to it; false: stop sending to it. |
| streamId* | string | Stream id (stream_...), from list_streams. |
| destinationId* | string | Destination id (dest_...), from list_destinations or list_stream_destinations. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId",
"destinationId",
"enabled"
],
"properties": {
"enabled": {
"type": "boolean",
"description": "true: send to it; false: stop sending to it."
},
"streamId": {
"type": "string",
"description": "Stream id (stream_...), from list_streams."
},
"destinationId": {
"type": "string",
"description": "Destination id (dest_...), from list_destinations or list_stream_destinations."
}
},
"additionalProperties": false
}Makes a stream play a scene (its published version) instead of a video playlist, or with scene null stops doing so. Refused (STREAM_LIVE) while the stream runs: stop it, set the scene, start it again. The scene must be published first (publish_scene).
| Argument | Type | Description |
|---|---|---|
| scene* | any | The scene (scn_…); null: none |
| streamId* | string | A stream's id, from list_streams. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId",
"scene"
],
"properties": {
"scene": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The scene (scn_…); null: none"
},
"streamId": {
"type": "string",
"minLength": 1,
"description": "A stream's id, from list_streams."
}
},
"additionalProperties": false
}Start streaming now, looping the stream's playlist (or playing its scene) until it is stopped. THIS PUTS VIDEO IN FRONT OF THE PUBLIC, so confirm with the user first unless they just asked for it. Requires a destination plus either a playable video playlist (at least one item) or an attached published scene (set_stream_scene) — check_stream_readiness reports what is missing without changing anything, and is worth calling first. A failed pre-flight is NOT an error: the call succeeds and the answer's `state` is `invalid` — read `state` first: invalid means refused, with `issues` saying why (fix them and start again); `preparing` means accepted. Media facts (duration, sound) arrive about 15-25 s after an upload completes, so a playlist of just-finished uploads can be refused until then. Other refusals (a state that cannot start, a guard failing at the moment of starting) are errors. Credits are NOT part of the pre-flight: the balance is verified when the stream actually activates, so an otherwise-ready stream can still fail to start on an empty wallet — get_billing_overview is what tells you that. A playlist loop takes the playlist as it currently is, with nothing to publish first; a scene plays its published version (publish_scene). Asynchronous: the stream reports a starting state immediately and is live within about a minute. To start at a future time (or every day) use schedule_stream instead. Returns state, started (false when refused), issues (when refused) and startStream (the stream: id, name, state).
| Argument | Type | Description |
|---|---|---|
| streamId* | string | Stream id (stream_...) to start. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId"
],
"properties": {
"streamId": {
"type": "string",
"description": "Stream id (stream_...) to start."
}
},
"additionalProperties": false
}Begin connecting a Google account so its YouTube channels can be streamed to with managed broadcasts (titles, privacy, thumbnails, live stats). Returns a Google consent URL — give it to the user to open in a browser; consent cannot be completed by an agent. Afterwards, list_youtube_channels shows the authorized channels and create_youtube_destination turns one into a destination. Also the fix for a destination whose status is needs_reauth or revoked. Not needed for plain RTMP publishing to YouTube — that is create_rtmp_destination, but it cannot manage the broadcast.
| Argument | Type | Description |
|---|---|---|
| returnUrl | string | Where Google should send the user after consent. Defaults to the Streamloop destinations page. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"returnUrl": {
"type": "string",
"description": "Where Google should send the user after consent. Defaults to the Streamloop destinations page."
}
},
"additionalProperties": false
}Stop a stream that is live or starting. It stops publishing and becomes inactive; nothing is deleted and it can be started again with the same configuration. Does NOT cancel a recurring schedule — a daily or weekly stream stopped here starts again at its next window, so call cancel_stream_schedule as well to keep it off.
| Argument | Type | Description |
|---|---|---|
| streamId* | string | Stream id (stream_...) to stop. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId"
],
"properties": {
"streamId": {
"type": "string",
"description": "Stream id (stream_...) to stop."
}
},
"additionalProperties": false
}Use this ONLY to put an edited playlist on the air while its stream is LIVE — the hot swap the product calls publishing. The stream changes over to the new arrangement without going off the air. NOT needed to set a stream up and NOT needed before a first start: starting a stream takes the playlist as it is then, so on a stream that is not live this tool just fails with NOT_LIVE. Asynchronous — newly added media may have to be encoded first, so poll get_playlist until the swap is no longer in progress. expectedDraftVersion is required (the `version` from get_playlist) so a concurrent edit is reported instead of going live by accident. Other failures name their cause: STALE_DRAFT (refetch and retry), EMPTY_PLAYLIST, BROKEN_ITEM (an item's media was deleted or never finished uploading), RECIPE_MISMATCH, NOT_FOUND.
| Argument | Type | Description |
|---|---|---|
| playlistId* | string | Playlist id (pl_...) whose draft should go live. |
| expectedDraftVersion* | integer | Required: the `version` from get_playlist. On STALE_DRAFT, refetch and retry. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"playlistId",
"expectedDraftVersion"
],
"properties": {
"playlistId": {
"type": "string",
"description": "Playlist id (pl_...) whose draft should go live."
},
"expectedDraftVersion": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Required: the `version` from get_playlist. On STALE_DRAFT, refetch and retry."
}
},
"additionalProperties": false
}Stop a playlist using a custom order; it returns to playing in its own item order (or shuffled, per the playlist's mode). The order itself is kept and can be assigned again. Like assigning, it takes effect on a running stream immediately, with no swap_live_playlist needed.
| Argument | Type | Description |
|---|---|---|
| playlistId* | string | Playlist (pl_...) to clear the custom order from. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"playlistId"
],
"properties": {
"playlistId": {
"type": "string",
"description": "Playlist (pl_...) to clear the custom order from."
}
},
"additionalProperties": false
}Maintain an existing destination: rename it, replace the stream key after the platform rotated it (custom RTMP only — a connected YouTube channel has no key to set), and archive or restore it. Archiving hides a destination from the picker without deleting it; it is reversible with archived=false. A live stream keeps publishing with the key it started with, so restart the stream after rotating a key. Pass only the fields to change. NOT transactional: each change commits on its own, and on a failure the response lists which ones already applied.
| Argument | Type | Description |
|---|---|---|
| id* | string | Destination id (dest_...), from list_destinations. |
| name | string | New display name. |
| rtmpKey | string | The new stream key, copied from the platform's own streaming settings after it rotated the old one. Custom RTMP destinations only. Stored encrypted and never shown again. |
| archived | boolean | true archives the destination (reversible); false restores it. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Destination id (dest_...), from list_destinations."
},
"name": {
"type": "string",
"description": "New display name."
},
"rtmpKey": {
"type": "string",
"description": "The new stream key, copied from the platform's own streaming settings after it rotated the old one. Custom RTMP destinations only. Stored encrypted and never shown again."
},
"archived": {
"type": "boolean",
"description": "true archives the destination (reversible); false restores it."
}
},
"additionalProperties": false
}Change a stream's settings (name, quality, framerate, duration, storage, backup ingest) and/or its YouTube broadcast settings via the `youtube` object. Where it publishes is add_stream_destination / set_stream_destination_enabled / remove_stream_destination; when it runs is schedule_stream. The `youtube` settings only apply when one of the stream's destinations is a YouTube channel connected through Streamloop — not a custom RTMP destination, and not YouTube reached through a raw RTMP URL and key — and are otherwise skipped with a notice. Set retryMirror=true to re-attempt mirroring the schedule onto YouTube after a reconnect or a quota recovery. PARTIAL SUCCESS: settings are applied first, so if the YouTube settings then fail, `stream` still reflects the applied settings and `youtubeSettingsError` explains the YouTube failure — the settings change is NOT rolled back.
| Argument | Type | Description |
|---|---|---|
| id* | string | Stream id (stream_...) to update. |
| name | string | Display name for the stream. |
| quality | string | Output resolution sent to every destination; the account's preferred quality when omitted. |
| youtube | object | YouTube broadcast settings (title/privacy/thumbnail/etc.). ONLY take effect when the stream's destination is a YouTube channel connected through Streamloop. They are NOT supported for custom RTMP destinations or for streaming to YouTube via a raw RTMP URL and stream key — there is no API access to that broadcast. The tool skips them with a notice in that case. |
| framerate | string | Frames per second the stream is sent at. |
| target | string | goFrame, next: which frame, by the id in its path frame/<id> |
| actionId | string | Your id for this one action (a new uuid each time): a retry with the same actionId applies it at most once |
| sourceId | string | setData: the table's id (as in source/data/<id>); skip: the playlist source's id (default: the scene's first) |
| streamId | string | The stream (stream_…) to act on, from get_scene_state; needed only when more than one stream plays the scene |
| controlId | string | setControl: the control's id, the <id> of control/<id>; scene_get control/* lists them |
| transition | any | goFrame: how it cuts — a kind, or { kind, ms } (default: the frame's own) |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"op"
],
"properties": {
"op": {
"enum": [
"goFrame",
"next",
"setControl",
"setData",
"skip"
],
"type": "string",
"description": "The action: take a frame to air, cue the next frame, set a control, replace a table's rows, or skip a playlist source to its next item"
},
"rows": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "setData: the new rows, each an object of column → value; they replace every row the table has now"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"value": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string"
},
{
"type": "number"
},
{
"type": "null"
}
],
"description": "setControl: the value (a toggle true or false, a text a string, a number a number, a select one of its options; none for a button)"
},
"target": {
"type": "string",
"description": "goFrame, next: which frame, by the id in its path frame/<id>"
},
"actionId": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Your id for this one action (a new uuid each time): a retry with the same actionId applies it at most once"
},
"sourceId": {
"type": "string",
"description": "setData: the table's id (as in source/data/<id>); skip: the playlist source's id (default: the scene's first)"
},
"streamId": {
"type": "string",
"minLength": 1,
"description": "The stream (stream_…) to act on, from get_scene_state; needed only when more than one stream plays the scene"
},
"controlId": {
"type": "string",
"description": "setControl: the control's id, the <id> of control/<id>; scene_get control/* lists them"
},
"transition": {
"anyOf": [
{
"anyOf": [
{
"enum": [
"cut",
"fade",
"slide",
"wipe",
"dip"
],
"type": "string",
"description": "The transition style; cut is instant"
},
{
"type": "object",
"required": [
"kind"
],
"properties": {
"ms": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "How long, in ms"
},
"kind": {
"enum": [
"cut",
"fade",
"slide",
"wipe",
"dip"
],
"type": "string",
"description": "The transition style; cut is instant"
}
},
"additionalProperties": false
}
]
},
{
"type": "null"
}
],
"description": "goFrame: how it cuts — a kind, or { kind, ms } (default: the frame's own)"
}
},
"additionalProperties": false
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"name": {
"type": "string",
"description": "Display name for the stream."
},
"quality": {
"enum": [
"q_720p",
"q_1080p",
"q_1440p",
"q_2160p"
],
"type": "string",
"description": "Output resolution sent to every destination; the account's preferred quality when omitted."
},
"framerate": {
"enum": [
"f_24",
"f_25",
"f_30",
"f_60"
],
"type": "string",
"description": "Frames per second the stream is sent at."
},
"storageBackend": {
"enum": [
"standard",
"large"
],
"type": "string",
"description": "Media storage: standard (included) or large, for big playlists, billed per GiB-month."
},
"streamDuration": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Stop after this many seconds of streaming; 0 = run until stopped."
},
"runBackupStream": {
"type": "boolean",
"description": "Also publish to YouTube's backup ingest for redundancy. Doubles the streaming cost while live."
}
},
"additionalProperties": false
}| integer |
| Size of the file in bytes; give it when known. |
| mimeType* | string | The file's MIME type, e.g. video/mp4 or audio/mpeg. The PUT to uploadUrl must send this exact value as its Content-Type header. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"name",
"type",
"mimeType"
],
"properties": {
"name": {
"type": "string",
"description": "File name, e.g. 'intro.mp4'."
},
"type": {
"enum": [
"audio",
"video",
"image"
],
"type": "string",
"description": "The kind of media the file holds."
},
"width": {
"type": "integer",
"maximum": 9007199254740991,
"description": "Width of a video or image, in pixels.",
"exclusiveMinimum": 0
},
"height": {
"type": "integer",
"maximum": 9007199254740991,
"description": "Height of a video or image, in pixels.",
"exclusiveMinimum": 0
},
"duration": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Length of a video or audio file, in seconds. Omitted: measured from the file once it is processed."
},
"fileSize": {
"type": "integer",
"maximum": 9007199254740991,
"description": "Size of the file in bytes; give it when known.",
"exclusiveMinimum": 0
},
"mimeType": {
"type": "string",
"description": "The file's MIME type, e.g. video/mp4 or audio/mpeg. The PUT to uploadUrl must send this exact value as its Content-Type header."
}
},
"additionalProperties": false
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"id": {
"type": "string",
"description": "Look up a single destination by id (dest_...)."
},
"kind": {
"enum": [
"custom_rtmp",
"youtube"
],
"type": "string",
"description": "Filter by kind: a connected YouTube channel, or a custom RTMP endpoint."
},
"name": {
"type": "string",
"description": "Case-insensitive substring to match destination names."
},
"sort": {
"enum": [
"newest",
"oldest"
],
"type": "string",
"description": "Order rows by creation time; newest first when omitted."
},
"after": {
"type": "string",
"description": "The `nextCursor` from a previous page of this same tool."
},
"first": {
"type": "integer",
"maximum": 100,
"description": "How many rows to return on this page; 20 when omitted.",
"exclusiveMinimum": 0
},
"status": {
"enum": [
"active",
"needs_reauth",
"revoked",
"disabled"
],
"type": "string",
"description": "Only destinations in this state. Streams publish only to active ones; needs_reauth and revoked ones need start_youtube_connection again."
}
},
"additionalProperties": false
}| createdBefore |
| string |
| Only items created at or before this RFC3339 time. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"kind": {
"enum": [
"video",
"audio"
],
"type": "string",
"description": "Only playlists of this media kind: the picture track or the background audio track."
},
"sort": {
"enum": [
"newest",
"oldest"
],
"type": "string",
"description": "Order rows by creation time; newest first when omitted."
},
"after": {
"type": "string",
"description": "The `nextCursor` from a previous page of this same tool."
},
"first": {
"type": "integer",
"maximum": 100,
"description": "How many rows to return on this page; 20 when omitted.",
"exclusiveMinimum": 0
},
"sceneId": {
"type": "string",
"description": "Only playlists belonging to this scene (scn_...)."
},
"streamId": {
"type": "string",
"description": "Only the playlists of this stream: its id (stream_...), from list_streams or create_stream."
},
"createdAfter": {
"type": "string",
"description": "Only items created at or after this RFC3339 time, e.g. 2026-05-01T00:00:00Z."
},
"createdBefore": {
"type": "string",
"description": "Only items created at or before this RFC3339 time."
}
},
"additionalProperties": false
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"name": {
"type": "string",
"description": "Case-insensitive substring to match the name."
},
"sort": {
"enum": [
"newest",
"oldest"
],
"type": "string",
"description": "Order rows by creation time; newest first when omitted."
},
"after": {
"type": "string",
"description": "The `nextCursor` from a previous page of this same tool."
},
"first": {
"type": "integer",
"maximum": 100,
"description": "How many rows to return on this page; 20 when omitted.",
"exclusiveMinimum": 0
},
"status": {
"enum": [
"active",
"inactive",
"scheduled"
],
"type": "string",
"description": "Only streams in this run state: active ones are live or starting, scheduled ones are waiting for their window, inactive ones are neither."
},
"createdAfter": {
"type": "string",
"description": "Only items created at or after this RFC3339 time, e.g. 2026-05-01T00:00:00Z."
},
"createdBefore": {
"type": "string",
"description": "Only items created at or before this RFC3339 time."
}
},
"additionalProperties": false
}| overwritePublished | integer | You accept what a STALE_PUBLISH refusal named: its publishedVersion, which this publish replaces |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"draftRevision"
],
"properties": {
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"dryRun": {
"type": "boolean",
"description": "Only check, and say what would change"
},
"confirm": {
"type": "boolean",
"description": "true: you accept what a CONFIRM_REQUIRED refusal named — a stream has this scene on air and viewers see the change at once. Send it only after telling the user."
},
"draftRevision": {
"type": "string",
"minLength": 1,
"description": "The draftRevision of the answer you reviewed (every scene_get and scene_set answer has one)"
},
"overwritePublished": {
"type": "integer",
"maximum": 9007199254740991,
"description": "You accept what a STALE_PUBLISH refusal named: its publishedVersion, which this publish replaces",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}| Reading a table's rows (source/data/<id>/rows): how many to return; 20 when omitted |
| scene* | string | The scene (scn_…), from list_scenes or create_scene. |
| offset | integer | Reading a table's rows (source/data/<id>/rows): the 0-based index of the first one; 0 when omitted |
| overlay | string | Picture: a guide drawn over it to check placement — grid: lines every 10 % labelled in design pixels; ids: each layer's outline and id. Omitted: none |
| version | any | Read a saved published version instead of the draft: its number, or "published" for the latest one — published even when no stream is running. What a running stream shows: get_scene_state |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"path"
],
"properties": {
"as": {
"enum": [
"image",
"json"
],
"type": "string",
"description": "image: a picture of a frame (the whole of it, at most 1280 px wide) or of one layer (at full design size, to look closely; origin: where its top-left is in the frame), with what drew and the issues: text outside title-safe (5 % in from each edge: x 96–1824, y 54–1026 at 1920×1080), text too small, a bound table with no rows. json: a frame as JSON (its layers as a node tree) instead of code"
},
"at": {
"type": "number",
"description": "Picture: ms since the frame went on air (default: entrances settled)"
},
"data": {
"enum": [
"live",
"sample"
],
"type": "string",
"description": "Picture: bindings read live values (the default: tables as last read) or sample data (each table's own rows). Either way state.* is the state the show starts with — what onStart sets, else each field's default or 0 / \"\" / false — and controls are their defaults: to see another state, set a field's default or what onStart returns"
},
"path": {
"type": "string",
"description": "type[/kind]/id[/…], a type, a mask ending in /*, or \"\" for the types"
},
"limit": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Reading a table's rows (source/data/<id>/rows): how many to return; 20 when omitted"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"offset": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Reading a table's rows (source/data/<id>/rows): the 0-based index of the first one; 0 when omitted"
},
"overlay": {
"enum": [
"none",
"grid",
"ids"
],
"type": "string",
"description": "Picture: a guide drawn over it to check placement — grid: lines every 10 % labelled in design pixels; ids: each layer's outline and id. Omitted: none"
},
"version": {
"anyOf": [
{
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991
},
{
"type": "string",
"const": "published"
}
],
"description": "Read a saved published version instead of the draft: its number, or \"published\" for the latest one — published even when no stream is running. What a running stream shows: get_scene_state"
}
},
"additionalProperties": false
}| headers |
| object |
| Request headers, e.g. { Authorization: "Bearer <key>" } — a key the user gave, never a guessed one |
| connector | string | How to read it as rows (default: from the content type); mcp: the URL is an MCP server |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"url"
],
"properties": {
"url": {
"type": "string",
"description": "The http(s) address to read: a JSON API, a CSV or spreadsheet file, a Google Sheet, a web page, or with connector mcp an MCP server's endpoint"
},
"args": {
"type": "object",
"properties": {},
"description": "mcp, with tool: the arguments to call that tool with, shaped as its input schema (a probe without tool lists each tool's schema). Omitted: none",
"additionalProperties": {}
},
"pick": {
"type": "string",
"description": "json, mcp: a path into the document or the tool's result, e.g. chart.result[0].meta — an array there is the rows, an object one row; html: a CSS selector, each match a row"
},
"tool": {
"type": "string",
"description": "mcp: the tool to call; without it, the server's tools are listed"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"sheet": {
"type": "string",
"description": "xlsx, sheets: which tab"
},
"headers": {
"type": "object",
"properties": {},
"description": "Request headers, e.g. { Authorization: \"Bearer <key>\" } — a key the user gave, never a guessed one",
"additionalProperties": {
"type": "string"
}
},
"connector": {
"enum": [
"json",
"csv",
"xlsx",
"sheets",
"html",
"mcp"
],
"type": "string",
"description": "How to read it as rows (default: from the content type); mcp: the URL is an MCP server"
}
},
"additionalProperties": false
}{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"scene",
"path"
],
"properties": {
"path": {
"type": "string",
"description": "What to write, e.g. frame/intro, source/data/headlines, component/LowerThird"
},
"edits": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"required": [
"new",
"old"
],
"properties": {
"new": {
"type": "string",
"description": "The text that takes old's place; empty deletes it"
},
"old": {
"type": "string",
"description": "Text as it is now (copied from what get answered); it matches ignoring whitespace and must match once"
},
"field": {
"type": "string",
"description": "Which text, when the resource has more than one (a web source's styles); default: its code"
}
},
"additionalProperties": {}
}
},
{
"type": "object",
"properties": {},
"additionalProperties": {}
}
],
"description": "Part of the resource: text replacements in its code (a frame's code, a component's tsx, a script), or an object merged into its fields (a JSON merge patch; null removes a field)"
},
"scene": {
"type": "string",
"minLength": 1,
"description": "The scene (scn_…), from list_scenes or create_scene."
},
"value": {
"type": "object",
"properties": {},
"description": "The whole resource: a field left out is cleared (to change part of it, send edits)",
"additionalProperties": {}
},
"revision": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
],
"description": "The revision get or the last set answered (of this path, or of the frame a layer is in): the set is refused (CONFLICT, with currentRevision) if it changed since. Send it whenever the path exists — \"*\" replaces whatever is there, knowingly; over the MCP a write to an existing path without one is refused (REVISION_REQUIRED). A new path needs none"
}
},
"additionalProperties": false
}| scheduleStartAt* |
| string |
| When the stream should start. An instant in the user's own time zone, with that zone's offset: 6am US Eastern daylight time is 2026-09-22T06:00:00-04:00. The user's clock numbers are not UTC. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"streamId",
"scheduleStartAt"
],
"properties": {
"days": {
"type": "array",
"items": {
"enum": [
"SUNDAY",
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY"
],
"type": "string"
},
"description": "Only with repeat=weekly: the UTC weekdays the window starts on (distinct). Omit for the same UTC weekday as scheduleStartAt, every 7 days."
},
"repeat": {
"enum": [
"none",
"daily",
"weekly"
],
"type": "string",
"description": "How the window recurs: once when omitted, every 24h, or every 7 days (or on `days`). A repeat needs scheduleEndAt."
},
"streamId": {
"type": "string",
"description": "Stream id (stream_...) to schedule."
},
"scheduleEndAt": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?([Zz]|[+-]\\d{2}:\\d{2})$",
"description": "When the stream should stop. Needed for a daily or weekly repeat, less than 24h after the start; omit for a one-off that runs until stopped. An instant in the user's own time zone, with that zone's offset: 6am US Eastern daylight time is 2026-09-22T06:00:00-04:00. The user's clock numbers are not UTC."
},
"scheduleStartAt": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?([Zz]|[+-]\\d{2}:\\d{2})$",
"description": "When the stream should start. An instant in the user's own time zone, with that zone's offset: 6am US Eastern daylight time is 2026-09-22T06:00:00-04:00. The user's clock numbers are not UTC."
}
},
"additionalProperties": false
}| retryMirror | boolean | true re-attempts copying this stream's schedule onto its YouTube broadcast, e.g. after reconnecting the channel. |
| storageBackend | string | Media storage: standard (included) or large, for big playlists, billed per GiB-month. |
| streamDuration | integer | Stop after this many seconds of streaming; 0 = run until stopped. |
| runBackupStream | boolean | Also publish to YouTube's backup ingest for redundancy. Doubles the streaming cost while live. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Stream id (stream_...) to update."
},
"name": {
"type": "string",
"description": "Display name for the stream."
},
"quality": {
"enum": [
"q_720p",
"q_1080p",
"q_1440p",
"q_2160p"
],
"type": "string",
"description": "Output resolution sent to every destination; the account's preferred quality when omitted."
},
"youtube": {
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "YouTube search tags for the broadcast; replaces the current tags."
},
"title": {
"type": "string",
"description": "Broadcast title."
},
"privacy": {
"enum": [
"public",
"unlisted",
"private"
],
"type": "string",
"description": "Who can find and watch the broadcast on YouTube: everyone, only people with the link, or only the channel."
},
"enableDvr": {
"type": "boolean",
"description": "Let viewers pause and rewind the live broadcast on YouTube."
},
"categoryID": {
"type": "string",
"description": "YouTube's numeric video category id, as a string: \"10\" is Music, \"20\" Gaming, \"22\" People & Blogs."
},
"embeddable": {
"type": "boolean",
"description": "Allow other websites to embed the broadcast; false blocks embedding."
},
"adFrequency": {
"enum": [
"off",
"conservative",
"balanced",
"aggressive"
],
"type": "string",
"description": "How often Streamloop asks YouTube for a mid-roll ad break. Ads only serve on a channel in the YouTube Partner Program; off sends no breaks."
},
"description": {
"type": "string",
"description": "The text shown under the broadcast on YouTube."
},
"madeForKids": {
"enum": [
"unspecified",
"yes",
"no"
],
"type": "string",
"description": "YouTube's required made-for-kids (COPPA) declaration. A stream still at unspecified will not start until it is answered yes or no; ask the user, never assume."
},
"ageRestricted": {
"enum": [
"unspecified",
"yes",
"no"
],
"type": "string",
"description": "Mark the broadcast 18+ on YouTube (yes), clear that mark (no), or leave YouTube's own setting alone (unspecified)."
},
"defaultLanguage": {
"type": "string",
"description": "Language of the title and description, as a BCP-47 code such as en or es-MX."
},
"recordFromStart": {
"type": "boolean",
"description": "Keep the broadcast on the channel as a video once it ends."
},
"thumbnailSource": {
"enum": [
"auto",
"custom",
"none"
],
"type": "string",
"description": "Where the broadcast thumbnail comes from: auto takes a frame of the first video in the playlist, custom uses the image in thumbnailObjectID, none leaves YouTube's own thumbnail untouched."
},
"latencyPreference": {
"enum": [
"normal",
"low",
"ultra_low"
],
"type": "string",
"description": "YouTube's live latency mode: lower latency means faster chat interaction but less buffering against network hiccups."
},
"thumbnailObjectID": {
"type": "string",
"description": "The image used when thumbnailSource is custom: a media id (obj_...) of an uploaded image, from list_uploads or create_upload."
},
"defaultAudioLanguage": {
"type": "string",
"description": "Language spoken in the broadcast audio, as a BCP-47 code such as en or es-MX."
},
"scheduleOnYoutubeWhenScheduled": {
"type": "boolean",
"description": "When the stream is scheduled, create its YouTube broadcast right away so it shows as upcoming on the channel, instead of when the stream starts. Cannot change while the stream is live."
}
},
"description": "YouTube broadcast settings (title/privacy/thumbnail/etc.). ONLY take effect when the stream's destination is a YouTube channel connected through Streamloop. They are NOT supported for custom RTMP destinations or for streaming to YouTube via a raw RTMP URL and stream key — there is no API access to that broadcast. The tool skips them with a notice in that case."
},
"framerate": {
"enum": [
"f_24",
"f_25",
"f_30",
"f_60"
],
"type": "string",
"description": "Frames per second the stream is sent at."
},
"retryMirror": {
"type": "boolean",
"description": "true re-attempts copying this stream's schedule onto its YouTube broadcast, e.g. after reconnecting the channel."
},
"storageBackend": {
"enum": [
"standard",
"large"
],
"type": "string",
"description": "Media storage: standard (included) or large, for big playlists, billed per GiB-month."
},
"streamDuration": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Stop after this many seconds of streaming; 0 = run until stopped."
},
"runBackupStream": {
"type": "boolean",
"description": "Also publish to YouTube's backup ingest for redundancy. Doubles the streaming cost while live."
}
},
"additionalProperties": false
}