API reference

HTTP API

REST endpoints for activating content, controlling outputs, and firing actions from external systems.

REST endpoints for controlling Lightpath from external systems. All paths are relative to your install's base URL. Pick whichever of these matches how the caller reaches your install:

SetupBase URL
Caller runs on the same machine as Lightpathhttp://localhost:3001/api
Caller is another machine on the same LANhttp://<install-ip>:3001/api (e.g. http://192.168.1.50:3001/api)
Caller is remote / over the internethttps://<your-tunnel-hostname>/api (Cloudflare tunnel, no port)

The Cloudflare tunnel surface exposes the same API as LAN — every endpoint documented here is reachable through it. For remote callers, integration API keys (covered below) are the recommended credential.

# Pick one and reuse below
BASE_URL="http://localhost:3001/api"
# BASE_URL="http://192.168.1.50:3001/api"
# BASE_URL="https://your-tunnel-hostname/api"

Authentication

Every endpoint except /api/health and /api/login requires a bearer token. Send it as Authorization: Bearer <token> on every request.

Lightpath accepts two kinds of bearer tokens on the same endpoints:

  • Integration API keys — long-lived, scoped, revocable tokens you mint in the UI. The recommended path for machine-to-machine integrations (Crestron, lighting boards, show-control software).
  • User session JWTs — short-lived tokens you get from POST /api/login. Appropriate for tooling operated by a specific person, or for one-off scripts where the caller's identity matters.

The server auto-detects which kind you sent by the lpath_ prefix on integration keys.

Integration API keys

Go to Settings → API Keys (admin-only) and click + New Key.

  1. Give the key a descriptive name — the integration's name plus where it lives, e.g. Crestron — Main Hall. The name shows up in the list view and audit logs; you can't see the key value again after creation, so make the name useful.
  2. Tick the scopes the integration needs (table below). Grant the minimum that gets the job done.
  3. Optionally set an expiry date. Default is "never expires."
  4. Click Create key.

The full key value is shown once, with a copy button. Paste it directly into the integration's configuration — Lightpath only stores a hash and can't surface the plaintext again later.

KEY="lpath_live_xxxxxxxxxxxxxxxxxxxxxxxxx"

curl -X POST "$BASE_URL/scenes/Showtime/activate" \
  -H "Authorization: Bearer $KEY"

Keys begin with the lpath_ prefix. Treat them like passwords — never commit them to source control, paste them into chat, or email them in plaintext.

Keys work identically over LAN and through the Cloudflare tunnel — remote integrations are a primary use case.

Scopes

Each key declares the scopes it's allowed to use. A request to a route that requires a scope the key doesn't carry returns 403.

ScopeWhat it allows
content.activateFire looks, scenes, and playlists
action.executeRun actions, set toggle states
output.controlSet output brightness, enable/disable outputs
playback.controlPause / resume / next / prev / stop playlists
state.readRead content, outputs, and catalog (all GET endpoints)

A "fire one scene from a button" integration typically only needs content.activate. Keys deliberately can't reach admin endpoints (user management, output config, calendar editing) — if your integration needs those, it's an admin tool, not a machine integration, and should use a user login instead.

Listing and revoking

The same Settings → API Keys page lists every key with name, scopes, prefix, created date, last-used timestamp, and status.

  • Revoke stops the key from authenticating, immediately. Future requests with it return 401 Invalid or expired integration key. The record is kept so audit logs can still resolve "who was that key?".
  • Delete removes the record entirely. Prefer Revoke unless the key was created in error.

User logins

Use POST /api/login to exchange credentials for a JWT. Appropriate for admin dashboards, internal scripts, or anything that should attribute its actions to a named user.

TOKEN=$(curl -s -X POST "$BASE_URL/login" \
  -H "Content-Type: application/json" \
  -d '{ "username": "you@example.com", "password": "..." }' \
  | jq -r .token)

curl "$BASE_URL/looks" -H "Authorization: Bearer $TOKEN"

POST /api/login accepts either email or username plus password. Tokens expire after 30 days by default — refresh by re-logging in on 401.

Catalog

Read what's available in the loaded project. Authenticated.

MethodPathDescription
GET/sourcesAvailable sources published by TouchDesigner
GET/effectsAvailable effects published by TouchDesigner

Content queries

Read content definitions. All require Authorization: Bearer <token>.

MethodPathDescription
GET/looksAll looks
GET/looks/:nameA specific look
GET/scenesAll scenes
GET/scenes/:nameA specific scene
GET/playlistsAll playlists (both scene-rotation and per-output look-rotation)
GET/playlists/:nameA specific playlist
GET/palettesAll palettes
List looks
curl "$BASE_URL/looks" -H "Authorization: Bearer $TOKEN"

Content activation

Activate looks, scenes, and playlists. Authenticated.

MethodPathDescription
POST/looks/:name/activateActivate a look by name
POST/scenes/:name/activateActivate a scene across all outputs
POST/playlists/:name/activateStart a playlist (per-output or global, auto-detected)
Activate a look
curl -X POST "$BASE_URL/looks/Sunset/activate" \
  -H "Authorization: Bearer $TOKEN"

Each look is bound to a single output at design time — the look activates on that output automatically; no body is required.

Playlist control

Add ?output=Name for per-output, omit for global. Authenticated.

MethodPathDescription
POST/playlists/pausePause playlist
POST/playlists/resumeResume playlist
POST/playlists/stopStop playlist
POST/playlists/nextAdvance to next track (requires ?output=Name)
POST/playlists/prevGo to previous track (requires ?output=Name)
POST/playlists/trackJump to a specific track
POST/playlists/loopToggle loop mode
Jump to track 2 on Output 1
curl -X POST "$BASE_URL/playlists/track" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "index": 2, "output": "Output 1" }'

Actions

Execute actions and set toggle states. Authenticated.

MethodPathDescription
GET/actionsGet all actions
POST/actions/:name/executeExecute an action by name
POST/actions/:name/stateSet a toggle action to a specific state
Fire an action
curl -X POST "$BASE_URL/actions/All%20Lights%20On/execute" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "targetState": true }'

Output control

Brightness and power state per output. Authenticated.

MethodPathDescription
GET/outputsGet all outputs
POST/outputs/:name/dimmerSet output brightness (0.0 – 1.0)
POST/outputs/:name/enabledEnable or disable an output
Set output brightness
curl -X POST "$BASE_URL/outputs/Facade/dimmer" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "value": 0.75 }'

Playback

Global playback control and status. Authenticated.

MethodPathDescription
POST/playback/stopStop all playback across all outputs
GET/playback/statusGet playback status for all playlists

Health

The only public endpoint besides /api/login. Useful for liveness probes.

MethodPathDescription
GET/healthStatus, uptime, version

Errors

The API uses standard HTTP status codes.

StatusMeaning
200Success
400Bad request — missing/invalid body or query parameters
401Missing or expired bearer token
403Authenticated but not authorized (e.g., wrong org for the loaded project, or local login attempted over a tunnel)
404Resource not found (e.g., /looks/Unknown)
500Server error

Error responses are JSON:

{ "error": "Invalid or expired token" }

On this page