> ## Documentation Index
> Fetch the complete documentation index at: https://heygen-1fa696a7.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# HeyGen Studio

> Compose avatar clips, images, and video footage into a single rendered video with one API call — HeyGen handles layout, timing, and the final export.

<img className="w-full h-44 object-cover rounded-xl" src="https://mintcdn.com/heygen-1fa696a7/hfMXXwJzjE7vBSYZ/images/theme/research-5.webp?fit=max&auto=format&n=hfMXXwJzjE7vBSYZ&q=85&s=422da140ece4b799d490f85bb2013ca5" alt="" noZoom width="1400" height="788" data-path="images/theme/research-5.webp" />

## Overview

A **studio video** is a single video composed from an ordered list of whole-frame scenes — avatar clips, still images, and existing video footage — concatenated in the order you send them. You describe the scenes; HeyGen owns the layout, derives each scene's duration, center-crops every scene to one global output canvas, and returns a single `video_id` for the finished MP4.

Create one by passing `"type": "studio"` to [`POST /v3/videos`](/reference/create-video) — the same endpoint that renders `avatar`, `image`, and `cinematic_avatar` videos:

```json theme={null}
{
  "type": "studio",
  "scenes": [ ... ]
}
```

Each scene is one of three types:

| Scene `type`                           | What it renders                                              | Duration comes from                         |
| -------------------------------------- | ------------------------------------------------------------ | ------------------------------------------- |
| [`avatar_video`](#avatar-video-scenes) | An avatar or animated image speaking a script or audio track | The audio                                   |
| [`image`](#image-scenes)               | A still image, silent or narrated                            | `duration` (silent) or the audio (narrated) |
| [`video`](#video-scenes)               | An existing video clip                                       | The clip itself                             |

Rendering is all-or-nothing: one request produces one video, and every scene renders or none do. Output settings — aspect ratio, resolution, captions — are global, set once per request.

<Note>
  Studio videos define their scenes **in the request**, which is ideal for programmatic composition. To render a reusable layout you've designed visually in HeyGen Studio, use a [HeyGen Studio Template](/templates) instead.
</Note>

## Quick Example

A three-scene video: a title card held for three seconds, an avatar delivering the message, and an existing clip as the outro.

```bash theme={null}
curl -X POST "https://api.heygen.com/v3/videos" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "studio",
    "title": "Product update — July",
    "aspect_ratio": "16:9",
    "resolution": "1080p",
    "scenes": [
      {
        "type": "image",
        "source": { "type": "url", "url": "https://example.com/title-card.png" },
        "duration": 3
      },
      {
        "type": "avatar_video",
        "input": {
          "type": "avatar",
          "avatar_id": "YOUR_AVATAR_ID",
          "script": "Here is everything we shipped this month.",
          "voice_id": "YOUR_VOICE_ID",
          "background": { "type": "color", "color": "#0b1220" }
        }
      },
      {
        "type": "video",
        "source": { "type": "url", "url": "https://example.com/outro.mp4" },
        "playback": { "volume": 0.6 }
      }
    ]
  }'
```

```json Response theme={null}
{
  "data": {
    "video_id": "vid_9f2c..."
  }
}
```

Generation is asynchronous. Poll [`GET /v3/videos/{video_id}`](/reference/get-video) until `status` is `completed`, then read the download URL — or pass a `callback_url` to receive a [webhook](/docs/webhooks) when the video is ready.

## Request Body

| Parameter      | Type   | Required | Default | Description                                                                                                                         |
| -------------- | ------ | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `type`         | string | Yes      | —       | Must be `studio`.                                                                                                                   |
| `scenes`       | array  | Yes      | —       | Ordered list of whole-frame scenes to concatenate, `1`–`50`. Each is one of the [scene types](#scene-types) below.                  |
| `title`        | string | No       | —       | Display title for the video in the HeyGen dashboard.                                                                                |
| `aspect_ratio` | string | No       | `16:9`  | Global output aspect ratio: `16:9`, `9:16`, `4:5`, `5:4`, `1:1`, or `auto`. Each scene is center-cropped to this canvas.            |
| `resolution`   | string | No       | —       | Global output resolution: `720p`, `1080p`, or `4k`.                                                                                 |
| `caption`      | object | No       | —       | Caption settings — see [Captions](#captions).                                                                                       |
| `callback_url` | string | No       | —       | [Webhook](/docs/webhooks) URL to receive a POST notification when the video is ready.                                               |
| `callback_id`  | string | No       | —       | Caller-defined identifier echoed back in the webhook payload.                                                                       |
| `watermark`    | object | No       | —       | Custom watermark image overlay (PNG or JPEG). A premium option for select Enterprise customers — contact support to request access. |

The output container is MP4.

## Scene Types

Every scene fills the whole frame and carries a `type` discriminator that determines its remaining fields.

### Avatar Video Scenes

`"type": "avatar_video"` renders a speaking scene. Its `input` object has `"type": "avatar"` and references an avatar you already have by `avatar_id`. The scene's duration is derived from the audio.

Mirrors the standalone [avatar creation mode](/generate-avatar-video), minus the output settings — those are global on the studio request. Any avatar look works, exactly as in the standalone mode: **video avatars, studio avatars, and photo avatars**. To use a photo avatar (a "talking photo"), pass its look id as `avatar_id`.

```json theme={null}
{
  "type": "avatar_video",
  "input": {
    "type": "avatar",
    "avatar_id": "YOUR_AVATAR_ID",
    "script": "Welcome back.",
    "voice_id": "YOUR_VOICE_ID"
  }
}
```

| Field            | Type   | Required                  | Description                                                                                                                                                      |
| ---------------- | ------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`           | string | Yes                       | Must be `avatar`.                                                                                                                                                |
| `avatar_id`      | string | Yes                       | HeyGen avatar ID (video avatar or photo avatar look ID).                                                                                                         |
| `script`         | string | One audio source required | Text for the avatar to speak. Pair with `voice_id`, or omit `voice_id` to use the avatar's default voice.                                                        |
| `voice_id`       | string | With `script`             | Voice for text-to-speech. Browse with [`GET /v3/voices`](/reference/list-voices). Optional when the avatar has a default voice.                                  |
| `audio_url`      | string | One audio source required | Public URL of an audio file to lip-sync.                                                                                                                         |
| `audio_asset_id` | string | One audio source required | Asset ID of an uploaded audio file — see [Asset Management](/assets).                                                                                            |
| `voice_settings` | object | No                        | Voice tuning: `speed` (`0.5`–`1.5`), `pitch` (`-50`–`50` semitones), `volume` (`0.0`–`1.0`), `locale`.                                                           |
| `engine`         | object | No                        | Generation engine — see [Choosing an engine](#choosing-an-engine).                                                                                               |
| `motion_prompt`  | string | No                        | Natural-language prompt controlling body motion and hand gestures. Supported for photo avatars on either engine, and for video avatars on [Avatar V](/avatar-v). |
| `expressiveness` | string | No                        | Expressiveness level for photo avatars on [Avatar IV](/avatar-iv). Defaults to `low`.                                                                            |
| `background`     | object | No                        | Solid-color background composited behind the avatar: `{ "type": "color", "color": "#RRGGBB" }`.                                                                  |

Provide exactly one audio source: `script` (with `voice_id` or a default voice), `audio_url`, or `audio_asset_id`.

<Note>
  To lip-sync your own photo, register it as a photo avatar first via the [photo-avatar API](/create-photo-avatar), then use its look id as `avatar_id` here. A studio scene references existing avatars; it does not animate a raw image inline.
</Note>

#### Choosing an engine

The `avatar` input accepts an optional `engine` object, defaulting to [Avatar IV](/avatar-iv) when omitted. Pass `{ "type": "avatar_v" }` for cross-reference-driven animation on [Avatar V](/avatar-v) — check `supported_api_engines` on the avatar look to confirm eligibility. On Avatar V you may also set `engine.reference_look_id` to an `instant_avatar` look in the same avatar group to pin the animation reference; when omitted, video avatars self-reference and photo avatars auto-select a sibling. `expressiveness` applies on Avatar IV; `motion_prompt` applies to photo avatars on either engine and to video avatars on Avatar V.

### Image Scenes

`"type": "image"` shows a still image in one of two modes — choose exactly one:

* **Silent** — set `duration` (seconds) and no audio source. The image holds on screen for that long.
* **Narrated** — set exactly one audio source and omit `duration`. The scene length follows the audio.

```json theme={null}
{
  "type": "image",
  "source": { "type": "url", "url": "https://example.com/chart.png" },
  "script": "Revenue grew forty percent quarter over quarter.",
  "voice_id": "YOUR_VOICE_ID"
}
```

| Field            | Type   | Required      | Description                                               |
| ---------------- | ------ | ------------- | --------------------------------------------------------- |
| `type`           | string | Yes           | Must be `image`.                                          |
| `source`         | object | Yes           | Still image to display — see [Asset input](#asset-input). |
| `duration`       | number | Silent mode   | Seconds to hold the image, up to `300`.                   |
| `script`         | string | Narrated mode | Text to speak over the image. Pair with `voice_id`.       |
| `voice_id`       | string | With `script` | Voice for text-to-speech.                                 |
| `audio_url`      | string | Narrated mode | Public URL of an audio file to play over the image.       |
| `audio_asset_id` | string | Narrated mode | Asset ID of an uploaded audio file.                       |
| `voice_settings` | object | No            | Voice tuning (speed, pitch, locale).                      |

### Video Scenes

`"type": "video"` drops an existing clip into the sequence at its own length.

```json theme={null}
{
  "type": "video",
  "source": { "type": "asset_id", "asset_id": "YOUR_ASSET_ID" },
  "playback": { "mute": true }
}
```

| Field      | Type   | Required | Description                                                                                                                                       |
| ---------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`     | string | Yes      | Must be `video`.                                                                                                                                  |
| `source`   | object | Yes      | Video clip to include — see [Asset input](#asset-input).                                                                                          |
| `playback` | object | No       | Audio control: `volume` (`0.0`–`1.0`, where `1.0` is the source level) and `mute` (force the clip silent). Omit to keep the clip's source volume. |

## Asset input

The image/video scene `source` field takes an asset object, discriminated by `type`. Provide one of:

| Form           | Shape                                                                                                                              |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Public URL     | `{ "type": "url", "url": "https://..." }`                                                                                          |
| Uploaded asset | `{ "type": "asset_id", "asset_id": "..." }` — from [`POST /v3/assets`](/reference/upload-asset) (see [Asset Management](/assets)). |
| Inline base64  | `{ "type": "base64", "media_type": "image/png", "data": "<base64>" }`                                                              |

## Captions

Pass a `caption` object to generate captions across the full composed video. A sidecar subtitle file is always returned via `subtitle_url` in the chosen `file_format`; add `style` to additionally burn the captions into the rendered video — the sidecar is still delivered.

| Field         | Type   | Required | Default | Description                                                                     |
| ------------- | ------ | -------- | ------- | ------------------------------------------------------------------------------- |
| `file_format` | string | No       | `srt`   | Format of the sidecar caption file.                                             |
| `style`       | string | No       | —       | Visual style for burned-in captions: `default`. Omit for sidecar-only captions. |

## Rendering at Scale

Studio requests batch like every other `/v3/videos` creation mode: include them as items in [`POST /v3/videos/batches`](/reference/create-video-batch) to submit many compositions in one call and poll a single `batch_id` — see [Batch Videos](/batch-videos).
