> ## 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, silent or narrated                   | The clip itself (silent) or the voiceover (narrated) |

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`.                                                                                 |
| `brand_glossary_id` | string | No       | —       | Brand glossary controlling how custom terms are pronounced — see [Brand Glossary](#brand-glossary).                                 |
| `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 a `digital_twin` look in the same avatar group to pin the animation reference; when omitted, video avatars self-reference and photo avatars pick an eligible look from the group. `expressiveness` applies on Avatar IV; `motion_prompt` applies to photo avatars on either engine and to video avatars on Avatar V.

<Note>
  Avatar V needs a usable reference look in the avatar's group, so it is not available for every photo avatar: if the group has none, the request fails with a `400`. A non-public `digital_twin` reference must also clear its group's consent requirements, whether you supplied it or it was selected for you. To see what a group already has, use [`GET /v3/avatars/looks?group_id=...&avatar_type=digital_twin`](/reference/list-avatar-looks) — the group id is the `group_id` field on any look in that group. Filtering by `avatar_type` alone searches your whole workspace and can return a digital twin from a different group, which is rejected. The listing shows candidates rather than guaranteeing eligibility: a non-public group needs [consent](/docs/avatar-consent). A look with `status: processing` (still training) can still be used as a reference.
</Note>

### 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. Like an [image scene](#image-scenes), it plays in one of two modes:

* **Silent** — no audio source. The clip plays full-length at its own duration.
* **Narrated** — set exactly one voiceover source (the same audio inputs a narrated image scene accepts). The voiceover drives the scene's duration, and [`playback.mode`](#playback-and-scene-duration) controls how the clip fills that time — hold the last frame, loop, or adjust speed to fit.

```json theme={null}
{
  "type": "video",
  "source": { "type": "asset_id", "asset_id": "YOUR_ASSET_ID" },
  "script": "Here is the feature in action.",
  "voice_id": "YOUR_VOICE_ID",
  "playback": { "mode": "fit_to_scene", "mute": true }
}
```

| Field            | Type   | Required      | Description                                                                                                                                                                                  |
| ---------------- | ------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`           | string | Yes           | Must be `video`.                                                                                                                                                                             |
| `source`         | object | Yes           | Video clip to include — see [Asset input](#asset-input).                                                                                                                                     |
| `script`         | string | Narrated mode | Voiceover text to speak over the clip. Pair with `voice_id`.                                                                                                                                 |
| `voice_id`       | string | With `script` | Voice for text-to-speech. Browse with [`GET /v3/voices`](/reference/list-voices).                                                                                                            |
| `audio_url`      | string | Narrated mode | Public URL of an audio file to play over the clip.                                                                                                                                           |
| `audio_asset_id` | string | Narrated mode | Asset ID of an uploaded audio file — see [Asset Management](/assets).                                                                                                                        |
| `voice_settings` | object | No            | Voice tuning (speed, pitch, locale) for a `script` voiceover.                                                                                                                                |
| `playback`       | object | No            | Scene-duration alignment and clip audio control — see [Playback and scene duration](#playback-and-scene-duration). Omit to hold the last frame (narrated) and keep the clip's source volume. |

In narrated mode, provide exactly one voiceover source: `script` (with `voice_id`), `audio_url`, or `audio_asset_id`.

#### Playback and scene duration

The `playback` object carries two composable controls: how the clip aligns to a voiceover-driven scene duration, and the clip's own audio level.

| Field    | Type    | Default  | Description                                                                                                                                                                                                                                 |
| -------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode`   | string  | `freeze` | Narrated scenes only — how the clip fills the voiceover-driven duration. `freeze` plays the clip once and holds the last frame; `loop` repeats the clip; `fit_to_scene` adjusts playback speed so the clip ends exactly with the voiceover. |
| `volume` | number  | `1.0`    | Clip audio volume, `0.0`–`1.0`, where `1.0` is the source level.                                                                                                                                                                            |
| `mute`   | boolean | `false`  | Force the clip silent regardless of `volume`.                                                                                                                                                                                               |

`mode` requires a voiceover, since the voiceover is what defines the target scene duration — a silent clip always plays full-length. The voiceover and the clip's own audio mix independently: there is no automatic ducking, so set `playback.volume` (or `mute`) yourself to bring the clip down under narration. With `fit_to_scene`, mute the clip when its source audio shouldn't be speed-adjusted along with the visuals.

## 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. Captions are derived from the speech in [`avatar_video`](#avatar-video-scenes) scenes and narrated [`image`](#image-scenes) scenes. 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. |

## Brand Glossary

A [brand glossary](/docs/brand-glossary) is a set of custom pronunciations, for example speaking `HeyGen` as `hey-jen`. Glossaries are created and edited in the HeyGen web app under Brand Kit. Pass one with the top-level `brand_glossary_id`, a **global** setting that applies to the whole request.

```json theme={null}
{ "brand_glossary_id": "8c2d5e91a47b4f3c8d1e6a9b2f5c7d40" }
```

Find your ids with [`GET /v3/brand-glossaries`](/reference/list-brand-glossaries), and see which terms a glossary remaps with [`GET /v3/brand-glossaries/{brand_glossary_id}`](/reference/get-brand-glossary).

A glossary applies wherever HeyGen generates the speech: an `avatar_video` scene, or an `image` or `video` scene narrated with `script`. Scenes that supply finished audio (`audio_url` or `audio_asset_id`) play unchanged.

> A glossary changes the **spoken audio only**. Captions and the `.srt` sidecar keep the original spelling, so a term written `HeyGen` still reads `HeyGen` on screen while being spoken as `hey-jen`.

An id that does not exist in your workspace returns `400` and no video is created.

## Rendering at Scale

A studio request composes its scenes into a single video, so it is not accepted by the batch endpoint. A `"type": "studio"` item in [`POST /v3/videos/batches`](/reference/create-video-batch) is rejected. Send studio videos one per request to `POST /v3/videos` and track each returned `video_id`.
