All skills
heygen-com avatar

/heygen-translate

@1bd5e4d
by HeyGenheygen-com/skills460 stars
78

Translate and dub a video into another language with voice cloning and lip-sync, powered by HeyGen Video Translation. The presenter keeps their face, their voice is cloned into the target language, and lips re-sync to the new audio — viewers see the same person speaking natively. Use when: (1) localizing an existing video into one or more languages ("translate this video to Spanish", "make this in French and German", "dub this into Japanese", "I need this in 10 languages for a launch"), (2) the user has a finished video and wants the SAME presenter speaking another language (not a new presenter — that's heygen-video), (3) podcast / audio-only translation ("translate this podcast", "dub the audio but keep my video"), (4) high-stakes translations where the user wants to review/edit subtitles before final render (the proofreads workflow), (5) "translate my video", "dub this", "localize this clip", "make a multilingual version", "subtitle and dub". Returns the translated video URL (or audio file for audio-only mode), one per target language. Chain signal: if the user wants to CREATE a new video in another language (no source video exists yet), route to heygen-video and write the script in the target language — do not use heygen-translate. Use heygen-translate only when there is an existing source video to localize. NOT for: creating new videos from scratch (use heygen-video), avatar creation (use heygen-avatar), TTS-only synthesis (use heygen-video with audio-only output), or text-only translation.

Use this Skill: https://skilld.dev/gh/heygen-com/skills/heygen-translate

This session only. Nothing lands on disk.

referencesproofreads-workflow.md

≈4.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Proofreads Workflow

The proofreads endpoint extracts editable subtitles BEFORE the engine commits to the final translated render. Use it for high-stakes content where a re-translation would be expensive (time, plan credits, disappointment) compared with the proofread overhead.

This page documents what the proofread CLI actually performs, verified against the live heygen video-translate proofreads commands and the v3 HeyGen API as of skill v3.1.0. Where API docs and live behavior diverge, this page reflects live behavior.


What the proofread workflow does

Five subcommands and three resources:

heygen video-translate proofreads
├── create        POST /v3/video-translations/proofreads
├── get           GET  /v3/video-translations/proofreads/{id}
├── srt get       GET  /v3/video-translations/proofreads/{id}/srt
├── srt update    PUT  /v3/video-translations/proofreads/{id}/srt
└── generate      POST /v3/video-translations/proofreads/{id}/generate

Resource lifecycle:

   create                    srt get          srt update         generate
     |                          |                 |                  |
     v                          v                 v                  v
proofread session  ----->  edited SRT      replace SRT         video_translation_id
   (status:                  + original                          (continues as a
   processing→completed)     auto-extracted                      normal translation
                             transcript                          render — poll via
                                                                 video-translate get)

Each language gets its own proofread_id. Submitting output_languages: ["Spanish", "French"] returns two ids; each is reviewed and finalized independently.


Step-by-step: what each command actually does

proofreads create

Inputs (per CLI --request-schema):

  • video (required): {type: "url", url: "..."} or {type: "asset_id", asset_id: "..."}
  • output_languages (required): array of canonical language strings (e.g. ["Spanish"])
  • title (required): short label that appears in the dashboard and in the downloaded SRT filename
  • mode: "speed" or "precision" — precision is recommended; final render quality follows from this
  • speaker_num: integer, sets diarization expectation
  • enable_speech_enhancement, disable_music_track, enable_video_stretching, keep_the_same_format: booleans matching the same flags on create
  • brand_voice_id: optional brand voice
  • folder_id: optional project folder
  • srt: OPTIONAL initial SRT to seed the proofread session — {type:"url",url:"..."} (asset_id route is also accepted by the schema but only specific media types upload via heygen asset create — see Caveats below)

What the engine does after submission:

  1. Downloads the video.
  2. Runs ASR on the source audio → produces an original SRT in the source language (this is fetched via srt get as original_srt_url).
  3. Translates that SRT into each target language → produces an editable SRT per language (returned via srt get as srt_url).
  4. Marks the proofread status: completed.

No video render happens at this stage. That's the point — all the upfront ASR + translation cost is paid, but no avatar inference / lip-sync work is done until you generate.

Response (verified):

{
  "data": {
    "proofread_ids": ["b84c8e8d8a18418a8f6a001f8c8b2423-es"],
    "status": "processing"
  }
}

status is the session-level submit status, not per-id. Each id is then independently checked via proofreads get.

proofreads get

Inputs: the proofread id.

What it returns (verified, while processing):

{
  "data": {
    "created_at": 1777334587,
    "id": "8ce0fba6c1bf41339f991dcc0a0fc3e6-es",
    "output_language": "Spanish",
    "status": "processing",
    "title": "PR82 sintel proofread"
  }
}

While failed:

{
  "data": {
    "created_at": 1777334317,
    "failure_message": "Your video's audio is missing or corrupted, please try with another video",
    "id": "b84c8e8d8a18418a8f6a001f8c8b2423-es",
    "output_language": "Spanish",
    "status": "failed",
    "title": "..."
  }
}

While completed: same shape as processing, just status: "completed". submitted_for_review and input_language are nullable schema fields and may not appear on every response.

Polling cadence (verified empirically): typical extraction time for a 50-second source video is 3–5 minutes. Poll every 60 s after the first 30 s. Hard timeout: 30 min — beyond that, treat the session as stuck.

proofreads srt get

Inputs: the proofread id (must be completed).

What it returns (verified):

{
  "data": {
    "original_srt_url": "https://resource2.heygen.ai/video_translate/.../<title>_proofread_original.srt?...",
    "srt_url":          "https://resource2.heygen.ai/video_translate/.../<title>_proofread.srt?..."
  }
}

Both URLs are presigned and short-lived. Download immediately:

SRT_URL=$(heygen video-translate proofreads srt get "$PR_ID" \
  | jq -r '.data.srt_url')
curl -s "$SRT_URL" -o /tmp/proofread.srt

srt_url = the editable, target-language SRT that the engine produced. This is what you edit and re-upload.

original_srt_url = the engine's transcription of the source audio in the source language. Useful as ground-truth context when the user is verifying the translation against what was actually said in the source. NEVER upload this back as a target-language SRT — it'll fail or render the wrong language.

Verified format of the SRT files: standard SRT (UTF-8), well-formed timecodes, line numbers, separator blank lines. No HeyGen-proprietary format. Editable by hand, by sed, or by any subtitle tool.

Example (real output from a Sintel trailer test):

1
00:00:12,220 --> 00:00:14,320
¿Qué te trae a la tierra de los guardianes?

2
00:00:18,660 --> 00:00:19,900
Busco a alguien.

proofreads srt update

Inputs:

  • proofread-id as positional argument
  • srt body: {type: "url", url: "..."} or {type: "asset_id", asset_id: "..."}

Upload behavior:

✅ asset_id route works (preferred). heygen asset create accepts SRT (supported types: png, jpeg, mp4, webm, mp3, wav, pdf, srt). Upload the edited SRT, capture .data.asset_id, and pass {type:"asset_id", asset_id:"..."}:

ASSET_ID=$(heygen asset create --file /tmp/proofread.srt | jq -r '.data.asset_id')
heygen video-translate proofreads srt update <proofread-id> \
  -d "{\"srt\":{\"type\":\"asset_id\",\"asset_id\":\"$ASSET_ID\"}}"

✅ URL route works (fallback). Host the edited SRT at any publicly reachable URL and pass {type:"url", url:"..."}. (Non-subtitle content-type mismatches still error on upload.)

Practical patterns for hosting the edited SRT (URL route):

Where the SRT lives How to make it reachable
Local file Upload to S3 / Cloud Storage with public-read OR a presigned URL ≥2 h
GitHub gist Use the raw URL: https://gist.githubusercontent.com/<user>/<id>/raw/<file>.srt
GitHub repo Use the raw URL: https://raw.githubusercontent.com/<owner>/<repo>/<branch>/<path>.srt
Vercel / static host Drop into public/ and reference the absolute URL
Already-uploaded HeyGen presigned URL The presigned URLs from srt get work for round-tripping, but they expire — use only for very short edit cycles

The URL must return Content-Type of application/x-subrip or text/plain and the SRT body. Verify with curl -sI "$URL" before submitting.

Response (verified): the full proofread resource shape. The session is still completed; the SRT replacement takes effect immediately for the next generate call.

proofreads generate

Inputs:

  • proofread-id as positional argument
  • --captions (default false): burn the corrected captions into the final video
  • --translate-audio-only (default false): skip lip-sync, output dubbed audio only
  • --callback-url, --callback-id: optional webhook for completion

What the engine does:

  1. Reads the proofread session's current SRT (the most recently uploaded one).
  2. Runs the actual translation render (precision-mode avatar inference + voice clone + lip-sync).
  3. Returns a video_translation_id that is then polled via the regular heygen video-translate get (NOT proofreads get — at this point the resource graduates from "proofread" to "translation").

Response (verified):

{
  "data": {
    "status": "processing",
    "video_translation_id": "10c0808c39d847c2b1412af252ce50b5-es"
  }
}

After this call, polling shifts to:

heygen video-translate get "$VID_ID"

…which returns the standard translation response shape (status, eventual video_url, failure_message, etc.).


Full CLI walkthrough (verified end-to-end)

Substitute <proofread-id> and <vid-id> with the actual ids you receive.

# 1. Submit proofread session
heygen video-translate proofreads create \
  -d '{"video":{"type":"url","url":"https://example.com/source.mp4"}}' \
  --output-languages "Spanish" \
  --mode precision \
  --enable-speech-enhancement \
  --keep-the-same-format \
  --speaker-num 1 \
  --title "Q4 launch — Spanish proofread"
# → {"data":{"proofread_ids":["<id-es>"],"status":"processing"}}

# 2. Poll until completed (typical: 3–5 min for short videos)
heygen video-translate proofreads get <proofread-id>
# → status: processing → completed (or failed + failure_message)

# 3. Fetch the editable SRT and the original-language transcript
heygen video-translate proofreads srt get <proofread-id> > /tmp/srt-resp.json
SRT_URL=$(jq -r '.data.srt_url' /tmp/srt-resp.json)
ORIG_URL=$(jq -r '.data.original_srt_url' /tmp/srt-resp.json)
curl -s "$SRT_URL"  -o /tmp/proofread.srt
curl -s "$ORIG_URL" -o /tmp/proofread-original.srt

# 4. Edit /tmp/proofread.srt — by hand, by sed, by tool
#    Example: brand glossary find-replace
sed -i 's/Centro Garra/ClawHub/g'        /tmp/proofread.srt
sed -i 's/José Joshua/Joshua Xu/g'       /tmp/proofread.srt

# 5. Upload the edited SRT as an asset, then reference it by asset_id
#    (fallback: host at a public URL and use {"type":"url","url":"..."})
ASSET_ID=$(heygen asset create --file /tmp/proofread.srt | jq -r '.data.asset_id')
heygen video-translate proofreads srt update <proofread-id> \
  -d "{\"srt\":{\"type\":\"asset_id\",\"asset_id\":\"$ASSET_ID\"}}"
# → returns the proofread resource (status still completed)

# 6. Kick off the final render with the corrected captions
heygen video-translate proofreads generate <proofread-id> --captions
# → {"data":{"video_translation_id":"<vid-id>","status":"processing"}}

# 7. Poll the resulting translation to completion
heygen video-translate get <vid-id>
# → status: running → completed; data.video_url has the final mp4

When to insist on proofreads

  • Long videos (>3 minutes). The cost of re-rendering a 10-minute mistranslation is high; ASR + glossary fixes are cheap.
  • Corporate / branded content. Brand names, product names, slogans, claims need to land exactly.
  • Legal, medical, educational content. Wrong terminology has consequences beyond user disappointment.
  • The user reads the target language natively. They'll catch things you can't.
  • Source has named entities (people, companies, products, places) that auto-translation will likely mangle.
  • High-formality languages (ja, ko, th, de) where the source is conversational and the user wants register-matched output.
  • RTL languages (ar, he, ur, fa) where caption styling matters — sidecar SRT delivery via proofread is cleaner than burned-in captions.

If two or more apply: default proofreads ON. Don't ask — propose: "For [reason], let's do a quick proofread before final render."

When to skip proofreads

  • Short videos (<60 s) that aren't high-stakes.
  • The user wants speed and is willing to re-translate if quality is off.
  • Single language, single speaker, neutral content.
  • The user explicitly says "just go" or "fast as possible".

When the user CAN'T review the SRT

If neither the user nor a reachable native speaker reads the target language:

  • Skip proofreads — they can't add value to a review nobody performs.
  • Set lower expectations on the result. Suggest a test clip first.
  • Offer to deliver the dubbed video plus the SRT as a sidecar so they can share with a native-speaking colleague to review BEFORE distribution.

Don't run proofreads as a checkbox if no one will actually read the SRT — it just adds time without adding accuracy.


Common SRT edits

The engine WILL translate proper nouns and jargon by default. Find-and-replace is the fastest way to surgically restore them.

Brand glossary / do-not-translate

# Names, products, companies, slogans
sed -i 's/Centro Garra/ClawHub/g'   /tmp/proofread.srt
sed -i 's/José Joshua/Joshua Xu/g'  /tmp/proofread.srt
sed -i 's/Garra/Claw/g'             /tmp/proofread.srt   # only if "Garra" wasn't intended

For non-trivial glossaries, write a small Python script with the full list. Show the user the list first; have them confirm before applying.

Register / formality fixes

For ja/ko/de/fr/es/th/hi conversational content where the engine landed too formal:

Language Find Replace
German Sie du
German Ihr dein
German Ihre deine
German Sie haben du hast
French vous tu (context-aware — see caveat below)
French votre ton / ta
French vos tes
Japanese です (declarative) → strip per-line review needed
Japanese ます (verb suffix) → strip per-line review needed
Korean ㅂ니다 → 요 per-line review needed
Thai ค่ะ / ครับ (particles) → strip per-line review needed
Spanish usted → tú LATAM informal target
Spanish vosotros → ustedes es-ES → LATAM

Caveat: bulk sed-replace on register markers can break sentences in CJK/SEA languages where the marker is part of a verb. Walk through line-by-line for ja/ko/th, batch with confirmation for de/fr/es.

Numbers, dates, units

The engine usually localizes numbers and dates correctly. Spot-check:

  • Currency: $1,000 may render as mil dólares, US$1.000, or 1.000 $. Pick the audience convention.
  • Dates: 4/27 → 27 de abril (es), 4月27日 (ja), 27.04. (de), 27/4 (UK).
  • Units: imperial → metric is NOT done. Surface to the user if the source uses miles/feet/Fahrenheit and the audience expects metric.

Cultural references

Idioms, sports references, and pop-culture callouts often translate literally and lose meaning. Flag affected lines, ask whether to adapt or keep literal.


Sidecar SRT delivery

If the user wants captions as a sidecar (.srt) instead of burned-in:

  1. Run the proofread workflow normally.
  2. On generate, omit --captions.
  3. Deliver the dubbed video AND the corrected SRT (which you already have at /tmp/proofread.srt from Step 4) to the user.

This is the cleanest path for: RTL languages, brand-styled captions (their editor styles), or accessibility workflows where SRT is required.


Cost / time math

Proofreads add roughly:

  • 3–5 min for SRT extraction (Step 2). Verified empirically on a 50-second source video.
  • 2–10 min for review/edit (Step 4) — depends on length and editing depth.
  • Same render time as a normal translation for the final generate (Step 6–7).

Total proofreads overhead: ~10–20 minutes. For high-stakes content, trivially worth it. For a 30-second product demo, overkill.

Cost: HeyGen bills the final render the same as a non-proofread translation. The proofread session itself is light — no avatar inference happens until generate. So you effectively pay once (for the final render) regardless of how many SRT-edit cycles you ran.


Failure modes specific to proofreads

Symptom Verified failure_message Cause Fix
failed immediately on submit Failed to download video from url, please check the url is valid or the video is public Source URL was 401/403/404, returned HTML, redirected to a login page, or had wrong MIME HEAD-check the URL with curl -sI; ask user for public URL or local file → upload route
failed after ~30 s of processing Your video's audio is missing or corrupted, please try with another video Source has no audible speech, audio track is silent / missing, or codec is unparseable Verify the source has speech; consider re-encoding to MP4 + AAC; for silent / animation-only sources, route to a different workflow (this skill won't help)
asset create returns Content type not supported ... (CLI error envelope, not API status) The uploaded file's sniffed content type isn't a supported type (png/jpeg/mp4/webm/mp3/wav/pdf/srt) — e.g. a mislabeled or non-subtitle file Upload a real .srt via heygen asset create --file <x.srt> and pass {type:"asset_id",...}, or use the URL route
Uploaded SRT timing is off n/a — bad render result SRT timecodes were edited or shifted Re-download the SRT from proofreads srt get and edit text only, never timecodes; re-upload
generate runs but final render uses old text n/a srt update didn't take or you forgot to call it before generate Re-upload, then re-call proofreads generate
Proofread session expired session not found Sessions have a TTL (typically 24 h) Re-create the proofread; don't try to revive an expired one

