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
RunningorIdle(returns409if inLoading/Stoppingstate)
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
RunningorIdle(returns409in transitional states likeLoading/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
.prjor.prjpextension (returns400) - The file must not be the currently loaded project, whether
RunningorIdle(returns409). Call/runtime/project/newfirst 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
.prjor.prjpextension (returns400) - The source file must not be the currently loaded project, whether
RunningorIdle(returns409). Call/runtime/project/newfirst 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
.prjor.prjpextension (returns400) - 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"
}