Document Version: 2.0
Verified On: 2026-02-06
Target LedFX Version: 2.1.4+
Base URL: http://<host>:<port>/api
This spec was validated against:
- Official docs (latest): https://docs.ledfx.app/en/latest/apis/api.html
- Scenes docs (latest): https://docs.ledfx.app/en/latest/apis/scenes.html
- Playlists docs (latest): https://docs.ledfx.app/en/latest/apis/playlists.html
- Latest release: https://github.com/LedFx/LedFx/releases/tag/v2.1.4
- Upstream source (HEAD at verification time): https://github.com/LedFx/LedFx/tree/0ef5218b3d6df6db22b85513228a55591f9580a2
The old file mixed historical behavior and outdated assumptions. This version is intentionally concise and focuses on the model details that matter when building MCP tooling:
- Presets are effect-scoped in storage, but virtual-scoped for most operations.
- Colors/palettes are a color-or-gradient store, not a first-class palette entity.
- Blender is a normal effect whose config references other virtual IDs.
- Scenes and playlists orchestrate virtual/effect state indirectly, with action semantics.
Deviceis physical output integration (WLED/OpenRGB/etc).Virtualis the control surface where effects run.Effectis attached to a virtual (/virtuals/{id}/effects).Presetlibrary is keyed by effect type (ledfx_presetsanduser_presets), but the easiest create/apply flow is virtual-based.Scenestores per-virtual actions/config snapshots.Playliststores orderedscene_iditems only.Colors/Gradientsare global stores (/api/colors); "palette" is convention, not native resource type.Blenderis an effect that composes output from three source virtuals (mask,foreground,background).
GET /api/infoGET /api/schema(optionally with request body to request specific schema groups)
GET /api/info commonly includes extra metadata fields beyond url, name, and version (for example github_sha, is_release, developer_mode).
GET /api/devicesGET /api/devices/{device_id}GET /api/virtualsPUT /api/virtuals(global pause toggle)POST /api/virtuals(create or update virtual config)GET /api/virtuals/{virtual_id}PUT /api/virtuals/{virtual_id}(setactive)POST /api/virtuals/{virtual_id}(updatesegments)DELETE /api/virtuals/{virtual_id}
GET /api/effects(active effects by virtual)PUT /api/effectswith action:clear_all_effectsapply_globalapply_global_effect
GET /api/effects/{effect_id}(effect schema string)GET /api/virtuals/{virtual_id}/effectsPOST /api/virtuals/{virtual_id}/effectsPUT /api/virtuals/{virtual_id}/effectsDELETE /api/virtuals/{virtual_id}/effects
GET /api/effects/{effect_id}/presetsPUT /api/effects/{effect_id}/presets(rename)DELETE /api/effects/{effect_id}/presets(delete)GET /api/virtuals/{virtual_id}/presetsPUT /api/virtuals/{virtual_id}/presets(apply)POST /api/virtuals/{virtual_id}/presets(save active effect as user preset)DELETE /api/virtuals/{virtual_id}/presets(currently non-functional as a true preset delete; see quirks)
GET /api/scenesPUT /api/scenesactions:activateactivate_in(requiresms)deactivaterename(requiresname)
POST /api/scenes(create or update; update ifidprovided)DELETE /api/scenes(body includesid)GET /api/scenes/{scene_id}DELETE /api/scenes/{scene_id}
GET /api/playlistsPOST /api/playlists(create or replace)PUT /api/playlistsactions:start(requiresid, optional runtimemodeandtimingoverride)stoppauseresumenextprevstate
DELETE /api/playlists(body includesid)GET /api/playlists/{id}DELETE /api/playlists/{id}
GET /api/colorsPOST /api/colors(upsert map of IDs to values)DELETE /api/colors(body: list of IDs)DELETE /api/colors/{color_id}
- Presets are stored by effect type in two categories:
ledfx_presetsuser_presets
GET /api/virtuals/{virtual_id}/presets:
- Requires virtual to exist.
- Requires virtual to have an active effect.
- Returns presets only for that active effect type.
- Adds an
activeboolean per preset by comparing preset config to active effect config.
PUT /api/virtuals/{virtual_id}/presets requires:
{
"category": "ledfx_presets" | "user_presets",
"effect_id": "<effect_type>",
"preset_id": "<preset_id>"
}Notes:
category=ledfx_presetswithpreset_id=resetapplies generated defaults foreffect_id.- This call creates/sets an effect instance on the virtual.
POST /api/virtuals/{virtual_id}/presets:
- Saves the virtual's current active effect config into
user_presets[effect_id]. - Requires body
{ "name": "..." }. - Generated preset key is ID-normalized from name.
DELETE /api/virtuals/{virtual_id}/presets is marked TODO upstream and currently clears effect state rather than doing reliable preset deletion. For true preset deletion, use:
DELETE /api/effects/{effect_id}/presetswith{ "category", "preset_id" }
/api/colors is a key-value store with two domains:
colors.{builtin,user}gradients.{builtin,user}
POST /api/colors takes an object like:
{
"my_color": "#ff00ff",
"my_gradient": "linear-gradient(90deg, #ff0000 0%, #0000ff 100%)"
}Behavior:
- If value validates as a color, key goes to user colors.
- Otherwise it is treated as gradient string and stored in user gradients.
Implication for MCP:
- A "palette" should be documented as naming convention over user gradients.
- Reusing one palette across many presets means copying the same gradient value into each effect config/preset; there is no separate palette reference object.
Scene virtual entries can use explicit action semantics:
ignore: leave virtual unchanged.stop: clear effect.forceblack: applysingleColorwith black.activate: apply effect.
For activate:
typeis required.- Either
configorpresetis used. - If
presetis set, scene activation resolves preset by effect type at runtime.
Important scene POST rules:
- With
id: updates existing scene. - Without
id: creates a new scene; dedupes ID from name. - If
virtualsomitted on create: captures a snapshot of current virtual effects.
Playlist items schema is:
{
"scene_id": "<scene_id>",
"duration_ms": 15000
}Key semantics:
- Items list only references scenes.
- Empty
itemsis valid and means "dynamic all scenes" at start time. modeissequenceorshuffle.- Runtime control is via
PUT /api/playlistsaction API, includingstate.
Blender is configured like any other effect on a target virtual, but its config references other virtual IDs:
{
"type": "blender",
"config": {
"mask": "virtual-mask",
"foreground": "virtual-fore",
"background": "virtual-back",
"mask_stretch": "2d full",
"foreground_stretch": "2d full",
"background_stretch": "2d full",
"invert_mask": false,
"mask_cutoff": 1.0
}
}Runtime behavior:
- Blender reads frames/matrices from those source virtuals every render cycle.
- If sources are missing/not ready, render can no-op for that frame.
- Stretching is applied when source dimensions differ.
Operational implication:
- Treat blender as cross-virtual dependency graph.
- Scene/playlist definitions must preserve source virtual lifecycle and effects, not only the blender target virtual.
LedFX API is not fully uniform. Clients should handle mixed response envelopes:
- Many endpoints:
{ "status": "success", ... } GET /api/virtuals/{id}returns{ "status": "success", "<id>": { ... } }- Some PUT/DELETE endpoints return message-style payloads rather than full objects.
GET /api/schemabody-filtering is defined in source, but some running instances effectively return full schema even when a body is sent.
- Effects: always treat virtual ID as required control key.
- Presets: validate virtual has active effect before preset operations.
- Palette UX: represent palettes as named user gradients.
- Playlists: build through scene IDs; do not model playlist items as effect presets.
- Blender workflows: provision source virtuals first, then blender target, then scene/playlist orchestration.
- Verify
/api/info.versionand log it. - Pull
/api/schemaat startup to discover effect schemas dynamically. - When applying preset:
- check virtual exists
- check active effect or explicit
effect_id - prefer
/api/effects/{effect_id}/presetsfor catalog inspection
- When creating playlist:
- validate all
scene_idvalues exist - allow empty
itemsonly if dynamic-all-scenes behavior is desired
- validate all
- For blender:
- validate source virtual IDs exist before applying effect
This document is intentionally scoped to the currently released 2.1.4 behavior (plus source at commit 0ef5218). If you target older builds, re-check:
virtual_presetsdelete behavior- scene action/preset inference fields
- playlists action set and state payload