# Skill: full/director-fast — "במאי AI חסכוני" (Complete video)

The AI director at a third of the price: the same recipe — takes joined, Gemini watches and directs, a score timed to the story, sound design, kinetic words — with three short Veo 3.1 Fast clips (720p, 3 s on screen). The fast model draws the scene well but follows the prompt less closely; one shot that must show a precise action can still be made with full Veo.

**Includes:** takes combined into one video · Gemini watches and directs the edit · 3 Veo 3.1 Fast clips (3 s) · a dramatic score timed to the story's turns · sound design · kinetic words · punch-ins and flashes · hook · captions corrected by ear · colour grade
**Built on:** hook `punch` ("זום פאנץ׳", `/skills/hook-punch.md`) + captions `hormozi` ("הורמוזי", `/skills/captions-hormozi.md`)
**Price:** 600–700 credits per finished minute · 30 s ≈ 480–560 credits
**Uses:** `/v1/speech/transcripts` · `/v1/studio/trim` · `/v1/studio/merge` · `/v1/ai/text` · `/v1/ai/video` · `/v1/ai/music` · `/v1/ai/sound-effects` · `/v1/studio/edit`

Base URL: `https://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.** Gemini 3.1 Pro watches and plans (`quality: "best"` on the text call); the clips run on Veo 3.1
Fast (`quality: "balanced"`, 720p) — a clip is priced by its generated seconds, so a 30-second video costs
almost as much as a 60-second one.

---

## 1. Combine the takes (skip for a single take)

The customer often sends a story and a re-take of the part that went wrong ("the pitch came out bad, so I shot it
again"). Transcribe every take, find the cut points in the words, trim, then join whole clips.

Retakes INSIDE one recording ("רגע, אני אגיד שוב…", a sentence said twice, "אז אני… אז אני רוצה", "את את") need no
manual cut: add `"remove_retakes": true` to /v1/studio/edit (or short / remove-silence) — the engine finds them in the
words, keeps the last complete take of each (`{ "mode": "best" }`: the cleanest one) and never cuts a word said only
once; the render's `retakes` lists what it cut. Whole takes in separate files: merge them whole, then edit the merged
video with `"remove_retakes": { "window_s": <its length in seconds> }` — or send the files to the video agent and pick
"combine the best takes".

```jsonc
POST /v1/studio/uploads { "filename": "take1.mp4", "content_type": "video/mp4", "size_bytes": <bytes> }   // → output.upload_url (PUT the file) + output.url · the ORIGINAL file as recorded, never a resized copy (a vertical video resized to 1920×1080 renders stretched)
POST /v1/speech/transcripts { "audio_url": "<take url>", "language_code": "he", "speech_models": ["universal-3-5-pro", "universal-2"], "keyterms_prompt": ["<names, brands>"] }
// cut point = the silence between the last word you keep and the first word you drop:
// "ברורה" ends 83.45 s, "אז" starts 84.36 s → cut at 83.9 (half a second of breath)
POST /v1/studio/trim  { "video_url": "<take 1>", "start": 0, "end": 83.9, "output": { "resolution": "source", "quality": "high" } }
POST /v1/studio/merge { "clips": [ { "url": "<trimmed take 1>" }, { "url": "<take 2>" } ], "output": { "resolution": "source", "quality": "high" } }
POST /v1/speech/transcripts { "audio_url": "<merged url>", "language_code": "he", … }   → transcript_id (captions + timings)
```

Trim first and merge **whole** clips (no `start`/`end` inside the merge). Do not remove silences for a
fast talker — the few seconds saved are not worth jump cuts in a story. For two versions (with / without a
pitch) cut once and render twice: the shorter version is the same timeline trimmed.

**Everything below is timed on the cut video's clock.**

## 2. Let Gemini watch the video and direct the edit

Gemini watches the merged video (picture + sound) — it sees faces, gestures and pauses, hears what the words
cannot say, and corrects the transcript. Give it the word timings too, for frame-accurate effects.

```jsonc
GET /v1/studio/hook-styles · GET /v1/studio/caption-styles · GET /v1/studio/text-styles   // the ids it may use
POST /v1/ai/text
{ "quality": "best",                                   // Gemini 3.1 Pro
  "system": "<the director brief below>",
  "prompt": "Watch and listen to the whole video, then return the edit plan JSON.",
  "media": [ { "url": "<merged url>", "type": "video", "fps": 2 } ],
  "json": true, "max_tokens": 32000, "temperature": 1 }
