Project Management

Endpoints for managing project files without filesystem access. All endpoints support GET in addition to their canonical HTTP method.

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).

Parameters: None

Preconditions:

  • Runtime state must be Running or Idle (returns 409 if in Loading/Stopping state)

Response (success): 200 OK

{
  "success": true,
  "message": "New empty project created. Runtime is idle.",
  "state": "Idle"
}

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 parameters:

Parameter Required Description
fileName Yes Target file name. Relative paths resolved against ProjectsDirectory.
switchTo No If true, ProjectFileName is updated to the new path so subsequent /start, /reload, and /saveas calls operate on the saved file. Default: false.

Preconditions:

  • Runtime state must be Running or Idle (returns 409 in transitional states like Loading/Stopping)
  • Target directory must exist

Response (success): 200 OK

{
  "success": true,
  "message": "Project saved as: MyProject-copy.prj",
  "state": "Running"
}

Response (with switchTo=true): 200 OK

{
  "success": true,
  "message": "Project saved as: MyProject-copy.prj (now the active project)",
  "state": "Running"
}

Response (missing parameter): 400 Bad Request

{
  "error": "fileName is required",
  "example": "/runtime/project/saveas?fileName=MyProject-copy.prj&switchTo=false"
}

Response (invalid characters in file name): 400 Bad Request

