The Assets API lets you upload files to HeyGen and receive an asset_id you can reference in other endpoints — including Video Agent, Avatar creation, Video Translation, and Lipsync.
There are two ways to upload:
POST /v3/assets— a singlemultipart/form-datarequest. Simplest option, capped at 32 MB.- Direct upload — a presigned-URL flow for files larger than 32 MB, up to 200 MB per video, image, or PDF file and 1.5 GB per audio file. The file bytes go straight to storage and never pass through the API.
Upload via POST /v3/assets
Full schema: POST /v3/assets. Upload a file using multipart/form-data — the MIME type is auto-detected from file bytes.
Constraints
Example request
Response
Upload large files (direct upload)
For files larger than 32 MB, use the direct upload flow. Direct upload accepts files up to 200 MB (209,715,200 bytes) for video, image, and PDF, and up to 1.5 GB for audio. The limit for your file type is also returned asmax_bytes. It is a three-step process — all three steps are required; the asset_id is not usable until you call the complete endpoint:
1
Initialize the upload
Call
POST /v3/assets/direct-uploads with the file’s name, MIME type, and exact byte size. The response contains an asset_id, a presigned upload_url, and upload_headers.2
PUT the file bytes
Send the raw file bytes to
upload_url with an HTTP PUT, including every header from upload_headers verbatim. The URL expires after expires_in_seconds, and the byte size is signed into it — the upload fails if the file doesn’t match the declared size_bytes.3
Complete the upload
Call
POST /v3/assets/{asset_id}/complete to finalize the asset. This step is idempotent — repeated calls return the same finalized asset. The asset_id is now usable anywhere the API accepts assets. The complete response returns status: "processing". You do not need to wait for it to change; pass the asset_id to the next request (for example POST /v3/avatars) right away.Example
Initialize response fields
Calling complete before the
PUT has finished returns a 409 conflict (“Uploaded object not found yet”). Retry after the PUT returns 200. You can optionally pass checksum_sha256 (hex) at both the initialize and complete steps to have the stored bytes verified end to end.Use assets in Video Agent
Once uploaded, reference theasset_id in the files array when creating a video:
Three ways to provide files
Video Agent and other endpoints accept files in three formats. Use whichever is most convenient for your workflow:Asset ID
Upload once, reference by ID. Best for files you reuse across multiple videos.
HTTPS URL
Point to a publicly accessible HTTPS URL. No upload step needed, HeyGen fetches the file directly. Limits: 16 MB for images, 32 MB for video, audio, and PDF.
Base64
Inline the file content as a base64-encoded string. Useful for small files or when you want a self-contained request.
Format comparison
Size limits by input
Signed URLs (for example S3 or Google Cloud Storage presigned URLs) work like any other
url input and have the same size limits. Signing a URL does not raise the limit. The URL must use HTTPS, be downloadable without extra headers, return the file directly (redirects are not followed) with a Content-Type header, and still be valid when HeyGen fetches it. For files over the URL limit, or when you cannot guarantee the link stays valid, use direct upload and pass the asset_id.Where assets can be used
Theasset_id format is accepted anywhere the API takes file inputs:
Asset lifetime and deletion
Anasset_id does not expire. It stays usable in any request until you delete it, so upload a file once and reuse the same asset_id on every retry, for example when you retry POST /v3/avatars or resubmit a consent video with POST /v3/avatars/{group_id}/consent after a rejected review. You do not need to upload the file again.
Only the presigned upload_url from direct upload is time-limited (expires_in_seconds, 24 hours). The asset_id it returns becomes usable after the complete step and then follows the same rule.
Delete an asset with DELETE /v3/assets/{asset_id}:
curl
asset_id returns asset_not_found in later requests.
Assets are separate from the avatars, videos, and translations you create from them. Deleting an avatar group with DELETE /v3/avatars/{group_id} removes the avatar and all its looks, but not the files you uploaded to create it, and deleting an asset does not affect avatars or videos already made from it. To remove an uploaded training footage or consent video file, call DELETE /v3/assets/{asset_id} for each asset_id.
Files you pass to other endpoints as a url or base64 are also saved as assets in your workspace, but those calls do not return the new asset_id. If you will need to delete a source file later, upload it here first and pass its asset_id.