// → output.json = the plan · ≈ 38 credits for 100 s of video · ~70 s
```

The director brief — send it as `system`, with the video length, the customer's must-have shots and, at the end,
the word timings (`GET /v1/speech/transcripts/{id}` → `words`, ms ÷ 1000, as `word@seconds`):

```text
You are the creative director and senior editor of a top short-form studio (Reels / TikTok / Shorts). You WATCH and LISTEN to the attached vertical video and design its edit. Be bold and creative, but every choice must serve the story — restraint is part of the craft. Return JSON only.

THE VIDEO: <L> s. Times below are seconds on this video's timeline.

RETURN
{
 "beats": [ { "start": s, "end": s, "beat": "…", "emotion": "…", "intensity": 1-10 } ],
 "corrections": [ { "at": s, "wrong": "word as transcribed", "right": "the word actually said" } ],
 "hook": { "text": "…", "style": one of ["box_red","yellow","black","outline","clean","type","punch","glitch","marker","words","box","counter","shake","rise","strike","blur","flash"], "duration": 2.5-3.5 },
 "captions_style": one of ["classic","hormozi","karaoke","one-word","marker","news","neon","quiet","pop","typewriter","bounce","underline","elegant","handwritten","comic","sticker","drama","cinema","shadow","lime","retro","scale","bubble","sport","minimal","slide","volt","neon-glow","marker-tape","keyword-pop","keyword-box","editorial","poster","broadcast","soft","cards","cards-bold","cards-neon"],
 "grade": one of ["none","warm","cool","vivid","cinematic","teal-orange","bw","vintage","matte"],
 "clips": [ { "start": s, "duration": 3, "mode": "full" | "split", "prompt": "…", "why": "…" } ],
 "texts": [ { "start": s, "duration": 1.2-2.4, "text": "…", "effect": one of ["fade","pop","blur_in","slide_up","typewriter","highlight","punch","glitch","marker","words","box","counter","shake","rise","strike","flash"], "preset": one of ["hero","mega","accent","pill","pill-accent","marker","outline","small","script","type"], "y": 22-45 } ],
 "zooms": [ { "start": s, "end": s, "to": 1.05-1.3, "ease": "punch" | "smooth" } ],
 "flashes": [ { "start": s } ],
 "sounds": [ { "start": s, "prompt": "English, max 8 words", "duration": 0.5-3, "volume": 0.3-0.7 } ],
 "music": { "style": "overall sound, always instrumental, sits UNDER the voice", "sections": [ { "start": s, "end": s, "name": "…", "styles": ["…"], "avoid": ["…"] } ] },
 "notes": "one paragraph: the idea of the edit"
}

RULES
- Land every effect on the frame its word is said — take times from the word timings and from what you see.
- beats: the emotional map from what you SEE and HEAR (voice, face, gestures, pauses).
- corrections: speech-recognition mistakes you hear; keep slang and curses.
- HOOK (0–3 s): the strongest line, 3–7 words in the video's language, never inventing facts; wrap ONE payoff word in *asterisks*. Prefer an effect style (punch, rise, words, glitch…) — the classic boxes cannot colour a word.
- CLIPS: exactly 3, AI-generated, photoreal, cinematic, vertical 9:16:
  1. MUST show: <the shot the customer asked for, e.g. the below-average guy walks up to the most attractive woman at the bar and she turns him down>. Place it while it is being told, mode "full".
  The rest: the most visual moments of the story. Never over a punchline or the last line; at least 8 s apart; none before 3 s.
  prompt: English, 40–80 words: subject, action, setting, camera move, lens and light, mood; no real people, never the speaker; tasteful; end with "no text, no logos, no watermark".
  Each clip is on screen 3 s (the first 3 s of a 4 s generation by a fast model): ONE simple, visible action that starts at once — no multi-step story inside a clip.
- TEXTS: 5–9 kinetic words, 1–4 words each taken from what is said at that moment, at most one per 5 s, never during the hook; counter for money and numbers; accent one word with *asterisks*. y = % from the top (captions live at 65–90).
- ZOOMS: 4–8 — punch on surprises and contrasts, smooth on emotional lines; never during a "full" clip.
- FLASHES: at most 3 (before a full clip or on a hard contrast).
- SOUNDS: at most 12 — a whoosh 0.3 s before each clip, an impact on the hardest line, a hit on the turn, a riser into the payoff, soft pops on text effects.
- MUSIC: consecutive sections covering 0 → <L> exactly, each ≥ 3 s; borders land exactly on the story's turns (quiet tension in the set-up, a hit or drop on the conflict, a lift on the turn, a build to the payoff, a real ending). English styles: instruments, dynamics, tempo, energy.

