---
title: Video Compositions
description: Read the contract a Video Editor node renders against, for both template mode and sceneGraph mode.
updated: 2026-08-02
---

Wireflow assembles finished video with the `video:remotion` node, labelled **Video Editor** in the app. These endpoints publish what that node expects, so you can fill one correctly instead of guessing.

A Video Editor node runs in exactly one of two modes:

| Mode           | Set by                                         | Contract endpoint                  |
| -------------- | ---------------------------------------------- | ---------------------------------- |
| **Template**   | `config.templateId` (e.g. `apollo-devotional`) | `GET /api/v1/remotion/templates`   |
| **SceneGraph** | `config.props.sceneGraph`                      | `GET /api/v1/remotion/scene-graph` |

Template mode renders a pre-built composition and its inputs are fixed. SceneGraph mode renders a composition you author yourself as JSON (scenes, tracks, overlays, captions, effects).

All three endpoints below are read-only and require the `workflows:read` scope.

## List compositions

```
GET /api/v1/remotion/templates
```

Returns every template composition your account can use. Private and agency templates are filtered by an allowlist, so two accounts can legitimately see different lists.

**Request**

```bash
curl https://www.wireflow.ai/api/v1/remotion/templates \
  -H "Authorization: Bearer sk-your-api-key"
```

**Response** `200 OK`

```json
{
  "data": [
    {
      "id": "apollo-devotional",
      "label": "Apollo Devotional",
      "category": "reel",
      "compositionId": "ApolloDevotional",
      "fps": 30,
      "width": 1920,
      "height": 1080,
      "durationInFrames": 4500,
      "inputMappings": [
        {
          "propPath": "paragraphs",
          "portId": "paragraphs",
          "portLabel": "Tagged Script (or planned paragraphs)",
          "portType": "TEXT",
          "required": true
        },
        {
          "propPath": "audioSrc",
          "portId": "audio",
          "portLabel": "Voiceover",
          "portType": "AUDIO",
          "required": true
        },
        {
          "propPath": "brollClips",
          "portId": "brollClips",
          "portLabel": "B-roll Clips (JSON)",
          "portType": "JSON",
          "required": false
        }
      ]
    }
  ]
}
```

Trimmed for length: the real entry also carries `description`, `thumbnail`, and `defaultProps`, and this template declares seven mappings rather than the three shown.

## Get one composition

```
GET /api/v1/remotion/templates/{id}
```

Same shape, one object instead of an array. Resolving a template by an id you already hold is allowed even when it is not in your catalog listing, so a workflow you cloned keeps working.

```bash
curl https://www.wireflow.ai/api/v1/remotion/templates/apollo-devotional \
  -H "Authorization: Bearer sk-your-api-key"
```

An unknown id returns `404` with code `not_found`.

## How inputMappings map to node ports

`inputMappings` is the useful part. One entry describes one wireable input:

| Field      | Meaning                                                                                |
| ---------- | -------------------------------------------------------------------------------------- |
| `propPath` | Where the value lands in the composition's props. Can be nested, e.g. `shots.0.image`. |
| `portId`   | **The port id on the node.** An edge targets it as `in-{portId}`.                      |
| `portType` | `TEXT`, `IMAGE`, `VIDEO`, `AUDIO`, `JSON`, or `NUMBER`.                                |
| `required` | When `true`, leaving it unfed fails the render instead of shipping a blank MP4.        |

`propPath` and `portId` are often the same string but they are not the same thing, and the difference is where mistakes come from. In the example above `propPath` is `audioSrc` while `portId` is `audio`, so the voiceover arrives on the handle `in-audio` and the composition reads it as `props.audioSrc`.

Two consequences worth internalising:

- **The static port list on the node is not its real surface.** A Video Editor node's ports are materialised from the selected template's `inputMappings` when the workflow is saved. Read the template, not the node's defaults.
- **Every one of these is a port, not a setting.** Ports are filled by wiring an upstream node, never by writing a config value. The MCP tool `update_workflow_config` will refuse every key on a Video Editor node for this reason, and it is right to.

