Skip to main content

Browse & Inputs API Reference

Four REST routes let a control surface walk the FXCanvas content as folders, activate any item, and preview a live input. These routes drive the Stream Deck Plugin. They are control-surface agnostic, so any HTTP client can use them.

For the rest of the API, see the interactive REST API Reference. For the server and authentication, see the REST API guide.


Conventions

TopicDetail
Base URLhttp://<host>:<port>/api/v1 — default http://127.0.0.1:8080/api/v1.
License ProThe REST server runs only with an activated license. Without a license, the server does not start, so a client gets a connection error and not an HTTP status. See What requires a license.
AuthenticationOptional. When you set an API key, send X-API-Key: <key> or Authorization: Bearer <key> on every request.
Name matchingThe API matches an identifier on id first. It then matches on name without regard to case, with space, underscore, and hyphen as equivalent characters. It ignores the whitespace at each end.
Error body{ "error": true, "code": "<CODE>", "message": "<text>" }, with an extra details object on ITEM_NOT_FOUND and ACTIVATION_TIMEOUT.

GET /api/v1/browse

Returns the contents of one folder of the browsable hierarchy.

Method: GETPath: /api/v1/browse

The hierarchy has up to four root folders, in this order:

  • /sources — source types
  • /presets — source presets
  • /live — live inputs, one folder for each provider type
  • /files — the media folders of your media library

An item node carries a thumbnailUrl and an activate descriptor that you can send without a change. The thumbnailUrl prefix follows the item kind: /api/v1/thumbnails/live/, /media/, or the prefix for a source type or a preset. A folder node carries a browsePath and a childCount. One item node per response has activeState true, at most, and it is the item that GET /api/v1/sources/current reports. The response omits unavailable devices, unavailable media files, and unsupported media files, and it does not count them.

The API omits a root folder that has no children. A show with no capture hardware does not get /live at all. It does not get an empty /live folder either. After you attach a camera, the folder appears at the next rebuild. The paths stay addressable in both cases. A client with a retained path to a folder that is now empty gets an empty listing, not a 404.

In all other cases the API lists a folder exactly as it is, with every item that it holds. A folder of 30 source types is a long scan on a control surface. The API does not subdivide the folder. Pin the few items that you use to keys of their own. The activate descriptor on every item node makes this possible for any client.

Parameters

ParameterInRequiredRangeDefault
pathqueryNo1–512 characters, only A-Z, a-z, 0-9, /, _, -. The API ignores a trailing /./ (the root)
depthqueryNoInteger 1–3. 1 returns the direct children with no children array. 2 and 3 expand the nested folders recursively. The children of each folder match a direct request for its browsePath.1

A folder of 500 nodes or fewer answers within 250 ms. A folder of 501 to 5000 nodes answers within 1000 ms. These times are for depth 1 over loopback.

Example success response

GET /api/v1/browse?path=/live200 OK

{
"path": "/live",
"parentPath": "/",
"count": 2,
"items": [
{
"kind": "folder",
"id": "folder:4a91c0de77b2",
"name": "NDI",
"activeState": false,
"browsePath": "/live/ndi",
"childCount": 3
},
{
"kind": "liveInput",
"id": "liveInput:webcam_0",
"name": "Logitech C920",
"activeState": true,
"thumbnailUrl": "/api/v1/thumbnails/live/webcam_0",
"activate": {
"method": "POST",
"path": "/api/v1/inputs/live/select",
"body": { "id": "webcam_0" }
}
}
]
}

items is always an array. An empty folder returns [], never null. parentPath is null at the root.

Error codes

StatuscodeCondition
400INVALID_PATHpath is longer than 512 characters, or it has characters outside the permitted set.
400INVALID_DEPTHdepth is not an integer from 1 to 3.
404PATH_NOT_FOUNDpath is correct in form, but it names no folder. The active source does not change.

POST /api/v1/inputs/live/select

Makes one live input the current source.

Method: POSTPath: /api/v1/inputs/live/select

GET /api/v1/browse advertises this route as activate on a liveInput node, and you can post it exactly as advertised. Inside FXCanvas, the route activates the input asynchronously. FXCanvas connects the device. It sends the response after the input connects and the first frame arrives, within a budget of 5000 ms. A request for the live input that is already current changes nothing, and it still answers 200.

Parameters

ParameterInRequiredRange
idbodyOne of id or nameString of 1–512 characters, matched against the device id first.
namebodyOne of id or nameString of 1–512 characters, matched by flexible name matching when no id matches.
{ "id": "webcam_0" }