The file-name portion (after stripping any directory) is rejected when it contains characters illegal on the target filesystem (<, >, :, ", |, ?, *, control chars, etc.).

{
  "error": "File name contains invalid characters.",
  "fileName": "Bad<Name>.prj"
}

GET /runtime/projects/list

Enumerates .prj and .prjp files in the configured ProjectsDirectory.

GET parameters:

Parameter Required Description
headerPeek No If true, extracts version and frame rate from each .prj file's XML header. Default: false.

Response: 200 OK — JSON array:

[
  {
    "name": "MyProject.prj",
    "fullPath": "C:\\Projects\\MyProject.prj",
    "size": 45678,
    "lastModified": "2026-04-18T10:00:00.0000000Z",
    "extension": ".prj",
    "version": "2.6.9605.39106",
    "frameRate": "50"
  }
]

Response fields:

Field Type Description
name string File name
fullPath string Full absolute path on the server
size long File size in bytes
lastModified string Last modified date/time (UTC ISO 8601)
extension string File extension (.prj or .prjp)
version string? Application version from XML header (only with headerPeek=true)
frameRate string? Video frame rate from XML header (only with headerPeek=true)

Response (directory not configured): 400 Bad Request

{
  "error": "Projects directory is not configured or does not exist",
  "projectsDirectory": ""
}

GET /runtime/project/download

Returns the current project file as a downloadable file with Content-Disposition header.

Parameters: None

Response (success): 200 OK

Content-Type: application/xml
Content-Disposition: attachment; filename="MyProject.prj"

<?xml version="1.0" encoding="utf-8"?>
<Settings>
  ...
</Settings>

Response (no project): 404 Not Found

{
  "error": "No project file available for download",
  "fileName": ""
}

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 parameters:

Parameter Required Description
fileName Yes Target file name within ProjectsDirectory. Must end in .prj or .prjp (or no extension — .prj is assumed).

Request body: Raw file bytes (the .prj XML content).

Response (success): 200 OK

{
  "success": true,
  "message": "File uploaded",
  "fileName": "MyProject.prj",
  "fullPath": "C:\\Projects\\MyProject.prj",
  "size": 45678
}

Response (no body): 400 Bad Request

{
  "error": "Request body is empty — expected project file content"
}

Response (disallowed extension): 400 Bad Request

{
  "error": "Only .prj, .prjp files may be uploaded.",
  "fileName": "unexpected.txt"
}

Response (over size limit): 413 Payload Too Large

{
  "error": "Upload exceeds the 4,294,967,296-byte limit.",
  "limitBytes": 4294967296
}

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 parameters:

Parameter Required Description
fileName Yes File name to validate. Relative paths resolved against ProjectsDirectory.

Response (valid file): 200 OK

{
  "valid": true,
  "fileName": "MyProject.prj",
  "version": "2.6.9605.39106",
  "frameRate": "50",
  "scenes": 2,
  "inputs": 5
}

Response (invalid XML): 200 OK

{
  "valid": false,
  "error": "XML parse error: Root element is missing."
}

Response (missing structure): 200 OK

{
  "valid": false,
  "error": "Expected root element 'Settings', found 'InvalidRoot'"
}

Response (encrypted file): 200 OK

{
  "valid": false,
  "error": "Encrypted project files (.prjp) cannot be validated without a decryption key"
}

Response (file not found): 404 Not Found

{
  "error": "Project file not found",
  "fileName": "C:\\Projects\\Missing.prj"
}

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.

The file searcher uses the same search algorithm as project loading: it checks the configured MediaDirectory, ProjectsDirectory, parent directories, and various subdirectory conventions.

GET parameters:

Parameter Required Description
fileName Yes File name to scan. Relative paths resolved against ProjectsDirectory.

Response (success): 200 OK

{
  "fileName": "MyProject.prj",
  "totalAssets": 5,
  "missingAssets": 1,
  "allFound": false,
  "assets": [
    {
      "property": "SourceUrl",
      "value": "Video/intro.mp4",
      "found": true,
      "resolvedPath": "C:\\Media\\Video\\intro.mp4",
      "component": "VideoInput1",
      "componentType": "MediaFileInput"
    },
    {
      "property": "SourceUrl",
      "value": "Images/missing-logo.png",
      "found": false,
      "resolvedPath": null,
      "component": "LogoImage",
      "componentType": "StillImageInput"
    }
  ]
}

Response fields:

Field Type Description
fileName string Project file name
totalAssets int Total asset references found in the XML
missingAssets int Number of assets not found by the file searcher
allFound bool True if every asset was resolved successfully
assets[].property string XML property name (e.g. SourceUrl, LutUrl, GarbageMatteSourceUrl)
assets[].value string Raw value from the project XML
assets[].found bool Whether the file was found
assets[].resolvedPath string? Resolved absolute path if found, null otherwise
assets[].component string Name of the component referencing this asset
assets[].componentType string Type of the component (XML element name)

Scanned properties:

SourceUrl, CleanPlateUri, CleanPlatePath, GarbageMatteSourceUrl, SuppressionMaskSourceUrl, LutUrl, ModelSourceUrl, TrackerTextureAtlasInfo, TexturesLocation

The endpoint also checks for a ScriptName entry in the project — if present, it verifies the script file exists in the same directory as the project file (without using the file searcher).

Response (file not found): 404 Not Found

Response (encrypted): 200 OK with error field and empty assets array


GET|DELETE /runtime/projects/delete

Deletes a project file from ProjectsDirectory. Also deletes the associated .tmb thumbnail if it exists.

GET parameters:

Parameter Required Description
name Yes File name to delete (e.g. OldProject.prj)

Preconditions:

  • The target file must have a .prj or .prjp extension (returns 400)
  • The file must not be the currently loaded project, whether Running or Idle (returns 409). Call /runtime/project/new first to unload it.

Response (success): 200 OK

{
  "success": true,
  "message": "Project deleted",
  "fileName": "OldProject.prj"
}

Response (disallowed extension): 400 Bad Request

{
  "error": "Only .prj, .prjp files may be deleted.",
  "fileName": "unexpected.txt"
}

Response (loaded project): 409 Conflict

{
  "error": "Cannot delete the currently loaded project. Call /runtime/project/new first to unload it.",
  "fileName": "CurrentProject.prj"
}

Response (not found): 404 Not Found

{
  "error": "Project file not found",
  "fileName": "C:\\Projects\\OldProject.prj"
}

GET|POST /runtime/projects/rename

Renames a project file within ProjectsDirectory. Also renames the associated .tmb thumbnail if it exists.

GET parameters:

Parameter Required Description
name Yes Current file name
newName Yes New file name

Preconditions:

  • Both source and target file names must have a .prj or .prjp extension (returns 400)
  • The source file must not be the currently loaded project, whether Running or Idle (returns 409). Call /runtime/project/new first to unload it.
  • The target file name must not already exist (returns 409)

Response (success): 200 OK

{
  "success": true,
  "message": "Renamed to NewName.prj",
  "oldName": "OldName.prj",
  "newName": "NewName.prj"
}

Response (disallowed extension): 400 Bad Request

{
  "error": "Only .prj, .prjp files may be renamed.",
  "sourceName": "Old.prj",
  "targetName": "New.txt"
}

Response (loaded project): 409 Conflict

{
  "error": "Cannot rename the currently loaded project. Call /runtime/project/new first to unload it.",
  "fileName": "CurrentProject.prj"
}

Response (target exists): 409 Conflict

{
  "error": "A project file with the new name already exists",
  "fileName": "NewName.prj"
}

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).

GET parameters:

Parameter Required Description
name Yes Source file name
newName Yes Target file name for the copy

Preconditions:

  • Both source and target file names must have a .prj or .prjp extension (returns 400)
  • The target file name must not already exist (returns 409)

Response (success): 200 OK

{
  "success": true,
  "message": "Cloned to Copy.prj",
  "sourceName": "Source.prj",
  "cloneName": "Copy.prj"
}

Response (disallowed extension): 400 Bad Request

{
  "error": "Only .prj, .prjp files may be cloned.",
  "sourceName": "Source.prj",
  "targetName": "Copy.txt"
}

Response (target exists): 409 Conflict

{
  "error": "A project file with the target name already exists",
  "fileName": "Copy.prj"
}