# Skill: full/own-voice-dub — "דיבוב לעברית בקול שלה — אותם רגשות, אותו טון" (Complete video)

A creator's reel in another language becomes the same reel in Hebrew, in her own voice, with her own emotions. Her voice is cloned from a Hebrew voice note (measured: a clone made from the source video alone lost the speaker's accent and warmth); Gemini watches the video and writes down the emotion, energy, pace and smile of every sentence; the Hebrew is written sentence for sentence with every brand in Latin letters and every gender form right, then read by a strict editor; her clone reads every line three times with that sentence's tags, the hub's director and pronunciation check inside the call, and Gemini listens to the takes and picks one that is clean and fits the slot. Her burned-in subtitles are painted out, the mix sits on her video's own background, HeyGen redraws her lips, soft Hebrew captions come from her own words. The words are checked against the script before it goes out.

**Includes:** her voice cloned once — from a voice note in Hebrew, the language of the dub · Gemini watches her: the emotion, energy, pace and smile of every sentence · the Hebrew written sentence for sentence — every product, number and claim, every gender form right (a woman, to her girls) · three directed takes a line in her clone, with her tags; a fast round for a line that runs long · Gemini listens to every line — no wrong word, the speech inside its slot · her burned-in subtitles painted out · one mix on her video's own background, her lips redrawn by HeyGen · soft Hebrew captions from her own words · the words checked against the script before delivery
**Built on:** the speaker's own ElevenLabs clone from a voice note in the target language (`POST /v1/ai/voices/clone`), Gemini 3.1 Pro watching the video for the emotion of every sentence and writing the Hebrew with every gender form, three directed takes a line through the hub's voice (`POST /v1/ai/voice`: the director, the lexicon, the pronunciation check), Gemini listening to every line, the burned-in subtitles painted out (`POST /v1/studio/clean-text`), one mix on the video's own background (`audio.stem: "background"` + `voiceover` lines), HeyGen Precision lips (`POST /v1/video/lipsync`) and captions `soft` from the takes' own words
**Price:** 1200–2000 credits per finished minute · 30 s ≈ 600–1000 credits
**Uses:** `/v1/ai/voices/clone` · `/v1/studio/uploads` · `/v1/studio/convert` · `/v1/speech/transcripts` · `/v1/ai/text` · `/v1/ai/voice` · `/v1/studio/clean-text` · `/v1/studio/merge` · `/v1/studio/edit` · `/v1/video/lipsync`

Base URL: `https://dev.apihub.co.il` · Auth: `Authorization: Bearer <API key>` · Docs: `/docs`
Send the same `X-Tag: <job name>` header on every call of one video; `GET /v1/usage?tag=<job name>` then
returns exactly what it cost. Times in request bodies are **seconds on the edited (output) timeline**.
Unknown fields are rejected with the name of the right field — read the error instead of guessing.
**Files are kept 24 hours** (renders, uploads, generated clips/images/music) — download the finished video.

**Models.** ElevenLabs Instant Voice Clone (once, from a voice note in the target language) and eleven_v4 read every line with
the Hebrew director, the lexicon and the pronunciation check inside the call; Gemini 3.1 Pro (`quality: "best"`) watches the video for the
emotion of every sentence, writes and edits the Hebrew, and listens to the three takes of every line; AssemblyAI transcribes the source
and checks the mix; PP-OCR + LaMa paint the burned-in subtitles out; HeyGen Precision redraws the lips; the stem, the mix and the captions
are our renders. The lips are most of the price; everything is priced by the video's length.

---

## 1. The voice — once, from a recording in the TARGET language

```jsonc
POST /v1/studio/uploads { "filename": "her-hebrew-note.mp3", "content_type": "audio/mpeg", "size_bytes": <bytes> }   // → upload_url + url
POST /v1/ai/voices/clone { "name": "<her name> (Hebrew)", "language": "he",
  "audio_urls": [ "<30–120 s of HER speaking Hebrew>" ],
  "consent": { "accepted": true, "subject": "other", "subject_name": "<her full name>", "basis": "consent", "locale": "he" } }   // ≈ 282 credits, once
// → voice_id — the caller's alone
```

