Appearance
Client API
The Lumora Controller desktop app runs a lightweight HTTP server on your local machine that local tools (Stream Deck plugin, Companion, scripts) can call to trigger hotkey-equivalent actions and query app state. No API key required.
Server API
Looking for the cloud-side API for triggering specific graphics by ID from anywhere on the internet? See the Server API.
When to use which
| Use this | If you want to… |
|---|---|
| Server API | Trigger a specific graphic by ID from anywhere on the internet, regardless of what the operator has selected. Authenticated, internet-exposed. |
| Client API | Drive whatever the operator currently has selected. Same outcome as pressing a hotkey. Local-only, no auth. |
The Client API is the right fit for hardware controllers (Stream Deck, foot pedals, deck builders) that sit on the same machine as the controller and act as remote hotkeys. Pressing a Stream Deck button feels exactly like pressing Numpad +.
Network access
The controller binds the API server to 127.0.0.1 (loopback) by default, so only processes running on the same machine can reach it:
text
http://127.0.0.1:<port>- No authentication by default. Matches Companion / vMix / OBS-WebSocket norms for broadcast-tool local APIs. See Enhanced security below for the opt-in token gate.
- HTTP, not HTTPS. Loopback-only, plain HTTP.
- Loopback by default. Stream Deck and Companion run locally and connect via
127.0.0.1. Cross-device access (e.g. a Stream Deck on a separate box) is not supported in the current release.
Security model
By default the Client API is unauthenticated. The loopback bind ensures only local processes can reach it. Do not expose the port to the network or the internet. If you also run a regular browser on the controller machine, enable Enhanced security so a website you visit can't fire CORS-simple POSTs at the loopback API.
Port configuration
The default port is 3031. To change it, open the controller and go to Settings → Client Configuration → Client API.
- Port range: 1024–65535
- Default: 3031
- Changing the port restarts the local API server immediately. Any in-flight Stream Deck / Companion bindings need updating to the new port.
Enhanced security
A vMix-style opt-in token gate. Off by default. When enabled, every request must present a shared token, and the permissive CORS header is dropped so a website opened in the operator's browser can no longer fire requests at 127.0.0.1.
Enabling
Settings → Client Configuration → Client API → tick Enhanced security. The controller mints a 256-bit token (43-char base64url) once per install and shows it in the same panel — Reveal, Copy, or Regenerate from there. Paste the token into your Stream Deck plugin / Companion module config.
Presenting the token
Either form is accepted, header takes precedence:
text
Authorization: Bearer <token>text
http://127.0.0.1:<port>/v1/take/on?key=<token>Requests without a valid token return 401 Unauthorized.
Regenerating
Click Regenerate in the Client Configuration panel. The previous token is invalidated immediately — update any Stream Deck / Companion bindings before regenerating, or you'll lock them out until they're re-pasted.
API versioning
The Client API uses path versioning. All canonical endpoints are prefixed with /v1/:
text
http://127.0.0.1:<port>/v1/<endpoint>Un-versioned paths (e.g. /take/on) continue to work and return the same response, but include Deprecation: true and Link: </v1/...>; rel="successor-version" headers per RFC 9745. Update your integrations to the /v1/ paths at your convenience — no removal date is set.
Endpoints
All endpoints are rooted at http://127.0.0.1:<port>/v1. None of them take path or query parameters. They all act on the operator's currently-selected graphic and active channel.
GET Requests
| Endpoint | Description |
|---|---|
/v1/graphic/name | Name of the currently selected graphic as a JSON string, or null if nothing is selected |
/v1/channel/number | Active output channel number (1-based integer) |
/v1/channel/name | Active output channel name as a JSON string |
POST Requests
| Endpoint | Description | Hotkey equivalent |
|---|---|---|
/v1/take/on | Take the selected graphic on — fires on every channel in its lock set | Numpad Enter |
/v1/take/off | Take the selected graphic off — clears it from every channel it's live on (no advance) | Numpad − (without advance) |
/v1/take/on/next | Take on, then advance selection to the next graphic | Numpad + |
/v1/take/off/next | Take off, then advance selection to the next graphic | Numpad − |
/v1/clear | Clear every graphic from the active output channel | (none) |
/v1/panic | Instantly clear all graphics from all channels | Esc Esc |
/v1/refresh-preview | Reload the graphic preview iframe | F8 |
See Hotkeys for the full keyboard reference.
Responses
POST requests
json
{ "ok": true }GET requests
GET endpoints return raw JSON values, not wrapped objects:
json
"Lower Third"json
3json
nullThe null value is returned when nothing is selected (/v1/graphic/name) or when no channel is active.
Status codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 404 | Endpoint not found (typo in path) |
| 500 | Controller error. Check the controller log |
There's no 401 because there's no authentication, and no 409 because POST actions are always accepted and behave as no-ops if there's nothing to act on (e.g. /v1/take/on with no graphic selected).
Examples
Read the active channel name
bash
curl http://127.0.0.1:3031/v1/channel/nameTake the selected graphic on-air
bash
curl -X POST http://127.0.0.1:3031/v1/take/onTake on and advance (Numpad + equivalent)
bash
curl -X POST http://127.0.0.1:3031/v1/take/on/nextPanic, clear everything
bash
curl -X POST http://127.0.0.1:3031/v1/panicReload the preview
bash
curl -X POST http://127.0.0.1:3031/v1/refresh-previewCommon integrations
The Client API has no Lumora-specific plugins. All of these drive the endpoints above with each tool's generic HTTP action.
- Stream Deck. Elgato's built-in HTTP Request action fires a request per button press. The Client API is particularly well-suited here: one button per hotkey-equivalent action, with no need to track graphic IDs as the show evolves.
- Bitfocus Companion. Point Companion at
http://127.0.0.1:<port>and use generic HTTP actions for each button. - Foot pedals / hardware controllers. Anything that can fire an HTTP request on press works. The "no auth" model is a feature here: hardware integrations can be set up in seconds.
- Custom scripts. Useful for show-day automation that needs to read state (
GET /v1/graphic/name) and react.
Troubleshooting
Connection refused. The controller isn't running, or it's running but the API server is bound to a different port. Open the controller and check Settings → Client Configuration → Client API for the actual port.
401 Unauthorized on every request. Enhanced security is enabled and your request isn't presenting the token. Either include Authorization: Bearer <token> or append ?key=<token> to the URL. The token is shown in Settings → Client Configuration → Client API.
Returns 404 on a known endpoint. Check for trailing slashes — POST /v1/take/on/ with a trailing slash may differ from POST /v1/take/on. Also verify you're using the /v1/ prefix.
Port in use. Another application on the controller machine is already bound to the same port (common conflicts: 3000, 8080, 8000). Pick a port in the range 1024–65535 that nothing else uses. The controller will refuse to start with a clear error if the port is taken.