WORD TIMINGS (word@start seconds): <הם@0.00 יושבים@0.22 בפאב@0.48 …>
```

A real plan it returned for the example (84 s, trimmed):

```json
{
 "hook": {
  "text": "הבחור שניגש לבחורה הכי *סקסית* בפאב",
  "style": "punch",
  "duration": 3
 },
 "captions_style": "hormozi",
 "grade": "cinematic",
 "corrections": [
  {
   "at": 14.94,
   "wrong": "דפוסה",
   "right": "תפוסה"
  },
  {
   "at": 70.71,
   "wrong": "מאמצים",
   "right": "אמיצים"
  }
 ],
 "clips": [
  {
   "start": 12.3,
   "duration": 3,
   "mode": "full",
   "prompt": "In a lively, softly lit pub, an ordinary, slightly awkward average-looking guy in his thirties leans in to talk to a strikingly attractive woman at the bar; she gives a polite smile, shakes her head and turns back to her drink. Handheld medium shot, shallow depth of field, warm cinematic light. no text, no logos, no watermark",
   "why": "מאמי, אפשר להכיר? … מצטערת, אני תפוסה"
  },
  {
   "start": 77.2,
   "duration": 3,
   "mode": "split",
   "prompt": "A dull, monotonous open office: uninspired workers sit passively at their desks in gray, dim light, one man stares blankly at his screen, nothing happens. Static camera, muted tones… no text, no logos, no watermark",
   "why": "אנשים שיושבים בצד שקטים"
  }
 ],
 "texts": [
  {
   "start": 32.61,
   "duration": 2,
   "text": "האומץ זה הניצחון",
   "effect": "blur_in",
   "preset": "hero",
   "y": 25
  },
  {
   "start": 52.31,
   "duration": 2.2,
   "text": "התגובה של *עצמנו*",
   "effect": "highlight",
   "preset": "marker",
   "y": 30
  }
 ],
 "sounds": [
  {
   "start": 14.8,
   "prompt": "deep bass impact on rejection",
   "duration": 1.5,
   "volume": 0.5
  },
  {
   "start": 22.7,
   "prompt": "vinyl record scratch wow moment",
   "duration": 0.8,
   "volume": 0.5
  }
 ],
 "music": {
  "style": "Cinematic instrumental drama with rhythmic tension, evolving from subtle curiosity to a powerful emotional climax, always sitting under the voice.",
  "sections": [
   {
    "start": 0,
    "end": 14.8,
    "name": "Pub Setup",
    "styles": [
     "subtle tension",
     "curiosity",
     "minimal"
    ]
   },
   {
    "start": 14.8,
    "end": 22.7,
    "name": "The Question",
    "styles": [
     "suspense",
     "pause effect",
     "low drone"
    ]
   },
   {
    "start": 22.7,
    "end": 42.2,
    "name": "The Twist",
    "styles": [
     "uplifting",
     "driving rhythm",
     "hopeful"
    ]
   },
   {
    "start": 42.2,
    "end": 60.1,
    "name": "Self Reflection",
    "styles": [
     "serious",
     "introspective",
     "slow cello"
    ]
   },
   {
    "start": 60.1,
    "end": 72.1,
    "name": "The Haters",
    "styles": [
     "dark pulse",
     "edgy",
     "steady bass"
    ]
   },
   {
    "start": 72.1,
    "end": 83.9,
    "name": "The Contrast Climax",
    "styles": [
     "epic build",
     "staccato strings",
     "triumphant finish"
    ]
   }
  ]
 }
}
```

**Your pass as the editor** before generating anything: 3 s is short — put clip 1 on the punch line itself
("אפשר להכיר? … אני תפוסה"), not on the set-up; make the most cinematic clip "full", drop any zoom that overlaps a
full clip, reuse one generation for repeated sounds (three whooshes = one call). The fast model follows prompts less
precisely: it draws the scene well but may skip the action (a jump, a head shake). When one shot MUST show a specific
action, generate that one with `"quality": "best"` (≈ 480 credits for 4 s) — the rest stay fast.

## 3. Generate everything in parallel

```jsonc
POST /v1/ai/video { "quality": "balanced", "prompt": "<clip prompt>", "aspect_ratio": "9:16", "resolution": "720p", "duration": "4",
                    "negative_prompt": "text, captions, subtitles, watermark, logo" }        // Veo 3.1 Fast 4 s ≈ 120 credits · ~1 min
// Veo's shortest clip is 4 s and every generated second is billed — show the first 3 s in the edit
POST /v1/ai/music { "prompt": "<music.style> Hybrid orchestral drama; instrumental.", "instrumental": true,
                    "sections": [ { "name": "Pub Setup", "duration": 14.8, "styles": ["subtle tension", "curiosity"] }, … ],
                    "avoid": ["vocals", "lyrics", "EDM drops"] }   // durations = end − start; the track lasts exactly their sum · ≈ 0.75 credits/s · ~18 s