**The clone decides the whole dub, and what it is made of decides the clone.** Measured on a 166-second run (Gemini 3.1 Pro
listening against her real Hebrew voice note, three lines, two rounds, five dimensions):

| Made from | Score /50 | What the ear said |
| --- | ---: | --- |
| **her 30-second Hebrew voice note** | **30.8** | her timbre, her warmth, her own slight accent kept |
| the 166 s of the Russian source video, the voice separated | 24.3 | the same woman, but Hebrew with no accent at all — "someone else" |
| the note + 36 s of the video's most energetic lines | 21.5 | flat and stiff: the Russian takes over |
| the video + the note | lower still | robotic |

So: **a sample of the speaker in the language of the dub, even a short one, beats minutes of the source video.** Ask for
a 30–120 second voice note in the target language — talking, not reading, with the energy of her videos. The source video's
voice (`POST /v1/studio/clean-audio` with `"stem": "voice"`, ≈ 74 credits) is the fallback when no such note exists.

**Only a voice whose owner agreed** (`"subject": "other"` with the name and `"basis": "consent"`). A recording the provider
flags for the speaker's own verification is refused and nothing is kept (`voice_needs_verification`): never worked around.
A clone that already exists on the account (from an earlier job) is reused — never re-cloned to "try again".



## 2. The source: one mp4, the sentences, where each one starts

```jsonc
POST /v1/studio/uploads { "filename": "reel.mov", "content_type": "video/quicktime", "size_bytes": <bytes>, "license": "own" }   // the phone's file as it is
POST /v1/studio/convert { "video_url": "<the upload>", "output": { "format": "mp4", "resolution": "source", "quality": "high" } }   // ≈ 209 credits for 166 s: every later step reads this mp4
POST /v1/studio/convert { "video_url": "<the upload>", "output": { "format": "mp3" } }                                            // the sound alone, for the transcript and the clone
POST /v1/speech/transcripts { "audio_url": "<the mp3>", "language_code": "ru", "speaker_labels": true }                            // ≈ 4 credits
GET  /v1/speech/transcripts/{id}/sentences                                                                                          // [{ start, end, text }] (ms)
```

- **One speaker, or stop.** The measured run was one woman; a second speaker needs a second clone and the lines split by `speaker`.
- **The sentence starts are the dub's clock.** AssemblyAI absorbs a pause into the next word, so a sentence after a pause
  starts up to half a second early: measure the silences of the mp3 (`silencedetect`, −32 dB, 0.25 s) and move a start that
  falls inside a silence to the silence's end. The measured run moved 4 of 37 starts by 20–70 ms.
- **The slot of a sentence** = from its start to the next sentence's start. The Hebrew take must fit in it (step 6).

## 3. Gemini watches her: the emotion of every sentence

```jsonc
POST /v1/ai/text { "quality": "best", "json": true, "temperature": 0.2, "max_tokens": 30000,
  "system": "You are a precise dubbing director with a native ear for Russian and Israeli Hebrew. Judge by watching and listening.",
  "prompt": "<the numbered sentences with [start, length]> … for each: emotion, energy 1-10, pace, 1-3 ElevenLabs v4 audio tags in brackets
             ([excited][smiling], [warmly], [giggles], [emphatic]), smile none|slight|broad, the stressed words, what she does with the product>",
  "media": [ { "url": "<the mp4>", "type": "video", "fps": 1 } ] }                                           // ≈ 41 credits for 166 s
// → speaker { gender, voice_character }, audience, mood (one line for the voice), sentences [ { i, emotion, energy, pace, tags, smile, stressed_words } ]
```

This is what makes the dub hers and not a reading: the tags and the energy of each sentence go into its takes (step 5) —
`[thrilled][smiling]` on "בנות, פשוט האהבה החדשה והמטורפת שלי", `[soft][informative]` on the texture, `[playful][urgent]` on
"לקחת ולברוח". The picture is watched at 1 fps; the voice is heard whole.

## 4. The Hebrew: every product, every number, every gender form

