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

# Digital Twin

> Generate AI avatar video from a script with the HeyGen API. Pick an avatar, voice, and background; HeyGen handles rendering, captions, and final export.

<video controls muted playsInline className="w-full aspect-video rounded-xl" src="https://mintcdn.com/heygen-1fa696a7/xiWXoDBsEfd3CTgQ/images/videos/create-videos.mp4?fit=max&auto=format&n=xiWXoDBsEfd3CTgQ&q=85&s=b8d543425dd12d58cf0acd608ed75b08" title="Digital Twin in 30 seconds — from script to rendered avatar video in four steps" data-path="images/videos/create-videos.mp4" />

## Prerequisites

<Check>
  A Digital Twin `avatar_id` (type: `digital_twin`). Use `GET /v3/avatars/looks?avatar_type=digital_twin` to find yours.
</Check>

<Check>
  A `voice_id` for the voice you want. Use `GET /v3/voices` to browse available voices.
</Check>

## Step 1 — Find your Digital Twin

List your private Digital Twin looks to get the `avatar_id`:

```bash theme={null}
curl -X GET "https://api.heygen.com/v3/avatars/looks?avatar_type=digital_twin&ownership=private" \
  -H "x-api-key: YOUR_API_KEY"
```

From the response, copy the `id` field of the look you want. This is your `avatar_id`.

## Step 2 — Choose a rendering engine

HeyGen v3 supports two rendering engines for Digital Twins. You select the engine via the `engine` object in your video creation request.

| Engine    | Key         | Default?                            | Quality                                          |
| --------- | ----------- | ----------------------------------- | ------------------------------------------------ |
| Avatar IV | `avatar_iv` | Yes — used when `engine` is omitted | Standard, widely supported                       |
| Avatar V  | `avatar_v`  | No — must be explicitly requested   | Higher quality, cross-reference-driven animation |

<Note>
  Before requesting Avatar V, check the `supported_api_engines` array on the avatar look via `GET /v3/avatars/looks/{look_id}`. If `"avatar_v"` is not listed, the request will be rejected. See [Avatar Models](/avatar-models) for full details.
</Note>

## Step 3 — Create the video

Send a `POST` request to `/v3/videos` with `type: "avatar"`, your Digital Twin ID, a script, and a voice.

### Using Avatar IV (default)

Omit the `engine` field or pass `{"type": "avatar_iv"}` — Avatar IV is the default:

```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": "avatar",
    "avatar_id": "YOUR_DIGITAL_TWIN_LOOK_ID",
    "script": "Hello! I am your Digital Twin. This video was generated entirely through the HeyGen API.",
    "voice_id": "YOUR_VOICE_ID",
    "title": "My First Digital Twin Video",
    "resolution": "1080p",
    "aspect_ratio": "auto"
  }'
```

### Using Avatar V (higher quality)

Pass `{"type": "avatar_v"}` in the `engine` field to request cross-reference-driven animation:

```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": "avatar",
    "avatar_id": "YOUR_DIGITAL_TWIN_LOOK_ID",
    "script": "Hello! I am your Digital Twin.",
    "voice_id": "YOUR_VOICE_ID",
    "title": "My Digital Twin — Avatar V",
    "resolution": "1080p",
    "aspect_ratio": "auto",
    "engine": { "type": "avatar_v" }
  }'
```

<Note>
  `motion_prompt` is supported on both Avatar IV and Avatar V — pass a natural-language prompt to control body motion and hand gestures.

  `expressiveness` remains an Avatar IV-only parameter. Do not include it when using Avatar V — it will cause a validation error.
</Note>

## Step 4 — Poll for completion

Video generation is asynchronous. Poll `GET /v3/videos/{video_id}` until `status` is `completed`:

```bash theme={null}
curl -X GET "https://api.heygen.com/v3/videos/YOUR_VIDEO_ID" \
  -H "x-api-key: YOUR_API_KEY"
```

### Status values

| Status       | Meaning                                        |
| ------------ | ---------------------------------------------- |
| `pending`    | Queued for processing                          |
| `processing` | Video is being generated                       |
| `completed`  | Ready — `video_url` is available               |
| `failed`     | Something went wrong — check `failure_message` |

Once completed, the response includes a `video_url` with a presigned download link.

## Full example

```python theme={null}
import requests
import time

API_KEY = "YOUR_API_KEY"
BASE = "https://api.heygen.com"
HEADERS = {"x-api-key": API_KEY, "Content-Type": "application/json"}

# 1. Create the video (Avatar V — swap engine for {"type": "avatar_iv"} or omit for default)
resp = requests.post(f"{BASE}/v3/videos", headers=HEADERS, json={
    "type": "avatar",
    "avatar_id": "YOUR_DIGITAL_TWIN_LOOK_ID",
    "script": "Welcome to our product demo. Let me walk you through the new features.",
    "voice_id": "YOUR_VOICE_ID",
    "resolution": "1080p",
    "aspect_ratio": "auto",
    "engine": {"type": "avatar_v"}  # omit entirely to use Avatar IV (default)
})
video_id = resp.json()["data"]["video_id"]
print(f"Video created: {video_id}")

# 2. Poll until done
while True:
    status_resp = requests.get(f"{BASE}/v3/videos/{video_id}", headers=HEADERS)
    data = status_resp.json()["data"]
    print(f"Status: {data['status']}")
    if data["status"] == "completed":
        print(f"Download: {data['video_url']}")
        break
    elif data["status"] == "failed":
        print(f"Error: {data.get('failure_message')}")
        break
    time.sleep(10)
```

## Optional parameters

| Parameter           | Type    | Engine  | Description                                                                                               |
| ------------------- | ------- | ------- | --------------------------------------------------------------------------------------------------------- |
| `title`             | string  | Both    | Display name in the HeyGen dashboard                                                                      |
| `resolution`        | string  | Both    | `4k`, `1080p` (recommended), or `720p`                                                                    |
| `aspect_ratio`      | string  | Both    | `auto` (recommended — matches the source, falling back to `16:9`), `16:9`, `9:16`, `4:5`, `5:4`, or `1:1` |
| `engine`            | object  | —       | `{"type": "avatar_iv"}` or `{"type": "avatar_v"}`. Defaults to Avatar IV when omitted                     |
| `remove_background` | boolean | Both    | Removes the avatar background (twin must be trained with matting enabled)                                 |
| `background`        | object  | Both    | Set a solid color or image background                                                                     |
| `voice_settings`    | object  | Both    | Adjust `speed` (0.5–1.5), `pitch` (-50 to +50), and `locale`                                              |
| `motion_prompt`     | string  | Both    | Natural-language prompt controlling avatar body motion and hand gestures                                  |
| `expressiveness`    | string  | IV only | `high`, `medium`, or `low`. Defaults to `low`                                                             |
| `output_format`     | string  | Both    | `mp4` (default) or `webm` (transparent background / alpha channel)                                        |
| `callback_url`      | string  | Both    | Webhook URL — receive a POST when the video is ready                                                      |

## Using webhooks instead of polling

Instead of polling, pass a `callback_url` when creating the video. HeyGen will send a POST request to that URL when the video completes or fails.

```json theme={null}
{
  "type": "avatar",
  "avatar_id": "YOUR_DIGITAL_TWIN_LOOK_ID",
  "script": "This video uses a webhook callback.",
  "voice_id": "YOUR_VOICE_ID",
  "callback_url": "https://your-server.com/webhooks/heygen"
}
```

<Note>
  Register a webhook endpoint via `POST /v3/webhooks/endpoints` and subscribe to `avatar_video.success` and `avatar_video.fail` events for production use.
</Note>
