2xx range indicate success. Codes in the 4xx range indicate an error with the information provided (e.g., a missing parameter, insufficient credits, or a resource not found). Codes in the 5xx range indicate an error on HeyGen’s servers.
Every error response includes a machine-readable code, a human-readable message, a doc_url linking to the relevant section below, and a request_id. Some errors that relate to a specific request field also include a param attribute.
The request_id is also returned in the X-HeyGen-Request-Id response header on every API response, successful or not, including errors that have no JSON body; a browser’s CORS preflight (OPTIONS) is answered without one. Include it when contacting support, and use it to find the call in the dashboard’s request logs. On a successful text-to-speech or speech-to-speech response under /v3/models/audio/, the X-Request-Id header and any request_id in the body identify the generated audio, not the call; search by X-HeyGen-Request-Id.
Error response format
HTTP status code summary
Error codes
unauthorized
HTTP status: 401
The API key provided is invalid, expired, or missing. Verify that you are sending your API key in the X-Api-Key header and that the key is active in your API keys page.
api_key_owner_workspace_access_revoked
HTTP status: 401
The API key’s owner no longer has active access to the workspace the key belongs to, or that workspace was deleted. For an active workspace, ask its admin to restore the owner’s membership. For a deleted workspace (including a personal workspace), use a key for an active workspace. Restoring active membership in an active workspace lets the same key work again; retrying or refreshing the credential without restoring access will not help.
forbidden
HTTP status: 403
The request is authenticated but cannot use the current workspace or action. This can happen when the workspace session expires or the caller lacks access. Reauthenticate if the session expired; otherwise use a workspace or role with the required access.
It is also returned when a voice provider rejects the API key of a voice provider account you connected, because the key is invalid or the account is inactive. Update the API key in your settings.
insufficient_api_key_scope
HTTP status: 403
The API key is valid but its permission scopes don’t cover this endpoint. Restricted API keys are only authorized for the resources and access levels (read/write) explicitly granted when the key was created. Check the key’s configured scopes in your API keys page, or use a key with broader access. The message names the scopes the endpoint requires, so it can be compared against the key’s grants directly.
api_key_required
HTTP status: 403
The request was made with an OAuth token whose client can’t pay for this endpoint. Either the endpoint charges only API credits, which only a HeyGen API key can spend, or the client has no wallet it can pay from. Send the request with a HeyGen API key in the X-Api-Key header. A request that carries both a key and a token is treated as the key. Nothing was created or charged.
resource_access_denied
HTTP status: 403
The authenticated user does not have access to the specific resource referenced in the request. The resource may belong to a different user or organization. Verify that the resource ID is correct and belongs to your account.
encryption_setup_pending
HTTP status: 409
The workspace was created with a customer-managed encryption key (BYOK) and that key has not yet been registered, validated and activated. Until it is, the workspace refuses every media write — uploads, generation, renders — so that nothing it ever holds is outside the customer’s key. A workspace administrator completes the setup from Settings → Workspace Controls → Encryption, or HeyGen completes it on the customer’s behalf.
encryption_disabled
HTTP status: 403
The workspace’s customer-managed encryption cannot be used for this request: it has been stood down, or the workspace is marked for customer-managed encryption and its key record cannot be resolved. Either way the media write is refused rather than falling back to a HeyGen-managed key.
Standing it down is deliberate and one-way, and it leaves the workspace unable to store media: its existing objects are under a key it would no longer use, and moving them out is a separate migration that does not exist yet. So this is not a way to return a workspace to ordinary shared storage — a customer who wants customer-managed encryption again is provisioned a new workspace, and support should treat a workspace in this state as one that needs a decision rather than a retry.
This is also not the error for a key the customer has disabled in their own AWS account. That needs no change on this side: the operation fails because AWS refuses it, and uploads and playback resume on their own once the key is usable again.
encryption_cross_tree_transfer
HTTP status: 409
Content cannot be moved or copied between a BYOK space and any other workspace, in either direction. Moves re-point database rows at the same stored object rather than copying bytes, so a cross-BYOK move would leave media outside the customer’s key, or put another customer’s media under it. Moves within one BYOK space are unaffected.
invalid_encryption_key
HTTP status: 400
The supplied KMS key reference was rejected. It must be a full key ARN (not an alias or bare key id, which would resolve inside HeyGen’s account rather than the customer’s), for a symmetric ENCRYPT_DECRYPT key in us-east-2.
encryption_setup_failed
HTTP status: 409
Provisioning the workspace’s encrypted bucket, or the validation round trip against it, did not complete. The key itself may be fine: this is the bucket refusing to be configured, an AWS error during setup, or a check in the validation report coming back red. The response carries the per-step or per-check report; act on the first failed entry. Nothing was activated. A provisioning failure during key registration means the key was not recorded; during activation the key stays recorded and the BYOK space stays awaiting_key.
encryption_temporarily_unavailable
HTTP status: 503
The service could not tell whether this workspace’s media must be stored under a customer-managed key, so the media was not stored. Nothing is wrong with the workspace or its key: the record that answers the question could not be read. Retry with backoff.
byok_not_supported
HTTP status: 403
The feature is not available for encrypted workspaces (customer-managed encryption, BYOK). White Glove, Cut Room and composition sync keep a workspace’s media where the customer’s key cannot follow it, so they refuse a BYOK workspace or any workspace under one. Retrying does not help; use the HeyGen web app for this workspace.
hyperframes_composition_sync_not_enabled
HTTP status: 403
Composition sync is not yet enabled for this workspace, so a new composition cannot be created in it and a new upload cannot begin. Compositions that already exist in the workspace can still be read and listed, repeating a create for one of them still returns it unchanged, and repeating a Begin with the same Idempotency-Key and body still returns its upload. This is the workspace’s rollout state, not a transient failure: when the setting cannot be read, the answer is service_unavailable (503) instead. Keep the project local and retry later, or contact support to have the workspace enabled.
output_upload_not_enabled
HTTP status: 403
The request included output_upload (or output_uploads), but uploading finished files to your own storage is not yet enabled for this workspace. Nothing was created. Send the request without the field and download the result from the video’s url, or contact support to have the workspace enabled.
hyperframes_composition_sync_limit_reached
HTTP status: 403
The workspace already has as many synced projects (live compositions) as its HyperFrames plan allows, so a new composition cannot be created in it. The message states the workspace’s count and its maximum; GET /v3/hyperframes/workspaces/{owner_space_id}/composition-sync/limits returns the same numbers as limits.max_compositions and usage.compositions, with plan_name. Pushes to compositions that already exist are never refused for the count, and repeating a create for one of them still returns it unchanged. Remove a project from the cloud to free a slot, then create the new one.
ai_vendor_access_restricted
HTTP status: 403
The workspace has restricted which AI vendor companies may be used. The action or model you requested relies on a vendor that is not allowed under the workspace’s AI vendor access policy. Ask a workspace administrator to update the policy if this vendor should be permitted.
brand_kit_locked
HTTP status: 403
The workspace’s brand policy is locked (an enterprise governance setting): only the workspace default brand kit may be applied, and the request named a different brand_kit_id. Pass the default brand kit id (included in the error message) or omit brand_kit_id entirely — the workspace default is applied automatically. Ask a workspace administrator to pin a different default or unlock the brand policy if another kit is needed.
voice_not_usable
HTTP status: 403
The voice referenced in the request cannot currently be used to generate this video. The voice is in a state that blocks generation and will not resolve on retry. Select a different voice.
This code is also returned when the upstream voice provider restricts the voice: it is only offered on a higher tier of the provider’s own plan, or the provider has suspended access to it. Upgrading your HeyGen plan does not change this. If you connected your own voice provider account, the error message names the provider tier the voice requires; otherwise, choose a different voice.
It is also returned when the voice provider has the voice but will not generate speech with it, for example because the voice is disabled, needs verification, has not been fine-tuned or has invalid samples at the provider, or because the provider returned no audio, which usually means the voice does not support the language of the text. Choose a different voice. If the voice is in your own connected ElevenLabs account, the error message says how to fix a disabled, unverified or not fine-tuned voice in that account: enable, verify or regenerate it.
voice_clone_terms_violation
HTTP status: 403
The recording was judged a possible violation of the terms of service while it was being cloned. A new voice clone that fails this way fails as a whole: the voice is not created on any voice engine, and retrying the same request fails the same way. Each recording is judged on its own, so a clone from a different recording can succeed.
For a voice that already exists, the code means one voice engine refused it on the same grounds; choose a different voice engine for this voice, or a different voice.
phone_verification_required
HTTP status: 403
The account associated with this request must complete phone (SMS) verification before this request can proceed. This is an anti-abuse safeguard that can apply to any generation feature (video, voice, avatar, and related operations). This also applies during API-key authentication, including requests that do not consume credits. Sign in to HeyGen and complete phone verification, then retry the request.
Older HeyGen app endpoints may return the numeric code 400193 for the same requirement.
rate_limit_exceeded
HTTP status: 429
You are sending requests too frequently. Back off and retry with exponential backoff. Check the Retry-After response header for the number of seconds to wait before retrying. See Usage Limits for concurrency and per-endpoint limits; Enterprise workspaces can raise effective concurrency with burst.
The service also returns this code when a voice provider limits speech or voice requests. This response has no Retry-After header. Retry the request with exponential backoff.
High demand can exceed the available capacity for speech generation. This applies to /v3/models/audio/tts and /v3/models/audio/tts/stream. The service then returns HTTP 429 with code=rate_limit_exceeded. This response has no Retry-After header. Retry the request with exponential backoff.
For streaming, the service returns HTTP 429 only before the response starts. If an SSE stream fails after this point, the service sends an error event. If a raw audio stream fails after this point, the service closes the connection.
api_endpoint_disabled
HTTP status: 403
This endpoint is not available for your account. Contact support.
quota_exceeded
HTTP status: 429
You have exceeded a usage quota (e.g., the free-tier limit for video agent or AI clip requests). Upgrade your plan or wait for your quota to reset. Check your current usage in the HeyGen dashboard.
insufficient_credit
HTTP status: 402
Your account does not have enough credits to complete this request. The error message includes how many credits you have and how many are required. Purchase additional credits or reduce the scope of your request (e.g., shorter video duration, fewer scenes).
usage_limit_reached
HTTP status: 402
A usage limit that a workspace administrator set was reached. Buying more credits does not remove it. There are two cases:
- A workspace member’s usage limit. The member who sent the request reached the usage limit set for them in the workspace. Ask a workspace administrator to check the limit, or wait for it to reset.
- An API key’s credit limit. The API key that sent the request reached the API-credit limit a workspace admin set on it. The message names the limit in credits and its window, either the calendar month (UTC) or the last 30 days, and says that a workspace admin can raise it. For a calendar-month limit it also gives the time the limit resets, 00:00 UTC on the 1st of the next month. A last-30-days limit frees up as older spend leaves the window. A regenerated key shares its predecessor’s limit. It is a soft limit: requests already running when it is reached still complete, so spend can end slightly above it.
402 with this code. Some jobs are charged only once their work is done, and the limit can be reached in between. Video Agent videos, HyperFrames renders, and /v3/models videos and images refused at that charge fail as a usage limit, and their status and webhook carry the limit’s message. Avatar videos and video translations refused at that charge fail through their usual failure path, with no dedicated failure code; a video translation records the refusal’s status code (400837). Other jobs charged at completion (third-party video generations, video and audio analysis, music and transcription) still report such a refusal as insufficient_credit. The refused charge is never taken.
trial_limit_exceeded
HTTP status: 402
You have reached the video generation limit for trial accounts. Upgrade to a paid plan to continue creating videos.
subscription_required
HTTP status: 402
You have used all the credits included in your pay-as-you-go trial, and further top-ups are not available on your account. Choose a plan to continue making API requests — see pricing.
This is distinct from insufficient_credit: buying more credits will not resolve it, because purchases are no longer available. Subscribing to a plan is the only action that restores API access.
hf_subscription_required
HTTP status: 402
HyperFrames Desktop asked to pay with the workspace’s HyperFrames plan, and the workspace has no HyperFrames plan or usage. Choose a HyperFrames plan in HyperFrames Desktop, or send the request with a HeyGen API key in the X-Api-Key header. Nothing was created or charged.
Composition sync answers it too: creating a composition or beginning an upload in a workspace whose paying workspace has no paid HyperFrames plan (Pro or Max). Compositions that already exist can still be read, pulled, downloaded and have their conflicts discarded, and repeating a create or Begin that already happened returns it unchanged. Choose a HyperFrames plan to sync.
hf_usage_exhausted
HTTP status: 402
HyperFrames Desktop asked to pay with the workspace’s HyperFrames plan, and the plan’s usage left does not cover this request. Wait for the plan to renew, change the plan in HyperFrames Desktop, or send the request with a HeyGen API key in the X-Api-Key header. Nothing was created or charged.
plan_upgrade_required
HTTP status: 402
The requested feature or resource requires a higher subscription tier than your current plan. This can occur when:
- Using a premium avatar that is not available on your plan.
- Accessing an integration that requires a higher tier.
- Requesting a resolution or feature gated by plan level.
resolution_not_allowed
HTTP status: 400
The requested video resolution exceeds the resolution available on your current plan. Avatar, image, and studio OAuth video creation check this before accepting the generation request. When resolution is omitted, avatar and image videos use 720p, while studio uses 1080p.
Choose a lower resolution or a plan that supports the requested resolution. Continue on HeyGen to learn more. Retrying the same request will not resolve the restriction. API-key video creation retains its existing resolution access.
invalid_upload_url
HTTP status: 400
An upload URL in output_upload or output_uploads can’t be used, and param names the field. Nothing was created. The URL must:
- be an
httpspresigned PUT URL for Amazon S3, Google Cloud Storage or Azure Blob Storage, on the default port; - carry its signature and expiry (
X-Amz-Expires,X-Goog-Expires, or an Azure SASsethat grants create or write); - stay valid for at least one hour after the request;
- be used for only one file.
Content-Type, Content-MD5, Content-Disposition, Content-Encoding, Content-Language, Cache-Control or x-amz-* / x-goog-* / x-ms-*. Sign a fresh URL with a longer lifetime (24 hours or more is recommended) and send the request again.
video_not_found
HTTP status: 404
No video, draft, or video translation was found matching the provided ID. Verify that:
- The
video_idis correct and was not mistyped. - The video has not been deleted.
- The video belongs to your account.
avatar_not_found
HTTP status: 404
No avatar was found matching the provided ID. This applies to all avatar types — standard avatars, photo avatars (photars), instant avatars, and avatar kits. Verify that:
- The
avatar_idis correct. - The avatar has finished training (if recently created).
- The avatar belongs to your account or is a public avatar.
voice_not_found
HTTP status: 404
No voice was found matching the provided ID. Verify that the voice_id is correct and that the voice is available in your account. If using a cloned voice, ensure it has finished processing.
The v3 TTS endpoints return 400 invalid_parameter with param=voice_id for missing or inaccessible voices.
template_not_found
HTTP status: 404
No template was found matching the provided ID. Verify that the template_id is correct and that the template is shared with your account or is publicly available.
asset_not_found
HTTP status: 404
No asset was found matching the provided ID. Assets may have been deleted or may not have finished uploading. Verify that the asset_id was returned from a successful POST /v3/assets call and that the asset has not been removed.
webhook_not_found
HTTP status: 404
No webhook endpoint was found matching the provided ID. Verify that the endpoint_id is correct and that the webhook has not been deleted. List your existing webhooks with GET /v3/webhooks/endpoints to find valid endpoint IDs.
batch_not_found
HTTP status: 404
No batch was found matching the provided ID. Verify that the batch_id is correct and that the batch belongs to your account.
hyperframes_composition_not_found
HTTP status: 404
No composition with this ID is available to you in this workspace. The same answer covers a composition that does not exist, one that was deleted, one that belongs to another workspace, and any composition in a workspace you are not a current member of (or that your OAuth token is not bound to). Check the workspace and composition IDs and that you are still an active member of that workspace. Retrying the same request does not help.
It also covers what is named under a composition. A snapshot ID that is not one of the composition’s answers exactly as a missing composition does, message included. An upload ID (…/compositions/{composition_id}/uploads/{upload_id}) that does not exist, belongs to another composition, or, on upload URLs, cancel and submit, belongs to someone else, answers with the message “Upload or composition not found”. Start a new upload with Begin rather than retrying the old ID.
resource_not_found
HTTP status: 404
The requested resource was not found. This is a generic not-found error for resources that do not have a more specific error code (e.g., streaming sessions, audio records). Verify that the resource ID is correct and belongs to your account.
It is also returned when a voice from a voice provider account you connected no longer exists in that account. Restore or re-import the voice in that account, or choose a different voice.
endpoint_retired
HTTP status: 410
This v1 or v2 endpoint has been retired. The error message names the v3 endpoint to call instead, or links the v3 API reference when there is no direct replacement. Retrying will not succeed.
voice_not_cloneable
HTTP status: 400
The selected voice is provider-owned and cannot be cloned, duplicated, improved, or changed to a different provider. Use the voice with one of its listed allowed_engines, or choose a cloneable voice for mutation operations.
context_length_exceeded
HTTP status: 400
The formatted decision prompt plus its answer token exceeds the server’s context
limit. Shorten the state, question, or criteria. The message includes the prompt’s
token count and applicable limit. Quality has a 131,072-token API cap on every
GPU; Turbo has a 262,144-token API cap. A lower engine limit still applies.
The request is not charged.
invalid_parameter
HTTP status: 400
One or more request parameters are invalid, missing, or in the wrong format. The message field describes which parameter failed validation and why. The param field, when present, identifies the specific field.
Common causes:
- A required field is missing from the request body.
- A field value is the wrong type (e.g., string instead of number).
- A field value is outside the allowed range or not in the set of accepted values.
- The request body is not valid JSON or is not a JSON object.
POST /v3/videos/{video_id}/deliverieswas called for a video whose render failed, so it has no files to deliver. Retrying will not change this.
graph_invalid
HTTP status: 422
The workflow product graph is structurally well-formed JSON but semantically invalid, so it cannot be saved as submitted or compiled into a version. Distinct from invalid_parameter (400), which means the request body itself is malformed; a graph_invalid response means the graph parsed but violates the workflow contract.
The errors[] array lists every problem found in one pass — validation never stops at the first issue. Each entry carries a stable code (for example unknown_node_type, node_type_unavailable, invalid_config, dangling_dependency, cycle_detected), a human-readable message, and where applicable the offending node_id, port, and path (a field path such as config.prompt or outputs.result). Fix every listed issue and resubmit; use GET /v3/workflows/node-types to discover the node types and config schemas available for authoring.
conflict
HTTP status: 409
The request conflicts with existing state. For example, attempting to create a webhook endpoint with a URL that is already registered for your account, or creating a brand glossary whose name another glossary in your workspace already uses (names are compared without regard to case). On brand glossary writes this also covers a term that appears more than once within any one of the terms, do_not_translate_terms, or forced_translations lists in the request body (terms are compared without regard to case). Use a different value or delete the existing resource first.
A brand glossary update can also conflict with a concurrent edit. The update is rolled back; fetch the latest glossary, reapply your changes and retry.
A video redelivery (POST /v3/videos/{video_id}/deliveries) conflicts when another redelivery of the same video staged the same file at the same moment. Nothing from the conflicting request is kept and no upload starts for it; retry it.
On the workflow authoring endpoints and composition sync upload Begin (POST /v3/hyperframes/workspaces/{owner_space_id}/compositions/{composition_id}/uploads) this also covers Idempotency-Key reuse: sending a key that was already used with a different request body or target returns this code instead of replaying the original response. On Edit Video (POST /v3/videos/{video_id}) it covers a key already used on the same video with a different request body; the same key sent for another video is a separate request and is not refused. Send a new key for a new request.
brand_kit_not_ready
HTTP status: 409
The brand kit is still being assembled, or its import failed, so its role assignments cannot be edited yet. A brand kit imported from a website gathers its colors, logos and fonts in the background; until status is completed the assets a role would name may not exist. Poll GET /v3/brand-kits/{brand_kit_id} until status is completed, then retry. Renaming a kit is allowed at any time and never returns this.
voice_not_ready
HTTP status: 409
The voice is not ready yet: a professional voice clone (PVC) is still training, or an instant voice is still being created. Speech cannot use it. An instant voice is ready within a few seconds; training takes minutes. Retry after a short delay, using Retry-After when the response has one.
voice_training_failed
HTTP status: 409
Training for this professional voice clone (PVC) ended in a terminal failure, so the voice cannot serve inference. Retrying the stream request will not succeed; contact support to retrain or replace the voice.
stale_workflow_draft
HTTP status: 409
Returned by PATCH /v3/workflows/{workflow_id} when the workflow draft moved before your write landed: the expected_revision you sent no longer matches. Nothing was written. Re-read the workflow to pick up the current draft_revision and reapply your change; retrying the same request unchanged will keep failing. Publishing (POST .../versions) no longer surfaces this code — its graph rides the request body, so a concurrent draft edit cannot invalidate it.
workflow_archived
HTTP status: 409
The workflow definition is archived, so it cannot be published or modified. This is terminal for that definition — retrying will not help. Create a new workflow instead.
template_update_in_progress
HTTP status: 409
Another write to this template is still being applied. Writes to one template, from the API or from the HeyGen editor, are serialized so two of them cannot interleave. The template row is only written by the writer that holds the lock; the editor keeps its lock alive while it stores element metadata, and a save that lost the lock is refused before it writes the row. Wait a moment and retry; if you use expected_edit_version, re-read the template first.
workflow_run_admission_refused
HTTP status: 409
The workflow run was created, but its coordinator workflow was authoritatively refused by workflow scheduling — its inputs failed scheduling validation. This is terminal for the run: retrying the same request will not help. Check the run’s status for details, or contact HeyGen support with the run id.
resource_not_ready
HTTP status: 409
The requested resource exists but is not yet in a ready state. This can occur when a video translation is still processing, an instant avatar has not finished training, or an existing reference image does not yet have its stored object. Poll the resource or asset status and retry once it reaches a ready state. Also returned when the uploaded object is not yet present in storage (e.g. POST /v3/assets/{asset_id}/complete called before the upload PUT landed), or while /complete is still checking the uploaded file, which it waits on for a few seconds before answering; safe to retry. Also returned by POST /v3/videos/{video_id}/deliveries while the video is still rendering. Edit Video (POST /v3/videos/{video_id}) returns it while the video is rendering, while another edit of the same video is being applied, or when the edit’s hold on the video lapsed before it saved; nothing is changed, so retry once the other edit or the render finishes. Missing or deleted reference images return asset_not_found instead.
image_processing_failed
HTTP status: 422
The referenced image exists but its processing has permanently ended without producing a usable result (for example, image processing failed, or it completed without generating a thumbnail), so the requested operation cannot be performed. Unlike resource_not_ready, this is terminal — retrying or polling will not change the outcome. Upload a new image and retry with it.
artifact_unavailable
HTTP status: 409
The requested artifact exists but permanently failed to resolve to a durable storage location, so no download URL can be issued. Unlike resource_not_ready, this is terminal — retrying or polling will not change the outcome. Inspect the artifact’s failure field for the reason.
hyperframes_composition_snapshot_not_downloadable
HTTP status: 409
POST /v3/hyperframes/workspaces/{owner_space_id}/compositions/{composition_id}/download-urls named a snapshot that is not the composition’s latest snapshot, an open conflict, or the base snapshot of an open conflict (the base_snapshot_id an open conflict was edited from, which a client merging that conflict needs), so a new download cannot start from it. Other older publications are history and are not downloadable. Read the composition again with GET /v3/hyperframes/workspaces/{owner_space_id}/compositions/{composition_id} and download its latest_snapshot_id, or an open conflict (or its base_snapshot_id) from GET …/snapshots?conflict_state=open. A base stops being downloadable once its last open conflict is resolved or discarded. A download already under way continues with its continuation_token even after latest moves.
hyperframes_composition_conflict_not_open
HTTP status: 409
POST /v3/hyperframes/workspaces/{owner_space_id}/compositions/{composition_id}/conflicts/{snapshot_id}/discard named a snapshot that is a publication or a conflict already resolved by a later push, so there is nothing to discard. Discarding a conflict that is already discarded succeeds and returns it unchanged. No files are deleted either way.
hyperframes_composition_download_expired
HTTP status: 410
The download’s continuation_token is past its expiry, at most 24 hours after the download started. Start a new download from the composition’s latest snapshot, an open conflict, or the base snapshot of an open conflict; already downloaded files with matching SHA-256 digests need not be fetched again.
hyperframes_composition_upload_reference_invalid
HTTP status: 409
POST /v3/hyperframes/workspaces/{owner_space_id}/compositions/{composition_id}/uploads named a base_snapshot_id that is not a published snapshot of this composition, a resolves_conflict_snapshot_ids entry that is not one of its open conflicts, or no base for a composition that already has a latest snapshot. param names the field. Read the composition and its open conflicts again, pull if latest moved, and begin a new push with a new Idempotency-Key.
hyperframes_composition_upload_not_pending
HTTP status: 409
The upload is no longer waiting for files, so this call cannot act on it: upload URLs, submission and cancel need a pending upload before its upload_deadline. A submitted (verifying) upload finishes on its own; read GET …/uploads/{upload_id} for its result. An expired, cancelled or failed upload is final; begin a new upload. Submitting an upload that was already accepted, or cancelling one that was already cancelled, succeeds again instead of returning this code.
hyperframes_composition_active_uploads_exceeded
HTTP status: 409
You already have the maximum number of uploads of this composition pending or verifying. Submit or cancel one of them (POST …/uploads/{upload_id}/verification or …/cancel), or wait for it to finish or expire, then begin again.
hyperframes_composition_upload_file_not_writable
HTTP status: 409
POST …/uploads/{upload_id}/upload-urls cannot sign parts for the file param names (files.{i}), and never will in this upload. A file over 64 MiB is written through one multipart upload, fixed at its first grant and never replaced; that upload is no longer open (it was completed, aborted or expired), or it was set up for a different part size, which a deployment changing between grants can cause. The upload itself is still pending. Cancel it (POST …/uploads/{upload_id}/cancel) and begin a new upload, which starts every file afresh. A refresh that sends a multipart_upload_id is answered invalid_parameter on files.{i}.multipart_upload_id instead, because the id may simply be wrong: send the request again without it, and this code follows if the file’s upload is gone.
context_limit_exceeded
HTTP status: 409
The Video Agent session’s conversation has grown too long for the agent to continue, so the message was not sent. This is terminal for the session: retrying, shortening the message or waiting will not help. GET /v3/video-agents/{session_id} reports the same condition as an error with this code. Start a new session with POST /v3/video-agents and carry over the brief and any assets you still need; videos the session already produced stay available.
stale_edit_version
HTTP status: 409
The Video Agent draft changed after the caller read its scenes, so the supplied edit_version no longer identifies the current scene graph. No edit was submitted. Fetch GET /v3/videos/{video_id}/scenes again, choose scene IDs from that response, and retry with its new edit_version.
On PUT /v3/templates/{template_id}/variables, the expected_edit_version you sent does not match the template’s current edit_version, so something changed the template after you last read it. Every write changes it, whether from another API client or from a save in the HeyGen editor, and the write itself is refused if the template moved between your read and your write. Re-read the template with GET /v3/templates/{template_id}, rebuild your variable set from the current state, and send it again with the new edit_version.
request_in_progress
HTTP status: 409
A prior request with this Idempotency-Key is still in progress. Wait for the original request to complete and retry. Once the original request finishes, subsequent retries with the same key within 24 hours replay the original response.
content_policy_violation
HTTP status: 400
The request was rejected for violating HeyGen’s content policy. This can occur when an instant avatar does not pass the moderation review, when submitted content contains inappropriate content, or when an uploaded image/video asset that was flagged by moderation is used in a request. Create a new resource (or upload a new asset) that complies with our usage policy.
For POST /v3/models/videos, an explicit prompt moderation rejection stops captioning and video generation when enforcement is enabled. Revise the prompt before retrying. With enforcement disabled, violations are recorded and generation proceeds. Moderation errors and timeouts currently allow generation to proceed in either mode.
This code is also returned as a look’s error.code (with status: failed) when a look generated from a prompt fails because the image provider’s content safety check refused the prompt. Retrying the same prompt fails the same way, so change the prompt. No credits are used for the refused generation.
It is also returned when the voice provider refuses a speech request under its own content policy, either for the text or for the selected voice. Retrying the same request fails the same way, so change the text or choose a different voice.
avatar_not_usable
HTTP status: 400
The requested avatar cannot be used because it did not pass content moderation or its creation failed. Select a different avatar; the error message identifies which condition occurred.
unlimited_mode_disabled
HTTP status: 400
The avatar does not support unlimited mode. Use a different avatar, or use Avatar IV or Avatar V.
scenes_unavailable
HTTP status: 400
This video’s scenes cannot be returned. Not every video has a scene representation, and there is no fix on the caller’s side — re-create the video through the API if you need its scenes.
video_not_editable
HTTP status: 400
This video cannot be edited through the API. It belongs to a Video Agent session, it is interactive or branching, it is a type the API cannot edit yet, or it uses avatars the workspace can no longer use or voices that no longer exist. The message names the reason, and an alternative where there is one. Nothing was changed.
edit_not_supported
HTTP status: 400
The request changes a field or uses an action that cannot be edited through the API yet. param names the part of the request at fault. Remove it and send the rest. Nothing was changed.
template_source_unsupported
HTTP status: 400
The video passed to POST /v3/templates cannot become a template. Either it has no editable draft in the current editor format (videos rendered before the current editor, or whose draft was never saved), or it was produced by a flow that does not keep an editable draft, such as URL to Video. Create the template from a video generated with POST /v3/videos or saved in the HeyGen editor. For an older editor video, open it in the editor and save it again, then retry.
variable_match_not_found
HTTP status: 400
A text variable in PUT /v3/templates/{template_id}/variables was declared with match, but that exact text does not occur in the template’s script or on-screen text (or not in the element_ids you limited it to). Check the current text in GET /v3/templates/{template_id} under elements[].current.text; matching is literal and case-sensitive. If the template already contains {{name}} placeholders for this variable, declare it without match.
variable_match_spans_styles
HTTP status: 400
The match text for a text variable occurs in a rich-text element only across differently styled runs (for example, half bold and half regular), so a single placeholder cannot replace it without losing styling. Shorten match to text inside one styled run, or make the styling uniform in the HeyGen editor and retry.
avatar_consent_required
HTTP status: 400
The avatar group used in the request requires consent before it can be used to generate a video (its instant-avatar consent was skipped, rejected, or never completed). Complete the consent flow for the avatar group (e.g. POST /v3/avatars/{group_id}/consent), then retry the request.
avatar_expired
HTTP status: 400
The digital twin used in the request has expired because the workspace exceeded its avatar slot limit — typically after an avatar-slot add-on lapsed or the plan was downgraded. Existing videos are unaffected, but the expired digital twin cannot be used to generate new videos. Renew or purchase avatar slots, or delete other digital twins to bring the workspace back within its limit; the digital twin becomes usable again immediately.
voice_expired
HTTP status: 400
The professional voice clone used in the request has expired because the workspace exceeded its professional voice clone slot limit — typically after a voice clone add-on lapsed or its quantity was reduced. The voice and its data are untouched, but it cannot be used to generate new speech. Purchase more professional voice clone slots, or delete other professional voice clones to bring the workspace back within its limit; the voice becomes usable again immediately.
resource_limit_reached
HTTP status: 400
You have reached the maximum number of a resource allowed for your account (e.g., voice clone slots, instant avatar redo attempts, verified avatar group slots). Delete unused resources to free up capacity, wait for limits to reset, or contact HeyGen support to request a higher limit.
Also returned when an account reaches the number of voice clones its plan includes. Every plan has a number; higher plans include more. The web and API clone pools are shared, so a clone created on either surface counts toward the same allowance, and voices created as part of avatar creation do not count against a paid plan’s allowance. Deleting a clone frees a slot; upgrading raises the limit where a higher plan exists.
template_limit_reached
HTTP status: 400
The workspace already holds the maximum number of templates (2000), so POST /v3/templates cannot create another one. Delete templates you no longer need with the HeyGen editor’s template library and retry. The limit is per workspace and shared with templates created in the web app.
role_not_allowed
HTTP status: 400
The requested space member role is no longer available and cannot be assigned. The viewer role has been retired; assign a different role (for example creator) instead. Distinct from invalid_parameter, which means the request was malformed — here the request is well-formed but names a role the workspace no longer offers.
voice_unavailable
HTTP status: 400
The requested voice exists but is not in a usable state. This occurs when a cloned voice failed processing, expired, or was canceled. Delete the voice and create a new voice clone, or use a different voice.
voice_settings_not_supported
HTTP status: 400
The voice is valid, but the requested voice-and-engine combination cannot be honored. Unlike voice_unavailable (the voice itself is in a broken state) and invalid_parameter (the request was malformed), the request is well-formed but this voice cannot run under the selected engine or configuration. Common causes:
- The selected engine needs a source clip for the voice and none could be prepared.
- A third-party (bring-your-own-key) voice was requested on an engine that only runs on HeyGen’s house account.
- The engine configuration mapping for the voice is missing and cannot be recreated.
message field does not currently distinguish a transient source-clip failure from a permanent one. If the error persists after a few spaced retries, try a different engine or voice, or contact support.
script_too_short
HTTP status: 400
The script provided is too short to generate a video. HeyGen requires the text-to-speech audio to be at least 1.0 second long. Very short scripts (a single word, a period, or a few characters) will not produce enough audio. Add more content to your script and retry the request.
tts_text_invalid
HTTP status: 400
The text provided for text-to-speech conversion is invalid or cannot produce speech. Check that the script is not empty and contains speakable words or valid pauses, then retry.
video_too_long
HTTP status: 400
The input video exceeds the maximum supported duration for this endpoint (for example, filler word removal supports sources up to 2 hours). The error message includes the probed duration and the limit. Trim or split the video and retry.
no_audio_track
HTTP status: 400
The input video has no audio track. Speech-based processing (such as filler word removal) needs speech audio to transcribe. Provide a video that contains an audio stream.
download_failed
HTTP status: 400
A URL provided in your request could not be downloaded. This applies to video URLs, image URLs, audio URLs, and any other user-supplied resource link. Common causes:
- The URL is not publicly accessible (authentication required, private video, restricted sharing settings).
- The URL is malformed or points to a page rather than a direct file.
- The remote server refused the connection or returned an error.
- For Google Drive links, the file must be shared with “Anyone with the link” access.
- For YouTube/Vimeo, the video must be public (unlisted or private videos are not supported).
message field names it, without any query parameters. Otherwise — the failure involves one of HeyGen’s own internal resources, or your URL could not be parsed — message is the fixed string Failed to download a required asset. If you supplied the URL, check it first; if it recurs on a URL you’ve confirmed is valid and reachable, contact support and quote that message.
youtube_video_download_forbidden
HTTP status: 400
A YouTube URL you provided points to a video that cannot be downloaded because YouTube itself forbids it. Unlike download_failed, this is not a connectivity or parsing problem — the video is reachable but not permitted for download. Common causes:
- The video is members-only, age-restricted, or otherwise gated behind a YouTube sign-in.
- The URL points to a live stream, a premiere, or an upcoming event rather than a finished video.
- The video is region-locked or blocked in the region HeyGen downloads from.
message field for the specific reason.
batch_too_large
HTTP status: 400
The batch request contains more items than the maximum allowed. Split the request into smaller batches and retry.
batch_item_invalid
HTTP status: 400
One or more items in the batch request failed validation. Check the message field for details about which item failed and why, correct the payload, and retry.
batch_finalize_failed
HTTP status: 409
A prior call to complete this upload batch started its finalize step, and that step ended in a terminal failure. Repeating the same request will not succeed. Check each item’s status with GET /v3/assets/batches/{batch_id}, and create a new upload batch for the files that did not finish.
too_many_ids
HTTP status: 400
The request specified more IDs than the maximum allowed in a single call. Reduce the number of IDs and split the request across multiple calls.
video_delete_failed
HTTP status: 500
The video could not be deleted due to an internal error. Retry the request. If the error persists, contact HeyGen support with the video_id.
internal_error
HTTP status: 500
An unexpected error occurred on HeyGen’s servers. This is not caused by your request. If the error persists, contact HeyGen support and include the full error response for faster debugging.
voice_provider_error
HTTP status: 502
An upstream voice provider that HeyGen relies on (for text-to-speech, voice cloning, or voice design) failed or closed the connection unexpectedly. This is not caused by your request. Retry after a short delay. If the error persists, contact HeyGen support and include the full error response for faster debugging.
bad_gateway
HTTP status: 502
The LiveAvatar API behind /v3/live-avatar/* could not be reached: the connection was refused, reset, or could not be established in time. This is not caused by your request. Retry after a short delay. If the error persists, contact HeyGen support and include the full error response for faster debugging.
Also returned by HeyGen’s internal billing setup tools when the billing provider failed or rejected the call. The message carries the provider’s reason. Retry only when that reason is a timeout or a server error; a rejected call fails the same way until the order or the provider’s setup is corrected.
not_implemented
HTTP status: 501
The requested endpoint exists, but its server-side implementation has not shipped yet. Retrying the same request will not succeed until the feature is implemented.
gateway_timeout
HTTP status: 504
An upstream dependency did not respond within the time limit. For a resource URL in your request (for example, background audio or an image), verify that the URL is publicly accessible, responds quickly, and is not blocked by firewall or geo-restrictions. For voice generation, retry after a short delay.
When the timeout came from a resource URL you supplied, message names that URL when it can be rendered safely; otherwise asset-download timeouts use the fixed string Failed to download a required asset. Voice-generation timeouts instead explain that the voice service is temporarily unavailable. For /v3/live-avatar/*, the LiveAvatar API accepted the connection but did not answer within 30 seconds. Clients should branch on code, not message.
service_unavailable
HTTP status: 503
Also returned when Avatar model reference-video screening is unavailable or times out. No video record or workflow is created. Retry the create request with the same Idempotency-Key.
A downstream service needed to fulfill your request (for example, the text-to-speech synthesis backend or video quality comparison judge) is temporarily overloaded or unavailable. This is transient and not caused by your request. Back off and retry after a short delay.
Also returned when brand glossary access cannot be determined temporarily. No glossary read or write is performed. Retry the request after a short delay.
For an IPA-only brand glossary create or update, also returned when the Respelling fallback cannot be generated. No changes are saved. Retry, or supply pronunciation alongside ipa to avoid conversion.
Also returned when publishing a workflow version loses the version number to a concurrent publish on the same workflow. Same guidance: back off and retry.
Also returned when an edit to a video’s scenes cannot be applied in time, or when Edit Video (POST /v3/videos/{video_id}) cannot take or confirm its hold on the video, which keeps two edits of one video from both writing it. Nothing is saved or rendered; retry shortly.
Also returned when creating or duplicating a workflow without a name could not assign one because concurrent creates in the same space kept taking the next free name. Nothing was created; retry the request.
Also returned by PUT /v3/templates/{template_id}/variables when the element metadata store cannot be read while binding a character variable. The variable set is left unchanged rather than stored with a guessed avatar type; retry the same request.
Also returned when starting a workflow run could not confirm its coordinator workflow was accepted (a timeout, transport error, or an unrecognized response from workflow scheduling). This is safe and expected to retry: repeat the request with the same Idempotency-Key to resume the same run.
Also returned when canceling a workflow run could not be confirmed as settled (workflow scheduling was unreachable or returned an error). The run is never moved backward by an unconfirmed cancel; retry the cancel request, or fetch the run to see its current status.
Also returned when a workflow run’s url or base64 asset input could not be resolved yet. The run stays preparing; repeat the run-create request with the same Idempotency-Key to resume resolution at the first unresolved input.
Also returned when a paid request signed in through the HyperFrames website would be billed to the HyperFrames usage pool, but could not recall or record which balance pays for it. Nothing is created or charged. Retry the request with the same Idempotency-Key.
Also returned when professional voice source-audio ingestion is temporarily saturated or a finalized source cannot be read consistently. Back off and retry the create request with the same Idempotency-Key.
Also returned by GET /v3/audio/sounds when the search backend is temporarily rate-limited. Nothing was searched; retry the same request after a short delay.
Also returned by POST /v3/assets/{asset_id}/complete when the uploaded file cannot be checked right now. Nothing was finalized; retry the same request after a short delay.
Also returned by POST /v3/assets/{asset_id}/complete and asset imports when processing for the asset could not be started right now. Retry the same request after a short delay; a retry never starts processing twice.
Also returned by the composition sync snapshot and download routes when a snapshot’s saved manifest cannot be read or download URLs cannot be signed right now. Nothing changed; retry the same request after a short delay.
Also returned by a composition sync create (PUT …/compositions/{composition_id}) or Begin (POST …/uploads) for a new composition or upload while the workspace’s sync rollout setting cannot be read, typically on a server that has just started. Nothing was created; retry the same request (with the same Idempotency-Key for Begin) after a short delay.
Also returned by the composition sync upload routes (…/compositions/{composition_id}/uploads…) when private storage or the upload workflow cannot be reached. Begin admitted nothing a retry cannot recover: repeat it with the same Idempotency-Key. Upload URL requests can be repeated as they are.
Also returned by HeyGen’s internal billing setup tools when the billing provider is not configured in this environment. Retrying does not help until it is configured.
render_not_started
HTTP status: 503
The video’s document changed, but its render did not start, so nothing is charged. Read the video’s scenes again for the new edit_version, then send the edit again with no changes to render it.
hyperframes_project_invalid
HTTP status: 400
The HyperFrames project zip you supplied isn’t a valid composition. Make sure the zip contains an index.html (or the composition entry file you specified) at the root or in a single top-level directory, and that it opens correctly with the hyperframes CLI before submitting.
hyperframes_project_too_large
HTTP status: 413
The HyperFrames project zip exceeds the maximum allowed size for the ingestion method you used. Use asset_id (pre-upload via POST /v3/assets) for projects larger than the url / base64 caps.
hyperframes_composition_upload_too_large
HTTP status: 413
The push is over a composition sync size limit: the number of files, one file’s size, the project’s total size (every file the manifest lists, reused ones included) or the canonical manifest’s size. The message names the limit and both numbers. Nothing was written. Reduce the project, for example by removing unused media, and begin again.
hyperframes_render_not_found
HTTP status: 404
No HyperFrames render with that render_id exists for your space, or the render has been soft-deleted. Check that the render_id is correct and was created under the same API key / space.
hyperframes_render_failed
HTTP status: 500
The HyperFrames render failed server-side while producing the video. The project was accepted and the render started, so this is not a problem with your request payload. Retry the render; if it fails repeatedly for the same project, verify the composition renders locally with the hyperframes CLI before contacting support with the render_id.
When the cause is in the composition, the render’s failure_message names it instead of the generic text:
realistic_human_reference_not_allowed
HTTP status: 422
An Avatar model API reference video contains a realistic human face. The create request is rejected before a video record or generation workflow is created.
Complete identity verification and create the avatar in HeyGen, then use /v3/videos. For enterprise access, contact HeyGen sales. Reference images and clearly stylized reference videos remain allowed.
content_policy_blocked
HTTP Status: 451
An image in a HyperFrames agent Messages, Responses or Decisions request cannot be processed. The response gives no detector,
category or hash. errors[0].path identifies the first refused image block, including an image inside a tool result or
function-call output or Decisions question instructions. Remove that image from the next request, including any
conversation history; resending the same image will refuse it again. Desktop keeps the original check findings
when a background Decisions judgement is refused.