```jsonc
POST /v1/ai/text { "quality": "best", "json": true, "temperature": 0,
  "system": "You are an Israeli dubbing writer for beauty influencers … never a literal translation, never dubbed-movie Hebrew, and you never get a gender form wrong.",
  "prompt": "<the numbered sentences> + WHO SPEAKS AND TO WHOM: a WOMAN (אני אוהבת, לקחתי, התאהבתי) to her FEMALE followers (בנות: אתן, לכן, שלכן; תראו, תקחו)
             + the rules below" }                                                                              // ≈ 75 credits
POST /v1/ai/text { …a strict native editor: gender agreement AND meaning against the original, returns only the lines to fix… }   // ≈ 28 credits
```

The rules that held on the measured run:
- **One Hebrew line per sentence, same number, same order, same content** — every product, number and claim stays
  ("1,5N" → "אחת נקודה חמש N", "83 евро" → "שמונים ושלושה יורו", "100%" → "מאה אחוז"). Spoken, not longer than the original
  by more than ~15 %: shorten the wording, never pad. The editor's pass caught 4 lines: decimals read in the feminine
  (אחת נקודה חמש), יורו is masculine (שמונים ושלושה), and two lines where "מתה עליו" had replaced a plainer "התאהבתי בו" / "האהוב עליי".
- **Brands and the English of the trade stay Latin:** Dior, Sephora, Westman Atelier, One Size, Prada, Hourglass, glow skin,
  must have, skin tint, Ultra Pink. Israeli women say מייקאפ (foundation), סומק, צלליות, פודרה, ספריי קיבוע, מים מיצלריים,
  מיני, גוון, פלטה, תיק איפור.
- **Her tics stay hers:** בנות for Девчонки, "אתן לא מבינות" for вы не представляете, פשוט, סופר, ממש, "מתה על", בומבה.
- **No em-dashes** (the voice stops dead on them; a comma or "…"), no niqqud in the lines — the hub's voice director and
  lexicon add what a voice needs.
- **The gender table is the law.** A dub that lets a woman say "אני אוהב" or tells her girls "לכם" is a failed dub.

## 5. Three directed takes a line, in her clone

```jsonc
POST /v1/ai/voice {                                                                 // ×3 a line, three lines at once
  "text": "[excited][warm] פשוט האהבה החדשה והמטורפת שלי.",                            // the sentence's tags from step 3 + the line
  "voice_id": "<her voice_id>", "model": "eleven_v4", "language_code": "he", "timestamps": true,
  "stability": 0.2,                                                                 // energy ≥ 8 → 0.2 · 6–7 → 0.3 · ≤ 5 → 0.4
  "mood": "<the sentence's emotion>; <the video's mood line>",
  "previous_text": "<the line before>", "next_text": "<the line after>" }           // the voice flows across the lines
// ≈ 450 credits for 37 lines × 3 (1,700 characters) · → url, duration_seconds, words [{ text, start, end }], verify { failed_words, uncertain_words }
```

- The hub directs and checks every Hebrew take by itself (`prepare`, on by default; the lexicon; the pronunciation check with
  its retakes) — the tags you send stay where they are, the words get what they need. `verify.failed_words` names a word the
  check heard wrong: that take is out.
