Skip to main content

Prerequisites

A Digital Twin avatar_id (type: digital_twin). Use GET /v3/avatars/looks?avatar_type=digital_twin to find yours.
A voice_id for the voice you want. Use GET /v3/voices to browse available voices.

Step 1 — Find your Digital Twin

List your private Digital Twin looks to get the avatar_id:
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 three rendering engines for Digital Twins. You select the engine via the engine object in your video creation request.
Before requesting an engine, check the supported_api_engines array on the avatar look via GET /v3/avatars/looks/{look_id} — it lists which of avatar_iii, avatar_iv, and avatar_v the look accepts, and requesting an engine that isn’t listed will be rejected. See Models for full details on each engine.

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:

Using Avatar V (higher quality)

Pass {"type": "avatar_v"} in the engine field to request cross-reference-driven animation:
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.

Step 4 — Poll for completion

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

Status values

Once completed, the response includes everything you need to download and display the result: These are presigned URLs with a limited expiry window — download promptly, or call GET /v3/videos/{video_id} again to get fresh URLs. When status is failed, the response includes a machine-readable failure_code alongside the human-readable failure_message.

Full example

Optional parameters

Captions and subtitles

Captions are off by default — omit caption and video_url is a clean render with no subtitles. To generate captions, pass a caption object:
This returns a sidecar subtitle file via subtitle_url in the status response, while the video itself stays clean — you control how subtitles display in your own player. To additionally burn captions into the rendered video, set a style:
caption is an object on /v3/videos. Boolean-style fields like caption: true or enable_caption (used by the v2 API and by the template endpoint) fail /v3/videos validation with invalid_parameter (“Extra inputs are not permitted”). Use the object form above.
If a downloaded video shows captions you didn’t ask for, you most likely grabbed captioned_video_url (or the url from a webhook payload that points at the captioned render). Fetch GET /v3/videos/{video_id} and download video_url for the caption-free file.

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.
Register a webhook endpoint via POST /v3/webhooks/endpoints and subscribe to avatar_video.success and avatar_video.fail events for production use.