Effects & Palettes API Reference
This page gives the REST routes for effects and palette colors. A control surface uses them to load and unload effects, and to read and switch the effects a show already has. It also uses them to read the colors of a palette, and it does not request an image. These routes work with the Browse & Inputs API behind the Stream Deck Plugin. Like those routes, they are not specific to one control surface, and any HTTP client can use them.
For the rest of the API, see the interactive REST API Reference. For setup and authentication, see the REST API guide.
Conventions
| Topic | Detail |
|---|---|
| Base URL | http://<host>:<port>/api/v1 — default http://127.0.0.1:8080/api/v1. |
| License Pro | The 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. |
| Authentication | Optional. If you set an API key, send X-API-Key: <key> or Authorization: Bearer <key> on every request. |
| Name matching | The match on a name is case-insensitive. A space, an underscore, and a hyphen are equivalent, and FXCanvas ignores whitespace at the start and the end. |
| Error body | { "error": true, "code": "<CODE>", "message": "<text>" }, with an extra details object on ITEM_NOT_FOUND. |
Two lists, two identities
This page has two different lists of effects. The most frequent mistake is to select the wrong list.
| List | Route | Identified by | Use it when |
|---|---|---|---|
| The catalog | GET /api/v1/effects/available | Effect type | You build a fixed set of controls, with one key for each effect a user can select. |
| The chain | GET /api/v1/effects | 1-based chain position | You mirror the Effects panel in its current state. |
Almost every control surface must use the catalog. A key bound to blur still means Blur after you change the chain order. The same key also means Blur on a machine with no chain, and this is what makes a layout you can ship. Add an effect with POST /api/v1/effects/add, and remove it with POST /api/v1/effects/remove. One press loads the effect, and the next press unloads it.
The chain is the right list for one specific slot. Two copies of the same effect at different chain positions are legal, and only the position identifies each one. toggle and enabled work on positions. They change visibility, and they do not add or remove an effect.
Three more facts are important before you build a client:
There is no single "current" effect. FXCanvas can load and enable any number of effects at once. Therefore /effects/current returns a list, and a row of keys can show several effects as active. Sources and palettes are different, because only one of them is current.
There are no effect thumbnails. An effect transforms the output of the effect before it, and it is not an image in itself. A preview needs an input image, and FXCanvas cannot invent one. Show the name of the effect and its loaded or enabled state instead.
Effect presets are separate. To load a saved effect preset, use GET /api/v1/actions and POST /api/v1/actions/{id} in the Effect Presets category. These routes do not load presets.
GET /api/v1/effects/available
This route returns every effect type this build can load. Each item also states whether the chain holds an effect of that type.
Method: GET — Path: /api/v1/effects/available
This route takes no parameters. GET /api/v1/capabilities reports it as the effects-catalogue feature, so a client can identify an older FXCanvas build that does not serve this route.
Example success response
GET /api/v1/effects/available → 200 OK
{
"count": 4,
"inChainCount": 1,
"items": [
{ "type": "blur", "name": "blur", "inChain": true },
{ "type": "fisheye", "name": "fisheye", "inChain": false },
{ "type": "kaleidoscope", "name": "kaleidoscope", "inChain": false },
{ "type": "strobe", "name": "strobe", "inChain": false }
]
}
inChain is true when the chain holds at least one effect of that type. The enabled state does not change it. A key that shows inChain means "this effect is in my chain", and add and remove switch that state.
Error codes
| Status | code | Condition |
|---|---|---|
| 503 | CHAIN_BUSY | FXCanvas cannot read the chain within 1000 ms. Send the request again. |
POST /api/v1/effects/add
This route loads an effect type into the chain.
Method: POST — Path: /api/v1/effects/add
FXCanvas adds the effect to the end of the chain, and the effect starts enabled. The Effects panel does the same.
Parameters
| Field | Type | Required | Meaning |
|---|---|---|---|
type | string | Yes | Effect type from /effects/available. FXCanvas matches the name as described in Conventions. |
idempotent | boolean | No, defaults to false | When true, FXCanvas returns a type already in the chain unchanged, and adds no second copy. |
Set idempotent for a client that can repeat a request. Examples are a repeated press, or two presses at the same time. The result is then "this effect is loaded", and not a second copy in the chain.
Example success response
POST /api/v1/effects/add with { "type": "kaleidoscope", "idempotent": true } → 200 OK
{
"success": true,
"index": 2,
"name": "Kaleidoscope",
"type": "kaleidoscope",
"enabled": true,
"presetId": ""
}
The response describes the chain slot of the new effect. A caller that also shows the chain does not need to read the chain again.
Error codes
| Status | code | Condition |
|---|---|---|
| 400 | INVALID_REQUEST | The body is not JSON, or carries no type. |
| 403 | EFFECT_LIMIT_REACHED | The chain is at the effect ceiling for this license tier. FXCanvas added nothing. |
| 404 | ITEM_NOT_FOUND | No effect type of that name is installed. details.available lists the installed types. |
| 500 | EFFECT_LOAD_FAILED | The type is installed, but FXCanvas cannot create the effect. |
| 503 | CHAIN_BUSY | FXCanvas cannot update the chain within 1000 ms. Nothing changed. Send the request again. |
POST /api/v1/effects/remove
This route unloads an effect from the chain.
Method: POST — Path: /api/v1/effects/remove
Parameters
Send exactly one of these in a JSON body:
| Field | Type | Meaning |
|---|---|---|
type | string | Effect type. FXCanvas removes the first effect of that type. A chain with two of them keeps the second. |
index | integer | Chain position, 1-based. Use this field for one specific slot in the chain. |
If you send both fields, type wins.
Example success response
POST /api/v1/effects/remove with { "type": "kaleidoscope" } → 200 OK
{
"success": true,
"removed": {
"index": 2,
"name": "Kaleidoscope",
"type": "kaleidoscope",
"enabled": true,
"presetId": ""
}
}
removed describes the effect before FXCanvas removed it. Every position after it moves down by one, as in the Effects panel.
Error codes
| Status | code | Condition |
|---|---|---|
| 400 | INVALID_REQUEST | The body is not JSON, or carries neither type nor index. |
| 404 | ITEM_NOT_FOUND | The chain holds no effect of that type, or nothing is at that position. |
| 503 | CHAIN_BUSY | FXCanvas cannot update the chain within 1000 ms. Nothing changed. Send the request again. |
GET /api/v1/effects
This route returns every effect in the chain, in chain order.
Method: GET — Path: /api/v1/effects
This route takes no parameters. One request gives everything for a whole row of keys: the label from name, and the active indicator from enabled. A client does not need one request for each effect.
Example success response
GET /api/v1/effects → 200 OK
{
"count": 3,
"enabledCount": 2,
"items": [
{
"index": 1,
"name": "Blur",
"type": "blur",
"enabled": true,
"presetId": ""
},
{
"index": 2,
"name": "Fisheye",
"type": "fisheye",
"enabled": false,
"presetId": ""
},
{
"index": 3,
"name": "Warm Trails",
"type": "trail",
"enabled": true,
"presetId": "effect-preset-7"
}
]
}
name is the display name of the effect, and type is its lowercase type identifier. A response gives "name": "Blur" with "type": "blur". Both values work where a route asks for a name.
items is always an array. A show with no effects returns [], and count and enabledCount are both 0. presetId has a value only for an effect that comes from a saved preset.
Error codes
| Status | code | Condition |
|---|---|---|
| 503 | CHAIN_BUSY | FXCanvas cannot read the chain within 1000 ms. FXCanvas reads and edits the chain on the render thread, so a read cannot race an edit in the Effects panel. A heavy frame can delay a read. Send the request again. |
GET /api/v1/effects/current
This route returns the effects in the chain that are enabled.
Method: GET — Path: /api/v1/effects/current
This route takes no parameters. The response has the same shape as GET /api/v1/effects, with the enabled effects only. Each item keeps its position in the full chain, so the index values can have gaps.
Example success response
GET /api/v1/effects/current → 200 OK
{
"count": 2,
"items": [
{ "index": 1, "name": "Blur", "type": "blur", "enabled": true, "presetId": "" },
{ "index": 3, "name": "Warm Trails", "type": "trail", "enabled": true, "presetId": "effect-preset-7" }
]
}
Error codes
| Status | code | Condition |
|---|---|---|
| 503 | CHAIN_BUSY | FXCanvas cannot read the chain within 1000 ms. Send the request again. |
POST /api/v1/effects/toggle
This route inverts the visibility of one loaded effect.
Method: POST — Path: /api/v1/effects/toggle
FXCanvas adds no effect and removes no effect. Only the enabled state of the effect changes.
Parameters
Send exactly one of these in a JSON body:
| Field | Type | Range | Meaning |
|---|---|---|---|
index | integer | 1 to the number of loaded effects | Chain position. |
name | string | — | FXCanvas matches this value against the display name of the effect first, then against the effect type. Both "Blur" (the display name) and "blur" (the type) work. |
If you send both fields, name wins.
Example success response
POST /api/v1/effects/toggle with { "index": 2 } → 200 OK
{
"success": true,
"current": {
"index": 2,
"name": "Fisheye",
"type": "fisheye",
"enabled": true,
"presetId": ""
}
}
current reports the state after the change. A caller does not need a second request to see the result.
Error codes
| Status | code | Condition |
|---|---|---|
| 400 | INVALID_REQUEST | The body is not JSON, or carries neither index nor name. |
| 404 | ITEM_NOT_FOUND | No effect is at that position, or no loaded effect has that name. For a name miss, details.available lists the loaded effect names. For an index miss, details.min and details.max give the range. |
| 503 | CHAIN_BUSY | FXCanvas cannot update the chain within 1000 ms. Nothing changed. Send the request again. |
POST /api/v1/effects/enabled
This route sets the visibility of one loaded effect.
Method: POST — Path: /api/v1/effects/enabled
This route is idempotent. It is the right route for a control surface that syncs its keys again at intervals. The route sets the state you send, so it cannot invert an effect by accident. A repeated toggle can.
Parameters
| Field | Type | Required | Meaning |
|---|---|---|---|
enabled | boolean | Yes | The state to set. |
index | integer | One of the two | Chain position, 1-based. |
name | string | One of the two | Display name or effect type. FXCanvas matches it in the same way. |
Example success response
POST /api/v1/effects/enabled with { "name": "Blur", "enabled": false } → 200 OK
{
"success": true,
"current": {
"index": 1,
"name": "Blur",
"type": "blur",
"enabled": false,
"presetId": ""
}
}
Error codes
| Status | code | Condition |
|---|---|---|
| 400 | INVALID_REQUEST | The body is not JSON, has no boolean enabled field, or carries neither index nor name. |
| 404 | ITEM_NOT_FOUND | No effect is at that position, or no loaded effect has that name. |
| 503 | CHAIN_BUSY | FXCanvas cannot update the chain within 1000 ms. Nothing changed. Send the request again. |
Palette colors
GET /api/v1/palettes and GET /api/v1/palettes/current both report the colors of a palette. A control surface can then draw its own swatches, and it does not request an image.
Method: GET — Path: /api/v1/palettes/current
Example success response
GET /api/v1/palettes/current → 200 OK
{
"index": 3,
"id": "palette-ocean",
"name": "Ocean Blues",
"colors": ["#0B2C4A", "#1B6AC9", "#3FA9F5", "#A9DCF7"]
}
colors is in palette order, as #RRGGBB strings. The response gives no alpha value, because palette swatches are opaque.
Error codes
| Status | code | Condition |
|---|---|---|
| 404 | NO_CURRENT_PALETTE | No palette is selected. |
When to use the rendered preview instead
GET /api/v1/thumbnails/palette/{id} returns a rendered image of the same palette. The size is 32–512 pixels, as png or jpg. The ETag follows the content version of the palette, so an edit invalidates every cached copy. Use colors for your own key image, and the thumbnail for the image that FXCanvas renders. For the parameters, see the thumbnail routes.
Related Topics
- Browse & Inputs API Reference — How to walk content as folders, and how to activate an item
- REST API — How to enable the server, authentication, and name matching
- Stream Deck Plugin — The control surface that these routes serve
- Effects — What the effects chain is, and how the Effects panel works
- Palettes — How to create and edit palettes