Skip to main content
Version: 1.3 (upcoming)

Browse & Inputs API Reference

These REST routes let a control surface walk the FXCanvas content as folders, activate any item, and preview a live input or the output. 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: GET — Path: /api/v1/browse

The hierarchy has up to five 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
  • /cues: the cues of the open show. First /cues/all (a Cruise item, then every cue, in cue-number order), then one folder for each cue page

The /cues folder​

A page folder lists three kinds of item, in this order: a pageGo item ("Go <page>"), a cruiseToggle item ("Cruise"), and then the cues of the page in page order. The /cues/all folder starts with a cruiseToggle item for All Cues ({"page": "all"}). It has no pageGo item, because All Cues is not a page. The page folder and its items carry the page color in a color field ("#RRGGBB"). A page with no cues has no folder. Hidden pages have a folder.

Item kindactivate sendsactiveState is true when
cuePOST /api/v1/cues/go {"id": "<cue id>"}the cue is live
pageGoPOST /api/v1/pages/go {"id": "<page id>"}the page is the active page
cruiseTogglePOST /api/v1/cruise/toggle {"page": "<page id>"}, or {"page": "all"} in /cues/allCruise Control runs on the page, or on All Cues

In the /cues folder, more than one item can be active at a time: the live cue, the Go item of the active page, and the Cruise item of the cruising page. GET /api/v1/cues/current reports all three (id, activePageId, cruisingPageId), so a client can light these keys from one request.

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. Outside /cues, 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. 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. A client that needs only part of a large folder can page it, see Paging and filtering.

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
offsetqueryNoPaging. Integer 0–1000000: the first matching item to return.0
limitqueryNoPaging. Integer 1–1000: the most nodes in the response, nested children included.100
searchqueryNoPaging. At most 256 characters. Only items whose name contains this text, without regard to case.none
kindqueryNoPaging. One of folder, sourceType, sourcePreset, liveInput, fileInput, cue, pageGo, cruiseToggle. Only items of this kind.none

Paging and filtering​

Send any of offset, limit, search and kind to page a folder. Without them, the response is the same as in FXCanvas 1.1.

  • search and kind filter the items of the folder you list. They do not filter nested children.
  • offset and limit then select a window of the items that match.
  • limit counts every node in the response. The items of the folder come first. At depth 2 and 3, nested children use what is left. A folder whose children were cut has "truncated": true. List its browsePath to see the rest.
  • The response has a page object: offset and limit as used, total (the number of items that match, before the window) and returned (the number of entries in items). count is the same as returned.
  • An offset past the end returns an empty items array, not an error.

GET /api/v1/browse?path=/sources&search=fire&limit=2 → 200 OK

{
"path": "/sources",
"parentPath": "/",
"count": 2,
"page": { "offset": 0, "limit": 2, "total": 3, "returned": 2 },
"items": [
{ "kind": "sourceType", "id": "sourceType:Bonfire", "name": "Bonfire", "activeState": false,
"thumbnailUrl": "/api/v1/thumbnails/source/type/Bonfire",
"activate": { "method": "POST", "path": "/api/v1/sources/select", "body": { "name": "Bonfire" } } },
{ "kind": "sourceType", "id": "sourceType:Fire", "name": "Fire", "activeState": true,
"thumbnailUrl": "/api/v1/thumbnails/source/type/Fire",
"activate": { "method": "POST", "path": "/api/v1/sources/select", "body": { "name": "Fire" } } }
]
}

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=/live → 200 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.
400INVALID_OFFSEToffset is not an integer from 0 to 1000000.
400INVALID_LIMITlimit is not an integer from 1 to 1000.
400INVALID_SEARCHsearch is longer than 256 characters.
400INVALID_KINDkind is not one of the node kinds.
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: POST — Path: /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: POST — Path: /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, or a cue, a shader source or a live input was selected while it loaded, which cancels the load. The source that was current before the request, or the one selected after it, drives the output.

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

Returns a preview image for one live input.

Method: GET — Path: /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=png → 200 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.

GET /api/v1/thumbnails/current​

Returns a preview image of what is on the output now.

Method: GET — Path: /api/v1/thumbnails/current

The parameters size and format work as for live previews. The ETag changes with every rendered frame.

Wait for the output to settle​

Right after a change of source or effects, for example a cue recall, the first frame of the new look can be incomplete, for example black. A cue recall also crossfades from the previous look, over 2 seconds by default. A palette change can also fade in over several seconds when palette transitions are on, and a change of source fades when the source switch fade is on. Add settle=true to wait until the output has shown the new look for a few frames and any cue crossfade, source switch fade or palette fade has ended, for at most 3 seconds. The wait does not slow down the output. The response then has an X-FXC-Output-Settling header:

ValueMeaning
falseThe image shows the settled output
trueThe output was still changing when the 3 seconds ran out, for example during a longer palette fade. The image shows the output at that moment. Send the request again to wait longer

When the value is true and a timed fade is running (a cue fade, a source switch fade or a palette fade), the response also has an X-FXC-Output-Transition-Remaining-Ms header. It gives the time in milliseconds that the longest fade still runs. Send the request again after that time to get the settled output. When the output has settled, this header is not sent.

Without settle, the response comes at once and has no X-FXC-Output-Settling header, as in FXCanvas 1.1.

GET /api/v1/thumbnails/current?size=256&format=jpg&settle=true

HTTP/1.1 200 OK
Content-Type: image/jpeg
X-FXC-Output-Settling: false