Example success response

200 OK

{
"success": true,
"current": {
"id": "webcam_0",
"name": "Logitech C920",
"sourceKind": "live",
"type": "live"
}
}

current uses the same schema as GET /api/v1/sources/current.

Error codes

StatuscodeCondition
400INVALID_REQUESTThe body carries no usable id or name string of 1 to 512 characters. The active source does not change.
404ITEM_NOT_FOUNDNo available live input matches the identifier. details.available lists at most 50 identifiers of that kind.
504ACTIVATION_TIMEOUTThe input did not connect and deliver a frame within 5000 ms. The source that was current before the request still drives the output.
{
"error": true,
"code": "ITEM_NOT_FOUND",
"message": "No live input matching 'webcam_9'",
"details": {
"requested": "webcam_9",
"kind": "live input",
"available": ["webcam_0", "ndi_studio_a"]
}
}

POST /api/v1/inputs/file/select

Makes one media file from the media library the current source.

Method: POSTPath: /api/v1/inputs/file/select

GET /api/v1/browse advertises this route as activate on a fileInput node, and you can post it exactly as advertised. FXCanvas loads the file asynchronously. It sends the response after the file loads and drives the output, within a budget of 5000 ms. A request for the file input that is already current changes nothing, and it still answers 200.

Parameters

ParameterInRequiredRange
idbodyOne of id or nameString of 1–512 characters, matched against the media file id first.
namebodyOne of id or nameString of 1–512 characters, matched by flexible name matching when no id matches.
{ "id": "8c1f4b2ad930" }

Example success response

200 OK

{
"success": true,
"current": {
"id": "8c1f4b2ad930",
"name": "city_loop.mp4",
"sourceKind": "file",
"type": "video"
}
}

Error codes

StatuscodeCondition
400INVALID_REQUESTThe body carries no usable id or name string of 1 to 512 characters. The active source does not change.
404ITEM_NOT_FOUNDNo available and supported media file matches the identifier. details.available lists at most 50 identifiers of that kind.
504ACTIVATION_TIMEOUTThe media file did not load and take over the output within 5000 ms. The source that was current before the request still drives the output.

GET /api/v1/thumbnails/live/:id

Returns a preview image for one live input.

Method: GETPath: /api/v1/thumbnails/live/:id

The requested input can be the connected input with a frame from the last 2000 ms. The response is then that frame. The API encodes the frame at the requested size and keeps the aspect ratio. The longest edge is equal to size, and the shorter edge follows the aspect ratio of the input. In all other cases the response is a placeholder image with the input name and its provider type. A thumbnailUrl from GET /api/v1/browse therefore always answers with an image for a known input.

Parameters

ParameterInRequiredRangeDefault
idpathYesString of 1–256 characters, matched exactly first and then by flexible name matching. The first device in enumeration order wins a tie.
sizequeryNoInteger 32–512 pixels. The API clamps a value outside the span. A value that is not a whole number takes the default.192
formatqueryNopng or jpg. Any other value takes the default.png
If-None-MatchheaderNoAn ETag from an earlier response for the same preview.

Example success response

GET /api/v1/thumbnails/live/webcam_0?size=192&format=png200 OK

HTTP/1.1 200 OK
Content-Type: image/png
ETag: "liveInput-192x108-png-3f9a1c0d77b24e15"
Cache-Control: no-cache

<PNG bytes>

The body is the encoded image. Content-Type is image/png or image/jpeg, to match format. The ETag is "<kind>-<width>x<height>-<format>-<digest>". A leading p on the digest marks a placeholder render. A live frame gives a new ETag for each frame. A placeholder ETag is stable while the input stays disconnected or stale.

Conditional requests

Send the request again with If-None-Match set to the ETag that you hold. The response is 304 Not Modified with no body, before the API queues any render work:

HTTP/1.1 304 Not Modified
ETag: "liveInput-192x108-png-3f9a1c0d77b24e15"
Cache-Control: no-cache

Cache-Control is always no-cache. A client therefore revalidates with If-None-Match, and it does not serve a stale image.

Error codes

StatuscodeCondition
404DEVICE_NOT_FOUND:id is empty, longer than 256 characters, or matches no enumerated live input.
503PREVIEW_TIMEOUTThe queued frame readback did not complete within 500 ms. The frame data of the device does not change.
503THUMBNAIL_QUEUE_FULLThe render queue is full (depth more than 32). It rejects new work until the depth falls to 16. Send the request again after a short wait.