# Projects and commands

> A project is one timeline document, and the only way to change it is a list of commands run through the same reducer the editor uses.

## The project

A project is an edit document: output settings (aspect ratio, width, height, frame rate, background), a list of tracks, and a list of clips. Clips are top-level in the document, each pointing at the track it sits on with `trackIndex`. A clip is video, audio, image, text, captions or a shape, with a `start` and `duration` in seconds on the timeline.

Read one with `GET /v1/projects/{id}`. The default `clips` view summarises every clip; `?view=document` returns the document verbatim, exactly what the editor and the renderer read.

## Commands

You never write the document directly. You send commands, and the server applies them with the same reducer the web editor, the phone app and the MCP server use:

```json
{
  "commands": [
    { "op": "addClip", "type": "video", "src": "https://api.vidmoat.com/uploads/clip.mp4", "start": 0, "duration": 6, "trackIndex": 0 },
    { "op": "addTextClip", "text": "Shipped from the API", "start": 0.4, "duration": 3, "trackIndex": 1, "style": { "fontSize": 72 }, "patch": { "y": -220 } }
  ]
}
```

`GET /v1/schema/commands` lists every op and its parameters. It is the same schema the editor validates against: if an op is not in there, it does not exist.

> **Warning: Where parameters live.** On `addTextClip`, `fontSize` goes in `style` and `x`/`y` go in `patch`. Passed at the top level they are ignored: the clip is created, the call succeeds, and the text lands in the centre on top of everything else. Positions are offsets from the canvas centre, and text is centre-anchored.

## How a batch is applied

- Commands run in order, one to 200 per request.
- **Each command succeeds or fails on its own.** A failure is reported in `results[]` (`ok: false` with an `error`), not as an HTTP error. The batch is saved when at least one command succeeded, so read `ok` and `results`, not only the status code.
- A feature the plan does not include (scripting, for example) is dropped and listed in `blocked`; the rest still applies.
- If the project changed while the batch ran, nothing is saved and you get `409` with `conflictCode: "stale_rev"`. Re-read the project and send the batch again.
- The call is **not idempotent**. A retried batch adds its clips a second time. After a timeout, re-read the project before deciding to resend.

## Check before you save

- `"dryRun": true` on `POST /v1/projects/{id}/commands` runs the batch and saves nothing.
- `POST /v1/projects/{id}/validate` does the same with only a read scope, so a read-only key can check a program.
- Both return `lint` (overlapping text, off-canvas elements, unreadable sizes) and `suggestedPreviewTimes`. Look at those frames with the [preview endpoint](https://developer.vidmoat.com/developer/docs/preview) before you render.

## Workspaces

A workspace is a folder for projects: a client, a series, a campaign. It is not a team; a team decides who can see a project, a workspace decides what body of work it belongs to. File a project with `PUT /v1/projects/{id}/workspace`, list a folder with `GET /v1/projects?workspace=<id>`. Deleting a workspace never deletes the projects in it. Workspaces use the projects scopes.

- [Apply commands](https://developer.vidmoat.com/developer/docs/api/projects/apply-commands): The endpoint reference, with every field of the response.
- [Run the editing agent](https://developer.vidmoat.com/developer/docs/api/generation/run-agent): Describe the edit in words and let the agent write the commands.

---

Source: https://developer.vidmoat.com/developer/docs/concepts/projects-and-commands
Previous: [Scopes](https://developer.vidmoat.com/developer/docs/scopes.md)
Next: [Renders and previews](https://developer.vidmoat.com/developer/docs/concepts/renders-and-previews.md)
All documentation: https://developer.vidmoat.com/llms-full.txt
