Runtime API
The Runtime API is the HTTP control surface for the VindralComposerRuntime process — the headless playback engine. It's a separate API from the project-level HTTP API: the project-level API talks to whatever project is currently loaded, while the Runtime API manages which project is loaded and the runtime process itself, and stays available even when no project is open.
What the API can do
- Manage project lifecycle — load a
.prj(or encrypted.prjp) onto the runtime, start its playback, stop it, reload it from disk, and unload it. Use this to swap between projects (MorningShow.prj→EveningShow.prjfrom a scheduler) or reload after a project edit on disk without restarting the process. - Drive runtime lifecycle — start / stop / reload as discrete actions; gracefully shut down the runtime from a service supervisor.
- Query state — current runtime state (
Idle/Loading/Running/Stopping), loaded project, uptime, capabilities, and errors / warnings. Useful for deployment dashboards and pre-flight checks before sending project-level API calls. - Validate projects ahead of time — pre-flight a
.prj's schema, asset paths, and licence requirements in a sandbox without affecting the live runtime. - Stream runtime events — subscribe to a WebSocket for real-time lifecycle events (project loaded, state transitions, runtime exiting) instead of polling.
- Prometheus metrics — scrape a Prometheus-format metrics endpoint for observability stacks (Grafana / Prometheus / Composer Monitor).
The Runtime API is consistent with the project-level HTTP API on auth (same apikey mechanism, same apikeys.json), CORS, and content-type — but listens on its own dedicated port so it can stay reachable when the project-level API is not.
For the headless playback engine itself — install, command-line arguments, container deployment — see the Composer Runtime manual. For driving a running project, see the HTTP API.
General Information
- Base URL:
http://{hostname}:{port}/runtime/ - Method: Endpoints accept
GETand/orPOSTas documented below - Authentication: API key via
apikeyheader or?apikey=query parameter. Keys are validated againstapikeys.json(same mechanism as the project-level HTTP API). If no keys are defined inapikeys.json, all requests are allowed. - CORS: All responses include
Access-Control-Allow-Origin: *and supportOPTIONSpreflight - Content-Type: All responses are
application/json; charset=utf-8 - All endpoint paths are case-insensitive.
- Availability: The Runtime API remains available regardless of project state, including when idle. It is unaffected by the
DisableWebApiWhenNotRunningsetting. - Runtime only: This API is only available on VindralComposerRuntime (headless CLI), not VindralComposerDesktop.
Note — not available on Core licences
The headless Runtime executable refuses to start under a Vindral Composer Core (free) licence (it logs
Core license is not valid for the headless Runtime.and exits with a non-zero code), so the Runtime API is unreachable on Core regardless of--runtime-api-port/COMPOSER_RUNTIME_API_PORT. Upgrade to a Subscription or full licence to use the Runtime. See Activating a license for licence-tier details.
Authentication
The Runtime API uses the same API key mechanism as the project-level HTTP API. See the Authentication section for the full reference.
In summary:
- Provide a key via the
apikeyHTTP header or?apikey=query parameter - Keys are validated against
apikeys.jsonin the application directory - If no keys are defined (empty
Keysarray or missing file), the API is open to all callers - An invalid key returns
401 Unauthorized:
{
"error": "Invalid API key"
}
Port Configuration
The Runtime API runs on a dedicated port, separate from the project-level HTTP API. The port is resolved in this priority order:
| Source | Example |
|---|---|
CLI argument --runtime-api-port |
--runtime-api-port 9101 |
Environment variable COMPOSER_RUNTIME_API_PORT |
COMPOSER_RUNTIME_API_PORT=9101 |
If neither is configured, the Runtime API is disabled and will not start.
Host binding: Controlled by RuntimeApiHostName in settings. Defaults to + (all interfaces). Set to localhost for local-only access.
Runtime States
The runtime maintains its own state machine, independent of the project's PlaybackState:
| State | Description |
|---|---|
Idle |
Process alive, all infrastructure services active. No project loaded. |
Loading |
A project file is being loaded and initialized. Transitional. |
Running |
A project is loaded and the video worker is processing frames. |
Stopping |
A project is being stopped (targets, inputs, worker shutting down). Transitional. |
+---------+
load/start | | stop / error
+----------> | Running | ----------+
| | | |
| +---------+ |
| |
+---------+ +----------+
| Loading | | Stopping |
+---------+ +----------+
^ |
| +---------+ |
+----------- | Idle | <---------+
| |
+---------+
Threading Model
- HTTP listener callbacks run on thread pool threads.
- Lifecycle operations (stop, start, reload, load) execute on the main runtime loop thread via a pending action queue.
- The HTTP handler submits a
RuntimeApiActionand awaits the result with a 30-second timeout. /runtime/statusresponds immediately (read-only, no action queue)./runtime/exitresponds immediately, then triggers shutdown.
Error Handling
All error responses follow a consistent JSON format:
{
"error": "Human-readable error description",
"currentState": "Running"
}
Common HTTP status codes across lifecycle endpoints:
| Code | Meaning |
|---|---|
200 |
Action completed successfully |
400 |
Bad request (missing/invalid parameters, disallowed file extension) |
404 |
Endpoint or resource not found (e.g. project file does not exist) |
409 |
Precondition failure (wrong state for the requested action, target already exists, or another action in progress) |
413 |
Request body exceeds the 4 GB upload limit |
500 |
Unexpected exception raised while executing the action |
504 |
Action timed out (exceeded 30-second deadline) |
CORS Preflight
OPTIONS (any endpoint)
All endpoints support CORS preflight requests.
Response: 204 No Content
Headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type
Unknown Endpoints
Requests to unknown paths under /runtime/ return a helpful 404 with the list of available endpoints.
Response: 404 Not Found
{
"error": "Not found",
"available_endpoints": [
"GET /runtime/status",
"GET|POST /runtime/stop",
"GET|POST /runtime/start",
"GET|POST /runtime/reload",
"GET|POST /runtime/load?fileName=project.prj",
"GET|POST /runtime/exit",
"GET|POST /runtime/project/new",
"GET|POST /runtime/project/saveas?fileName=...&switchTo=false",
"GET /runtime/project/download",
"POST /runtime/project/upload?fileName=...",
"GET|POST /runtime/project/validate?fileName=...",
"GET|POST /runtime/project/assets?fileName=...",
"GET /runtime/projects/list",
"GET|DELETE /runtime/projects/delete?name=...",
"GET|POST /runtime/projects/rename?name=...&newName=...",
"GET|POST /runtime/projects/clone?name=...&newName=..."
]
}
Concurrent Action Rejection
Only one lifecycle action can execute at a time. If a second action is submitted while another is in progress, it is rejected immediately.
Response: 409 Conflict
{
"error": "Another lifecycle action is already in progress. Try again shortly.",
"currentState": "Running"
}
Action Timeout
Lifecycle actions have a 30-second timeout. If the main loop does not complete the action within this window, the HTTP handler returns a timeout response.
Response: 504 Gateway Timeout
{
"error": "Action timed out after 30 seconds",
"actionType": "Reload",
"state": "Running"
}
Comparison: Runtime API vs. Project HTTP API
| Feature | Runtime API | Project HTTP API |
|---|---|---|
| Port | Dedicated (--runtime-api-port) |
Settings (HttpApiPort) |
| Prefix | /runtime/ |
/api/ |
| Available when idle | Yes | Only /api/start, /api/stop, /api/quit, /api/exit (if DisableWebApiWhenNotRunning) |
| Authentication | API key via apikey header/query (validated against apikeys.json; if none defined, all requests are allowed) |
API key via apikey header/query |
| Purpose | Process lifecycle + project file management | Project content and component control |
| Project file CRUD | Yes (list, download, upload, validate, delete, rename, clone, new, save-as) | Save, get XML, load latest version |
| Desktop support | No (Runtime only) | Yes (both Desktop and Runtime) |
Endpoint reference
Status
- GET /runtime/status — Returns comprehensive runtime state, project info, system metrics, and cumulative statistics. Responds immediately without queuing a lifecycle action.
Capabilities
- GET /runtime/capabilities — Returns static hardware and software capabilities of this runtime instance. Responds immediately (read-only, no lifecycle action). The response is useful for capacity planning, feature-gate checks, and diagnostic tooling.
Warnings
- GET /runtime/warnings — Returns recent warning log entries. Useful for verifying expected warnings after error-path operations (e.g., rejected lifecycle actions).
Errors
- GET /runtime/errors — Returns recent error log entries.
Lifecycle Actions
- GET|POST /runtime/stop — Stops the currently running project, transitioning to Idle state. All infrastructure services (HTTP API, WebSocket, Prometheus) remain active.
- GET|POST /runtime/start — Starts the last loaded project from Idle state. The project file must have been previously loaded (via initial CLI startup or
/runtime/load). - GET|POST /runtime/reload — Stops the current project, reloads it from disk, and starts it again. Useful for picking up project file changes without a full restart.
- GET|POST /runtime/load — Loads a different project file and starts it. Works from any stable state — if a project is currently running, it is stopped first.
- GET|POST /runtime/exit — Requests a graceful application shutdown. Responds immediately, then the runtime process exits.
Project Management
- GET|POST /runtime/project/new — Clears the current project and transitions to Idle. After this call no project is loaded. To make the runtime runnable again, either call
/runtime/loadto open an existing file, or call/runtime/project/saveas?switchTo=trueto persist the blank project (which also setsProjectFileNameso a subsequent/runtime/startworks). - GET|POST /runtime/project/saveas — Saves the current project under a new file name. Works from both
RunningandIdlestate — saving from Idle writes whatever is in the in-memory project, including the blank project produced by/runtime/project/new. This enables anew → saveas?switchTo=true → startworkflow for creating a project from scratch. - GET /runtime/projects/list — Enumerates
.prjand.prjpfiles in the configured ProjectsDirectory. - GET /runtime/project/download — Returns the current project file as a downloadable file with
Content-Dispositionheader. - POST /runtime/project/upload — Uploads a project file to ProjectsDirectory. The request body is the raw file content. The body is streamed directly to disk (never buffered whole in memory) and capped at 4 GB to prevent resource exhaustion.
- GET|POST /runtime/project/validate — Parses a project file without loading it. Reports XML validity, version, frame rate, and scene/input counts. Useful for checking a file before loading.
- GET|POST /runtime/project/assets — Scans a project file's XML for all asset references (media files, images, LUTs, models, etc.) and checks whether each file can be found using the file searcher. Useful for pre-flight validation before loading a project.
- GET|DELETE /runtime/projects/delete — Deletes a project file from ProjectsDirectory. Also deletes the associated
.tmbthumbnail if it exists. - GET|POST /runtime/projects/rename — Renames a project file within ProjectsDirectory. Also renames the associated
.tmbthumbnail if it exists. - GET|POST /runtime/projects/clone — Duplicates a project file within ProjectsDirectory. Also clones the associated
.tmbthumbnail if it exists. Cloning the currently running project is allowed (read-only copy).