Templates whose input count varies (a reel with N shots, say) compute `inputMappings` from their current props, so the response reflects the slots that exist now rather than a fixed maximum.

## Get the sceneGraph contract

```
GET /api/v1/remotion/scene-graph
```

Publishes the authoring contract for sceneGraph mode.

```bash
curl https://www.wireflow.ai/api/v1/remotion/scene-graph \
  -H "Authorization: Bearer sk-your-api-key"
```

**Response** `200 OK`

```json
{
  "data": {
    "mode": "sceneGraph",
    "nodeType": "video:remotion",
    "configPath": "config.props.sceneGraph",
    "doc": "A SceneGraph is a JSON object the renderer turns into a real video:\n\n{\n  \"fps\": 30, ...",
    "jsonSchema": {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "object",
      "required": ["fps", "width", "height", "scenes"],
      "properties": { "fps": {}, "width": {}, "height": {}, "scenes": {} }
    }
  }
}
```

`doc` is the same authoring guide the internal renderer agent receives. `jsonSchema` is emitted from the exact Zod schema that validates your graph at run time, so the published contract cannot drift from the enforced one. The full schema declares 15 top-level properties (`fps`, `width`, `height`, `background`, `scenes`, `tracks`, `overlays`, `captionTrack`, `audioTrack`, and more); the sample above is truncated.

`$schema` is JSON Schema **draft 2020-12**, so a consumer validating against it needs a matching validator build (with Ajv that is `ajv/dist/2020`, not the default import).

Two fields frame what you are reading. `readerNote` says the document is reference material: `doc` was written as a generation prompt, so imperatives inside it like "Output ONLY the JSON object" describe what a generator emits when producing a scene graph, not how to answer this fetch. `scopeNote` records that this endpoint needs only `workflows:read`, while the preview step `doc` recommends at the end (`POST /api/v1/render/remotion/preview`) needs `workflows:execute`. Read is implied by execute, never the reverse, so a read-only key can fetch the contract but will get a `401` from preview.

Some internal fields are withheld from the published schema, so treat it as the supported surface rather than an exhaustive one. For block scenes, use the `blockId` you were given.

**It is big.** Roughly 21KB of prose and 54KB of schema, about 77KB together. Take one half with `include`:

```bash
curl "https://www.wireflow.ai/api/v1/remotion/scene-graph?include=doc" \
  -H "Authorization: Bearer sk-your-api-key"
```

`include` accepts `doc`, `schema`, or `all` (the default).

## Authentication

These endpoints accept an `sk-` API key only:

```
Authorization: Bearer sk-your-api-key
```

An MCP OAuth access token is **not** accepted here and returns `401` with code `invalid_api_key`. If your agent talks to Wireflow over MCP, it needs a separate API key to read a composition contract. See [Authentication](/docs/api/authentication).

## Filling a Video Editor node

What you can do depends on how you are connected.

**Over REST** with the `workflows:write` scope, `PUT /api/v1/workflows/{id}` accepts the full `nodes` and `edges` arrays, so you can add a Video Editor node, wire its ports, set `config.templateId`, or author `config.props.sceneGraph` outright.

**Over MCP**, you cannot, though the reason is narrower than "the tools cannot write graphs". `clone_template` and `create_workflow_from_prompt` both persist a full set of nodes and edges. What MCP cannot do is add a node to a workflow that already exists, draw an edge, or generate a Video Editor node in the first place: the graph generator builds only from the model registry, which has no `video:remotion` entry. Combined with the node having no settable config keys, that means you cannot assemble a composed video from an empty canvas over MCP.

Start from a workflow template that already contains one (`list_templates`, then `clone_template`), then confirm with `get_workflow` that a `video:remotion` node is really present before you spend a run. The `video` category tag means "makes video", not "assembles video", so many tagged templates have no assembler node and some templates that have one are not tagged `video`. Then change the content by editing the upstream nodes that feed it or by passing `inputs` at run time. See [MCP integration](/docs/mcp).

Last updated: 2026-08-02

---

Documentation index: fetch https://www.wireflow.ai/llms.txt for the full list of Wireflow docs.
