---
title: Video Project Management
url: https://docs.kaltura.com/docs/conversational-agents/avatar-videos/video-management
---

## Video Project Management

A video project is the central object — it holds the avatar assignment, an ordered list of scenes (each with narration text and an optional layout), and tracks the generation status. You create a project, populate its scenes (manually or via AI composition), then generate the final video.

### Create a Video Project

The `video/add` endpoint creates a new project. You must provide a `name` and an `avatarId` (from section 5). Scenes can be included at creation time or added later via `video/update`.

```bash
VIDEO_RESULT=$(curl -s -X POST "$AVATAR_API/video/add" \
  -H "Authorization: Bearer $KS" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"Q1 Training Overview\",
    \"avatarId\": \"$AVATAR_ID\",
    \"scenes\": [
      {
        \"layoutType\": \"full-screen\",
        \"narration\": { \"text\": \"Welcome to the Q1 training overview.\" }
      },
      {
        \"layoutType\": \"broll\",
        \"narration\": { \"text\": \"Here we see the key metrics from last quarter.\" },
        \"broll\": {
          \"entryId\": \"$BROLL_ENTRY_ID\",
          \"startTime\": 30
        }
      }
    ]
  }")
VIDEO_ID=$(echo "$VIDEO_RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Video ID: $VIDEO_ID"
```

#### Top-Level Request Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | yes | Display name for the video project |
| `avatarId` | string | yes | The avatar ID returned by `avatar/upsert` — determines which AI presenter appears in the video |
| `scenes` | array of scene objects | no | Ordered list of scenes. Can be empty at creation and populated later via `video/update` or `video/compose` |

#### Scene Object

Each element in the `scenes` array represents one segment of the video.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `layoutType` | string enum | no | How the scene is displayed. Default: `"full-screen"` |
| `narration` | object | no | The spoken content for this scene (see narration fields below) |
| `broll` | object | no | Background video configuration (see broll fields below). The broll data is stored regardless of `layoutType` — you can set it up front and switch `layoutType` to `"broll"` later |

**`layoutType` enum:**

| Value | Visual | Description |
|-------|--------|-------------|
| `"full-screen"` | Avatar fills the frame | The avatar character is rendered full-screen with its configured background. Use for introductions, conclusions, and talking-head segments |
| `"broll"` | Avatar overlaid on video | The avatar is composited as a smaller overlay on top of a background video clip. Use when referencing visual content like charts, demos, or slides |

#### Narration Object

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `text` | string | yes (if narration provided) | The script text the avatar will speak. This is converted to audio via text-to-speech during generation |
| `avatarId` | string | no | Override the video-level avatar for this specific scene. Omit to use the project's default `avatarId`. Useful for multi-presenter videos where different scenes feature different characters |

#### Broll Object

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `entryId` | string | yes (if broll provided) | Kaltura entry ID of the background video to display behind the avatar |
| `startTime` | number | yes (if broll provided) | Start time in seconds within the background video. The clip plays from this point for the duration of the scene's narration |

**B-roll constraints:**
- The same entry can be reused across multiple scenes with different `startTime` values — each reuse does not count as an additional source  
- B-roll entries require a **standard frame rate** (25 or 30 fps). Re-encode PowerPoint exports and other non-standard-rate sources to 25 fps before uploading (e.g., `ffmpeg -i input.mp4 -r 25 -c:v libx264 -profile:v main -c:a aac output.mp4`)  
- Kaltura's transcoding pipeline adds an audio track to video-only uploads, so entries uploaded through the standard upload workflow (uploadToken → media.add → media.addContent) are always compatible with the Avatar renderer  
- The `broll` object is stored on the scene regardless of `layoutType` — you can pre-configure B-roll data and switch `layoutType` to `"broll"` later via `video/update` without re-specifying the entry  

**Narration constraints:**
- Provide narration text as a non-empty string — the API validates this at `video/add` time. Omitting the `narration` object entirely is accepted, but scenes with narration are required for generation  
- Each scene's narration must produce at least **~1.5 seconds of audio** after text-to-speech conversion. Use at least one complete sentence per scene (~4+ words) to meet this threshold  