For other failure_message strings, see troubleshooting.md.


Caveats and known quirks (verified)

  • heygen asset create accepts SRT files. Supported types: png/jpeg/mp4/webm/mp3/wav/pdf/srt. Upload the edited SRT, capture .data.asset_id, and pass {type:"asset_id",...} to srt update (preferred); the URL route remains a fallback.
  • proofreads create returns proofread_ids (plural, one per language) and a status at the SESSION level. The session-level status is just the submit ack; per-id status comes from proofreads get.
  • status enum is processing | completed | failed. Not pending / running. (The translation-render endpoint uses pending | running | completed | failed — these are different state machines.)
  • original_srt_url is auto-populated even when no SRT was provided at create time. It's the engine's source-language transcription, not a copy of an SRT you uploaded. Useful for verification, never re-upload as a target-language SRT.
  • SRT filenames carry the title. The downloaded SRTs are named <title>_proofread.srt and <title>_proofread_original.srt. Pick a clean, fileNAME-safe --title.
  • Polling shifts after generate. Up through srt update, you poll heygen video-translate proofreads get. After generate returns a video_translation_id, you poll heygen video-translate get instead — same translation poll loop as a non-proofread workflow.
  • Captions on generate are independent of captions in the proofread session. The proofread session always produces an editable SRT (that's its purpose); whether the FINAL video has captions burned in is controlled by --captions on generate.

Source: SKILL.md on GitHub

1 warning16d3 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill enables video translation using HeyGen's services. It provides instructions to install the HeyGen CLI from their official site and uses it to manage assets and dubbing. All identified behaviors, such as remote script installation and API key handling, are consistent with the vendor's official documentation and intended use of the service.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: MEDIUM · 2 issues

Signed by skilld at 1bd5e4d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last week.

Steadyupdated 3 months ago
What it can do
Runs commands Network Reads files Edits files
MCP servers
heygen
version
3.2.0
argument-hint
[video_url_or_path] [--to language]
homepage
https://docs.heygen.com/reference/video-translate
All 5 allowed tools
BashWebFetchReadWritemcp__heygen__*
Other metadata
metadata
{
  "openclaw": {
    "requires": {
      "env": [
        "HEYGEN_API_KEY"
      ]
    },
    "primaryEnv": "HEYGEN_API_KEY"
  }
}

README badge

README badge for heygen-com/skills/heygen-translate