- **eleven_v4 ignores `speed`.** A line that comes out longer than its slot is read again with `[quickly]` in front of its
  tags, and shortened where the Hebrew allows ("זה המותג One Size" → "זה של One Size", "הפינה האהובה עליכן: הרכישות החדשות
  שלי" → "הפינה האהובה עליכן, החדשים שלי"). The measured run's first round left 8 of 37 lines over their slot; the fast round
  fixed them. **Never time-stretch her voice** — it is the first thing an ear hears.

## 6. Gemini listens, the slot decides

```jsonc
POST /v1/ai/text { "quality": "best", "json": true, "temperature": 0.1, "max_tokens": 6000,
  "system": "You are a demanding native Israeli dubbing director. You hear three takes of the same Hebrew line, in order (take 1, 2, 3), read by a cloned voice of a beauty influencer. For EACH take: list every word heard differently from the text (the word as written and what was heard, in Latin letters), score naturalness 1-10 (a real woman talking to her followers, not a robot or an announcer) and delivery 1-10 (matches the intended emotion, energy and pace given), and add a one-line note. Then rank the takes best to worst. Answer JSON: {\"takes\": [{\"n\": 1, \"wrong_words\": [{\"word\": \"\", \"heard\": \"\"}], \"naturalness\": 0, \"delivery\": 0, \"note\": \"\"}], \"ranking\": [1]}",
  "prompt": "The text: <the line>. The intended delivery: <emotion>, energy <n>/10, pace <pace>, <tags>; smile: <smile>. Overall: <mood>",
  "media": [ { "url": "<take 1>", "type": "audio" }, { "url": "<take 2>", "type": "audio" }, { "url": "<take 3>", "type": "audio" } ] }   // ≈ 8 credits a line
```

The pick, in this order: **no wrong word** (the hub's check and Gemini's agree) → **the speech fits the slot** (the take's
words: last `end` − first `start` ≤ slot + 0.25 s) → Gemini's rank → the shortest. One call per line, the three takes of
that line only: several lines in one call swap their labels.

**Where the take goes:** at the sentence's start minus the take's leading silence (`words[0].start` — v4 leaves 0.2–0.5 s
before the first word), pushed later only while the previous take still speaks (a 60 ms gap). The measured run ended its speech
0.3 s before the original did.

## 7. The burned-in subtitles painted out

```jsonc
POST /v1/studio/convert { "video_url": "<the mp4>", "trim": { "start": 0, "end": 94.5 }, "output": { "format": "mp4", "resolution": "source", "quality": "high" } }   // halves ≤ 2 minutes, cut at a pause
POST /v1/studio/clean-text { "video_url": "<a half>", "region": { "top": 0.78, "bottom": 0.95, "left": 0, "right": 1 }, "quality": "high" }   // ≈ 7.5 credits a second
POST /v1/studio/merge { "clips": [ { "url": "<half 1 clean>" }, { "url": "<half 2 clean>" } ], "output": { "format": "mp4", "resolution": "source", "quality": "high" } }
```

Her Russian captions were burned into the picture; a Hebrew dub under Russian text is not a dub. `clean-text` takes files up
to 2 minutes — cut at a pause between sentences, clean each half (the only render that needs the render node: when it
answers `service_unavailable`, wait a minute and send it again), merge. Keep the `region` tight to the text band (hers:
the lower 17 %): the measured run found 349 and 338 lines of text and painted 285 + 333 frames. Skip this step for a video without
burned-in text.

## 8. One mix: her picture, its own background, her Hebrew

```jsonc
POST /v1/studio/edit { "video_url": "<the clean mp4>",
  "audio": { "stem": "background", "normalize": true },                       // her Russian voice out, the room and the music stay
  "voiceover": [ { "url": "<take>", "start": 3.16, "volume": 1 }, … ],          // every chosen take at its place (step 6)
  "output": { "format": "mp4", "resolution": "source", "quality": "high" } }   // ≈ 209 credits a render for 166 s
```

**A render takes 30 `voiceover` lines.** 37 lines = two renders: the first 30 on the clean video with the stem, the rest on
the first render's result with `"audio": { "volume": 1 }` (the stem is already out). The second render costs the same ≈ 209.

## 9. Her lips

```jsonc
POST /v1/studio/convert { "video_url": "<the mix>", "output": { "format": "mp3" } }   // HeyGen takes an AUDIO file: a video as audio_url fails preflight
POST /v1/video/lipsync { "video_url": "<the clean mp4>", "audio_url": "<the mix as mp3>", "mode": "precision", "fit_to_audio": false,
  "consent": { "accepted": true, "subject": "other", "subject_name": "<her name>", "basis": "consent", "locale": "he" } }   // ≈ 1817 credits for 166 s · minutes
```

The mix is exactly the video's length, so `fit_to_audio` stays off and nothing is stretched. A dub without the lips (the
mix + captions) is the fallback when the lip-sync fails or the customer wants her real mouth.