**TTS speaking rate:**
- The text-to-speech engine speaks at approximately **2.4 words/second** (measured average across varying sentence lengths). When writing narration for B-roll scenes, calculate the maximum safe word count as: `available_clip_duration × 2.1` (using a 15% safety buffer)  
- Use `video/previewAudio` to verify the actual TTS duration for critical scenes before generating

#### Response Fields

The response returns the full video object. Key fields:

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | The video project ID — use as `$VIDEO_ID` in all subsequent calls |
| `partnerId` | number | Your Kaltura partner ID |
| `userId` | string | The KS user who created the project |
| `status` | string enum | Current status — starts as `"draft"` (see status lifecycle in section 3) |
| `name` | string | The project name |
| `avatarId` | string | The assigned avatar ID |
| `scenes` | array | The scenes array as submitted |
| `entryId` | string or null | Kaltura media entry ID of the generated video — populated only when status is `"ready"` |
| `composeParams` | object or null | The compose parameters if AI composition was used (see section 7) |
| `createdAt` | string | ISO 8601 creation timestamp |
| `updatedAt` | string | ISO 8601 last update timestamp |

#### Scene Examples

**Full-screen scene** — avatar talks directly to camera:

```bash
{ "layoutType": "full-screen", "narration": { "text": "Let me introduce the agenda." } }
```

**B-roll scene** — avatar overlaid on a video clip starting at the 45-second mark:

```bash
{ "layoutType": "broll", "narration": { "text": "As you can see in this demo..." }, "broll": { "entryId": "1_xyz789", "startTime": 45 } }
```

**Scene with per-scene avatar override** — different presenter for this scene:

```bash
{ "layoutType": "full-screen", "narration": { "text": "Hi, I am Adam.", "avatarId": "ADAM_AVATAR_ID" } }
```

**Minimal scene** — layout defaults to `"full-screen"`:

```bash
{ "narration": { "text": "This scene uses the default full-screen layout." } }
```

### Get a Video Project

```bash
curl -s -X POST "$AVATAR_API/video/get" \
  -H "Authorization: Bearer $KS" \
  -H "Content-Type: application/json" \
  -d "{ \"id\": \"$VIDEO_ID\" }"
```

Returns the full `VideoDto` including `status`, `entryId` (if generated), and all scenes.

### Update a Video Project

```bash
curl -s -X POST "$AVATAR_API/video/update" \
  -H "Authorization: Bearer $KS" \
  -H "Content-Type: application/json" \
  -d "{
    \"id\": \"$VIDEO_ID\",
    \"name\": \"Q1 Training — Updated\",
    \"scenes\": [
      {
        \"layoutType\": \"full-screen\",
        \"narration\": { \"text\": \"Updated welcome message for Q1 training.\" }
      }
    ]
  }"
```

**Request fields:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | yes | Video project ID |
| `name` | string | no | Updated name |
| `avatarId` | string | no | Updated avatar ID |
| `scenes` | array | no | Replaces all scenes (removed trailing scenes are cleaned up) |

Scenes are editable only when status is `draft` or `composed` — the API returns `VIDEO_IS_PROCESSING` during active operations.

### List Video Projects

```bash
curl -s -X POST "$AVATAR_API/video/list" \
  -H "Authorization: Bearer $KS" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": { "orderBy": "-createdAt" },
    "pager": { "offset": 0, "limit": 10 }
  }'
```

**Filter options:**

| Field | Type | Values |
|-------|------|--------|
| `orderBy` | string | `"-createdAt"`, `"createdAt"`, `"-updatedAt"`, `"updatedAt"` (default: `"-createdAt"`) |

**Pager options:**

| Field | Type | Description |
|-------|------|-------------|
| `offset` | number | Number of results to skip (0-based) |
| `limit` | number | Maximum number of results to return |

**Response:**

```json
{
  "objects": [ ... ],
  "totalCount": 42
}
```

### Delete a Video Project

```bash
curl -s -X POST "$AVATAR_API/video/delete" \
  -H "Authorization: Bearer $KS" \
  -H "Content-Type: application/json" \
  -d "{ \"id\": \"$VIDEO_ID\" }"
```