POST /v1/ai/sound-effects { "text": "deep bass impact on rejection", "duration": 1.5 }                     // ≈ 1–2 credits each
GET  /v1/speech/transcripts/{id}/captions   → the caption lines (ms); apply the plan's corrections to line and word text
```

## 4. One render

```jsonc
POST /v1/studio/edit     // x-wait-ms: 110000, then poll GET /v1/executions/{id}
{ "video_url": "<merged url>",
  "hook": { "text": plan.hook.text, "start": 0, "duration": plan.hook.duration, "style": plan.hook.style },
  "captions": { "style": plan.captions_style, "captions": <corrected caption lines> },
  "overlays": [ { "url": "<clip>", "type": "video", "start": 12.3, "duration": 3, "mode": "full", "fade_ms": 150 },
                { "url": "<clip>", "type": "video", "start": 77.2, "duration": 3, "mode": "split", "fade_ms": 150,
                  "split_side": "top", "split_ratio": 0.55, "speaker_focus": 0.3 } ],
  "texts":  [ { "text": "התגובה של *עצמנו*", "start": 52.31, "duration": 2.2, "effect": "highlight", "preset": "marker", "style": { "y": 30 } } ],
  "zoom":   [ { "start": 22.7, "end": 28.4, "to": 1.25, "ease": "punch", "y": 0.35 } ],
  "panels": [ { "start": 22.74, "duration": 0.12, "color": "#FFFFFF", "fade_ms": 30 } ],                   // the flashes
  "sounds": [ { "url": "<sfx>", "start": 14.8, "volume": 0.5 } ],
  "music":  { "url": "<score>", "volume": 0.26, "duck": false, "loop": false, "fade_in_ms": 400, "fade_out_ms": 250 },
  "color":  { "preset": plan.grade },
  "audio":  { "normalize": true },
  "output": { "aspect_ratio": "9:16", "resolution": "1080", "fit": "crop", "quality": "high" } }
```

**Music level:** a talking head speaks almost all the time — with `duck: true` the score would be pushed under
the voice for the whole video. `duck: false` at 0.26 keeps it ≈ 14 dB under the voice: heard, never in the way.
The score was written to the story's turns, so its hits land by themselves.

**Optional — a closing call to action and a banner** (the second version of the example):
- End card: `POST /v1/ai/voice` (the pitch, ElevenLabs v3), a Veo 8 s background, `POST /v1/studio/edit`
  on it with `"speed": 0.88` (stretches 8 s to 9.1 s), `"audio": { "mute": true }` and the voice as
  `voiceover` → merge it after the story → in the final edit add its captions (transcribe the voice, offset the
  words), texts on it, and a dark panel (`"color": "#000000", "opacity": 0.55`) behind the last line so the URL reads.
- Banner: one small text at the bottom, once in the middle —
  `{ "text": "נערך אוטומטית עם *API* *Hub*\napihub.co.il", "preset": "small", "effect": "slide_up", "exit": "fade",
  "style": { "y": 84, "size": 3.9, "weight": 800, "background": "#111111E6", "accent_color": "#FFD600", "max_chars_per_line": 40 } }`.

## 5. Check before you deliver

Grab frames at the hook, inside every clip and on every text (`POST /v1/studio/thumbnail { "video_url", "at" }`,
≈ 1 credit each) and look at them: the accent word must be visible on its box, the captions clear of the texts,
the clips on the right words. Re-render only what is wrong — a render of 84 s takes ~7 minutes.

## What the example cost and how long it took

84 s, two takes, three Veo 3.1 Fast clips (4 s each, 3 s on screen, 720p), a 6-section score, 8 sounds, one 1080p render:

| Step | Credits | Time |
|---|---|---|
| Upload + transcribe 2 takes | 6 | 10 s |
| Trim + merge | 186 | ~2 min |
| Transcribe the merged video | 3 | 8 s |
| Gemini watches and plans | 38 | 70 s |
| 3 Veo 3.1 Fast clips | 360 | ~1 min (parallel) |
| Score (84 s, 6 sections) + 8 sounds | 72 | ~20 s (parallel) |
| Final render (1080p) | 84 | ~7 min |
| **Total** | **749** | **~12 min** |

A third of the full-quality price (`/skills/full-director.md`, 2,309 credits with Veo 3.1). The clips are 48% of it;
a single-take video skips "Trim + merge" (−186).

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.
