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.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 the Server-Sent Events stream, or to a WebSocket, for real-time lifecycle events (project loaded, state transitions, runtime exiting) instead of polling. Both carry the same events; the SSE stream is one plain GET and is served in every runtime state, including when no project is loaded.
  • 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 & authorization: API key via apikey header or ?apikey= query parameter, validated against apikeys.json (same mechanism and file as the project-level HTTP API). Each key carries a role (viewer / operator / editor / admin) that determines which endpoints it may call. An unknown/invalid key returns 401 Unauthorized; a valid key whose role lacks the endpoint's required permission returns 403 Forbidden. If no keys are defined, the API is open and every request runs as admin. See Authentication for the role model.
  • CORS: All responses include Access-Control-Allow-Origin: * and support OPTIONS preflight
  • Content-Type: All responses are application/json; charset=utf-8, except /runtime/events/subscribe, which is text/event-stream; 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 — and the same apikeys.json file, including its roles — as the project-level HTTP API. See the Authentication and API keys section for the full reference (both key forms, the role ladder, and the permission definitions).

In summary:

  • Provide a key via the apikey HTTP header or ?apikey= query parameter.

  • Keys are validated against apikeys.json in the application directory. Keys may be bare strings (legacy — full admin access) or scoped objects { "key", "role", "label" }. A bare string or an object with no role ⇒ admin; an unknown role string fails closed to viewer.

  • Roles form a fixed ladder, each a superset of the one below:

    Role Grants
    viewer read
    operator read, control
    editor read, control, config, builder
    admin everything
  • If no keys are defined (empty Keys array or missing file), the API is open and every request runs as admin.

  • An unknown / invalid key returns 401 Unauthorized:

{
  "error": "Invalid API key"
}
  • A valid key whose role lacks the endpoint's required permission returns 403 Forbidden, and the JSON error states the required permission.

Each endpoint section below lists its required permission with a Requires: note. The per-path summary:

Permission Endpoints
read (viewer+) /runtime/status, /runtime/capabilities, /runtime/warnings, /runtime/errors, /runtime/projects/list, /runtime/project/validate, /runtime/project/assets
control (operator+) /runtime/stop, /runtime/start, /runtime/reload, /runtime/load, /runtime/exit
config (editor+) /runtime/project/new, /runtime/project/saveas, /runtime/project/download, /runtime/project/upload, /runtime/projects/delete, /runtime/projects/rename, /runtime/projects/clone

The health & probe endpoints (/startup, /health, /ready, and their aliases) are unauthenticated by design — see Health & Probe Endpoints.


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)
Event stream /runtime/events/subscribe (SSE) — served in every state, including idle /api/events/subscribe (SSE) — only while playback is running
Authentication API key via apikey header/query, with per-key roles (validated against apikeys.json; if none defined, all requests run as admin) API key via apikey header/query, with per-key roles (same apikeys.json)
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, and clears the project graph — releasing its inputs, decoders and sockets. All infrastructure services (HTTP API, WebSocket, Prometheus) remain active. Use the legacy /api/stop instead to stop playback while keeping the project loaded.
  • GET|POST /runtime/start — Starts the last loaded project from Idle state, re-reading it from disk. The project file must have been previously loaded (via initial CLI startup or /runtime/load). Use the legacy /api/start instead to start whatever is currently in memory without re-reading it.
  • 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).

SSE Events

  • GET /runtime/events/subscribe — Opens a long-lived text/event-stream connection. Each event arrives as a pair of lines — an event: line naming the event kind and a data: line carrying a single-line JSON payload — separated by a blank line. The server emits a : keepalive comment every 15 s so dead connections are detected within ~30 s on both ends, and intermediate proxies and NAT mappings stay open on otherwise-quiet streams.