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.prjEveningShow.prj from 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 GET and/or POST as documented below
  • Authentication: API key via apikey header or ?apikey= query parameter. Keys are validated against apikeys.json (same mechanism as the project-level HTTP API). If no keys are defined in apikeys.json, all requests are allowed.
  • CORS: All responses include Access-Control-Allow-Origin: * and support OPTIONS preflight
  • 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 DisableWebApiWhenNotRunning setting.
  • 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 apikey HTTP header or ?apikey= query parameter
  • Keys are validated against apikeys.json in the application directory
  • If no keys are defined (empty Keys array 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 RuntimeApiAction and awaits the result with a 30-second timeout.
  • /runtime/status responds immediately (read-only, no action queue).
  • /runtime/exit responds 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

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/load to open an existing file, or call /runtime/project/saveas?switchTo=true to persist the blank project (which also sets ProjectFileName so a subsequent /runtime/start works).
  • GET|POST /runtime/project/saveas — Saves the current project under a new file name. Works from both Running and Idle state — saving from Idle writes whatever is in the in-memory project, including the blank project produced by /runtime/project/new. This enables a new → saveas?switchTo=true → start workflow for creating a project from scratch.
  • GET /runtime/projects/list — Enumerates .prj and .prjp files in the configured ProjectsDirectory.
  • GET /runtime/project/download — Returns the current project file as a downloadable file with Content-Disposition header.
  • 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 .tmb thumbnail if it exists.
  • GET|POST /runtime/projects/rename — Renames a project file within ProjectsDirectory. Also renames the associated .tmb thumbnail if it exists.
  • GET|POST /runtime/projects/clone — Duplicates a project file within ProjectsDirectory. Also clones the associated .tmb thumbnail if it exists. Cloning the currently running project is allowed (read-only copy).