## 10. Hebrew captions from her own words

```jsonc
POST /v1/studio/edit { "video_url": "<the lip-synced video>",
  "words": [ { "text": "בנות,", "start": 3160, "end": 4120 }, … ],             // the takes' words moved to their place on the video (ms)
  "captions": { "style": "soft", "language": "he" },
  "output": { "format": "mp4", "resolution": "source", "quality": "high" } }   // ≈ 209 credits
```

`soft` (white, a soft shadow, no outline — "for brands") sits where her Russian captions were. The words come from the
takes, never from a new transcript: a transcript of the mix would time the captions to what it heard, and the text would
drift from the script.

## 11. Before it goes out

- **The words:** `POST /v1/speech/transcripts` of the mix (`"language_code": "he"`) against the script line by line; every
  brand, number and gender word that differs is heard by ear (cut the word ±0.15 s with `/v1/studio/convert` `trim`, ask
  an open "transcribe exactly as heard"). The measured run: 4 credits.
- **The meaning:** the RU → HE table, sentence by sentence, goes to the customer with the video — every step there, nothing
  added, the punchline kept.
- **The length:** the speech ends where hers did; no line late by more than half a second.
- **Her ear is final.** The speaker hears her own timbre before anything else; send her a short A/B of the clones with the video.

## What a measured run cost

A measured run: 167 seconds of a Russian beauty reel, Russian → Hebrew in the speaker's own clone, made by this recipe
on dev (its video is not published) — 5398 credits, the clone made before (≈ 282). 37 lines, 111 takes + 24 fast ones + 13 fixes, 8 lines shortened, Gemini's ear on
every line, her subtitles painted out, her lips redrawn.

| Step | Credits |
| --- | ---: |
| The mp4 from the phone's file | 209 |
| The transcript | 4 |
| Gemini watches her (the emotion of every sentence) | 41 |
| The Hebrew and its editor | 103 |
| The takes (3 a line + the fast round + the fixes) | 450 |
| Gemini listens (every line) + the open spot checks | 366 |
| The voice stem (the clone's fallback sample) | 74 |
| The subtitles painted out (two halves), the cuts and the merge | 1671 |
| The mix (two renders) and its sound as mp3 | 450 |
| The lips (HeyGen Precision) | 1817 |
| The captions | 209 |
| The check (the mix transcribed against the script) | 4 |
| **Total** | **5398** |

The lips are most of it; a dub without them is about 3581 credits for the same reel. A video without burned-in
subtitles saves the 1671 of step 7.

## Tune it

The tags and `stability` per line (step 5) · `[quickly]` for a slot that is too short · `mood` · the caption style (any
`/skills/captions-<id>.md`; `soft` and `minimal` stay out of her way) · `"mode": "speed"` on the lips for a draft · a second
speaker = a second clone and the lines split by `speaker`.

Send `x-wait-ms: 110000` to wait for the render in the same call; if the response is still
`processing`, poll `GET /v1/executions/{id}` until `status` is `completed` and read `output.url`.

## Check the bill

`GET /v1/usage?tag=<job name>` — per call, per operation and the total, in credits. Quote that to the
customer, not the estimate above.

## Rules that keep captions readable on every platform

- **Safe zone.** Keep text inside the middle 80% of the frame: `side_margin` ≥ 6 handles the sides; `margin`
  ≥ 12 keeps the bottom clear of the platform UI (username, buttons, progress bar).
- **A line never leaves the frame** — a line too wide for the side margins is set smaller automatically. Do
  not shrink the size yourself, wrap instead (`max_chars_per_line`).
- **Hebrew has no capitals** — keep `uppercase: false`; it only breaks Latin words mixed into a sentence.
- **Fonts.** `GET /v1/studio/fonts` lists the bundled families and which scripts they draw. A font that
  cannot draw the caption's language is swapped and reported in `warnings`.
- The customer's transcript wins: to fix a word, `GET /v1/speech/transcripts/{id}/captions`, edit the text,
  and send the list back as `captions.captions` — timing is kept when the word count is unchanged.
