Project Control
The core lifecycle surface for the project a Composer host is running. These endpoints start and stop processing, exit or quit the application, and manage the project file itself — set project-level properties, save, read back the project XML, list the projects available on the host, load one (by name, by upload, or the latest saved version), and inspect the project tree, its limits, or push in supporting assets. This is the section a remote operator or deployment system drives to get a host from "idle" to "playing the right project" and back.
POST /api/start
Starts Composer processing.
Parameters: None
Response:
200 OK—Project successfully started!403 Forbidden— Playback state is notStopped
POST /api/stop
Stops Composer processing.
Parameters: None
Response:
200 OK—Project stop requested!403 Forbidden— Playback state is notRunning
POST /api/exit
Exits the Composer application. Playback must be stopped first.
When to use: This is the standard, preferred shutdown endpoint. Use it from any orchestration or supervision script that drives Composer.
Parameters: None
Response:
200 OK—Composer exit called. Application will exit within three seconds.403 Forbidden— Playback state is notStopped
POST /api/quit
Exits the Composer application. Only works when playback state is Stopped.
When to use: Functionally near-identical to /api/exit — both require the project to be stopped and both trigger application shutdown. The difference is plumbing: /api/exit sets the per-project ApiExitRequested flag, while /api/quit sets a process-wide ApplicationQuitRequested flag and is dispatched ahead of the normal route table (so it works even if DisableWebApiWhenNotRunning blocks other endpoints). Prefer /api/exit; reach for /api/quit only if you need the early-dispatch behaviour.
Parameters: None
Response:
200 OK—Application quit requested- (no error response) — When playback state is not
Stoppedthe call is silently ignored at the early-dispatch layer.
GET /api/project/setproperty
Sets a property on the Project object itself. Use this for project-level settings that aren't reachable via /api/setproperty (which only targets NamedModel instances such as inputs, scenes, operators, and targets). Examples: StartAllInputsOnStart, StartAllTargetsOnStart, UseOptimizedRenderer, VideoFrameRate.
Parameters:
property(required) — Public, settable property name on theProjectclass. Case-sensitive.value(required) — String form of the value. Converted to the property's type viaTypeDescriptor.GetConverter.
Response:
200 OK—Project.{property} set to {value}400 Bad Request— Missingpropertyorvalue, or no such settable property onProject.500 Internal Server Error— Conversion or setter exception (returned message includes the exception text).
GET /api/project/save
Saves the current project to disk. Without parameters, overwrites the running .prj file (the one Composer was launched with). With path, performs a "Save As" to a different file without changing the running project's file association.
Parameters:
path(optional) — Absolute file path to save to. Parent directory must exist. Does not changeRunningProjectFileName, so subsequent saves withoutpathstill target the original file.
Response:
200 OK—Project successfully saved!(nopath)200 OK—Project successfully saved to {path}(withpath)200 OK—Error! No project file has been defined!(nopathand no running project file is set)400 Bad Request—Error! 'path' must include a directory.400 Bad Request—Error! Directory does not exist: {directory}500 Internal Server Error—Error saving project: {message}
GET /api/project/getxml
Returns the raw XML content of the current project file.
Parameters: None
Response:
200 OK— Project XML content (Content-Type: text/xml)400 Bad Request— Error reading project file
POST /api/project/loadlatestversion
Triggers Composer to reload the project from disk if a newer version is available, or if the current project file has been modified.
What "the latest version" means
Composer's project management feature uses a versioned filename convention:
<projectName>-version-<6 digits>.prj
For example: MyProject-version-000017.prj. New revisions are saved as new files with a higher 6-digit version suffix (...000017.prj, ...000018.prj, etc.) in the same folder as the current project.
When this endpoint is called, Composer:
- Scans the project's folder for files matching the version pattern of the currently loaded project, and picks the one with the highest version number.
- If that version is newer than what's currently loaded, queues it for loading.
- Also checks whether the current project file's modification timestamp has changed on disk (a "touch" reload of the same file). If so, queues a reload of the current file.
- If no newer version exists and the current file is unmodified, no reload is performed and a log entry indicates so.
The reload itself is performed asynchronously by the runtime — the response returns as soon as the request has been forwarded.
Parameters: None
Preconditions:
PM_EnableProjectManagementmust betruePM_AutoReloadNewVersionUsingApimust betrue- The runtime must be in
Runningstate with at least one frame rendered (otherwise the request is silently ignored).
Response:
200 OK—Api Request /api/project/loadlatestversion forwarded to Composer400 Bad Request— Project management not enabled or auto-reload not enabled
GET /api/project/list
Lists .prj files in the directory of the currently-loaded project. Returns an empty list if the directory contains no other projects; returns 400 if no project is loaded.
Response: 200 OK — JSON array:
[
{ "name": "Show.prj", "fullPath": "C:\\Projects\\Show.prj", "size": 18432, "lastModified": "2026-05-15T09:31:18Z", "extension": ".prj", "isCurrent": true },
{ "name": "Backup.prj", "fullPath": "C:\\Projects\\Backup.prj", "size": 17984, "lastModified": "2026-05-12T14:05:00Z", "extension": ".prj", "isCurrent": false }
]
Entries are ordered newest-first by lastModified. isCurrent flags the entry that matches Project.RunningProjectFileName.
GET /api/project/load
Loads a named .prj file from the current project's directory. The project must be stopped first (POST /api/stop); the server returns 409 Conflict if it's running.
Parameters:
file(required) — File name without path. Must end with.prj. Must live in the current project's directory (path traversal is blocked).
Response: 200 OK on success. 400 Bad Request for missing / malformed file. 404 Not Found if the file doesn't exist. 409 Conflict if playback is running. 500 Internal Server Error if Project.LoadProjectFromFile returns false.
Emits a project.load SSE event on success.
POST /api/project/upload
Uploads a new .prj file into the current project's directory. The request body is the file's raw bytes (no multipart wrapping). Existing files are overwritten unless overwrite=false.
Parameters:
filename(required) — Target file name without path. Must end with.prj.load(optional) — Whentrue, the server loads the uploaded project immediately after the write. Requires playback to be stopped; otherwise the upload succeeds but load returns 409.overwrite(optional) — Defaults totrue. Whenfalse, the upload is rejected with409 Conflictif a file with the same name already exists (the existing file is left untouched).
Body: Raw .prj bytes (typically application/octet-stream).
Response: 200 OK — JSON { name, fullPath, sizeBytes, loaded }. 409 Conflict if overwrite=false and the target already exists. 413 Payload Too Large if the body (declared via Content-Length or measured mid-stream) exceeds the configured upload cap (default 1 GiB; see Settings.HttpApiMaxUploadBytes). 507 Insufficient Storage if the declared body wouldn't fit on the project volume.
Writes go to a .upload temp file first and rename on success, so an aborted upload leaves the previous version intact.
GET /api/project/limits
Reports the upload-related limits and current project-volume capacity. A client can query this once on entry to reject a too-big upload locally instead of streaming a 1 GiB body just to receive 413.
Response: 200 OK — JSON:
{
"maxUploadBytes": 1073741824,
"projectExtensions": [".prj", ".js"],
"allowedAssetExtensions": [".mp4", ".mov", ".mxf", ".avi", ".mkv", ".webm", ".m4v",
".wav", ".mp3", ".aac", ".m4a", ".ogg", ".flac",
".png", ".jpg", ".jpeg", ".js"],
"projectDirectoryFreeBytes": 482155593728
}
projectDirectoryFreeBytes is -1 if no project is loaded (so the project volume can't be resolved) or if the OS-level query failed. maxUploadBytes reflects the configured cap; clients should treat it as authoritative rather than caching a constant.
POST /api/project/asset/upload
Uploads an asset (audio / video / image / .js script) into the current project's directory. Existing files are overwritten unless overwrite=false.
Allowed extensions: .mp4, .mov, .mxf, .avi, .mkv, .webm, .m4v, .wav, .mp3, .aac, .m4a, .ogg, .flac, .png, .jpg, .jpeg, .js. Anything else returns 415 Unsupported Media Type.
Size limit: Configured by Settings.HttpApiMaxUploadBytes (default 1 GiB). 413 Payload Too Large if exceeded. 507 Insufficient Storage if the declared body wouldn't fit on disk.
Parameters:
filename(required) — Target file name without path; extension must be in the allowlist.overwrite(optional) — Defaults totrue. Whenfalse, the upload is rejected with409 Conflictif a file with the same name already exists (the existing file is left untouched).
Body: Raw asset bytes.
Response: 200 OK — JSON { name, fullPath, sizeBytes, loaded: false }. (Asset uploads never auto-load anything.) 409 Conflict if overwrite=false and the target already exists.
GET /api/project/tree
Returns the loaded project's hierarchical structure: scenes, layers, operators, and per-scene targets, plus the project-level inputs collection. Designed for browser-based admin UIs that need to render a navigable tree of the project without making many flat /api/objects/list calls and reconstructing containment.
The response is a read-only snapshot taken at request time. To edit a specific component's properties, follow up with /api/objects/getproperties?target=<id> (full property values) or /api/types/schema?type=<typeName> (schema-only, instance-independent).
Parameters: None
Response: 200 OK — JSON object:
{
"projectFileName": "C:\\Projects\\cloud-keyer.prj",
"topMostSceneId": "11111111-1111-1111-1111-111111111111",
"activeSceneId": "11111111-1111-1111-1111-111111111111",
"scenes": [
{
"id": "11111111-1111-1111-1111-111111111111",
"name": "Scene Input 1",
"type": "Scene",
"isTopMost": true,
"isActive": true,
"isInternalLivePreviewScene": false,
"layers": [
{
"id": "22222222-2222-2222-2222-222222222222",
"name": "Sample video",
"type": "Layer",
"isVisible": true,
"input": {
"id": "33333333-3333-3333-3333-333333333333",
"name": "Sample video",
"type": "MediaFileInput"
},
"operators": [
{ "id": "44444444-4444-4444-4444-444444444444", "name": "HSV Keyer", "type": "HsvKeyerOperator" },
{ "id": "55555555-5555-5555-5555-555555555555", "name": "LayerTransform", "type": "LayerTransform" }
]
}
],
"targets": [
{ "id": "66666666-6666-6666-6666-666666666666", "name": "MoQ Target #1", "type": "MoqTarget" }
]
}
],
"inputs": [
{ "id": "33333333-3333-3333-3333-333333333333", "name": "Sample video", "type": "MediaFileInput" }
]
}
Response fields:
| Field | Type | Description |
|---|---|---|
projectFileName |
string | Full path to the currently loaded .prj file, or empty string if no project is loaded. |
topMostSceneId |
guid? | Id of the scene currently rendered to the preview, or null when no scenes exist. |
activeSceneId |
guid? | Id of the scene whose properties are currently selected/active, or null. |
scenes[] |
array | Scenes in project order. Each scene has id, name, type, isTopMost, isActive, isInternalLivePreviewScene, layers[], and targets[]. |
scenes[].isInternalLivePreviewScene |
bool | true for the synthetic scene Composer uses internally to drive the live-preview viewport. Admin UIs should typically hide this scene from users — it's not authored content. |
scenes[].layers[] |
array | Layers in scene order. Each layer has id, name, type, isVisible, input (the source feeding this layer, or null), and operators[]. |
scenes[].layers[].input |
object? | The named AbstractInput feeding this layer. Often references an entry from the top-level inputs[] array. null if the layer has no input assigned. |
scenes[].layers[].operators[] |
array | Operator chain for the layer, in execution order. |
scenes[].targets[] |
array | Per-scene output targets (e.g. MoQ, SRT, RTMP). |
inputs[] |
array | Project-level (shared) inputs. Layers reference these by id via their input field. |
Every node has id (Guid), name (string), and type (CLR class name).
Errors:
500 Internal Server Error— Exception while enumerating the project (e.g. mid-load state). Caller may retry.