---
name: hubfluencer-create
description: Generate a finished video ad, short, image carousel, or tracking-overlay clip with Hubfluencer — and run the surrounding content operation (plan → draft → review → schedule → measure). Use whenever the user wants to make a video — e.g. "use Hubfluencer to make a 15s ad for X", "create a short for my product", "make me a TikTok ad", "turn my product page into a campaign", "plan a month of ads for X". Drives Hubfluencer end-to-end through the @hubfluencer/mcp MCP server.
---

# Hubfluencer: create a video

You turn a plain-language request into a finished MP4 using the Hubfluencer MCP server. The user buys credits and creates an access token in the Hubfluencer app; you spend those credits to generate the video. Credits are real money — **$0.99 each** bought singly, less in bulk — so state what a generation will cost (credits + ~dollars) and get the user's go-ahead before the first paid call.

## Creative export contract

- Production prompts (`product_prompt`, `scenario_prompt`, and scene `prompt`) are visual directions. Narration scripts are read aloud verbatim, so `set_narration_script.script` must contain only the exact spoken words—never camera notes, prompt syntax, labels, or scene directions. Never copy a creative brief into `script`; use `generate_narration` or Autopilot unless the user supplied final voice-over copy.
- Do not rely on the video model to spell essential brand names, UI, signs, prices, offers, or CTAs. Put exact copy in Hubfluencer overlays, narration, or supplied ending artwork.
- A short logo position is a preferred corner: the renderer moves it away from headline, captions, CTA, offer, and badge areas. Omit `treatment` so transparent marks default to `throughout` and opaque square lockups default to `end_card`. Use a tight transparent wordmark; use square or full-bleed art as the poster instead. If poster artwork already has its logo/CTA, set `poster_includes_lockup: true` and do not add an `end_card` logo.
- Short posters fill the ending frame: phone-shaped portrait art is cover-cropped, while square, classic portrait, and landscape art is contained over a blurred backdrop. Editor closing images are always cover-cropped. Keep important content in the central 9:16 safe area. An Editor closing image gets no generic CTA, so put the CTA in the artwork or state it in the preceding narration/scene.
- Pass `segments_count` (3–10) to `create_editor_ad` when length matters. It is the exact structural target used by the cost quote and Autopilot launch.
- Each Editor AI scene is 8 seconds. Default a concise social ad to 3 scenes (about 24 seconds) unless the user asks for a longer story.

## Generation lifecycle authority

Every Editor and Short product read includes a non-null `generation_summary`. Treat it as the sole
lifecycle authority even when product projections such as `stage`, `autopilot_status`, `batch_generation_status`, child
statuses, or `latest_render` disagree. Poll only while
`generation_summary.timing.poll_after_ms` is a positive integer, at that server cadence; `null`
means stop polling. Read terminality from `generation_summary.status`, invoke only
`generation_summary.available_actions`, and download only when `download_result` is advertised for
a verified delivery artifact. Product projection fields are display metadata or explicit route fences only;
never derive polling, retry, cancel, or download availability from them.

When a stopped summary advertises `edit_required_input` or `review_spend_cap`, call
`provide_generation_input` with that action and its non-empty `input` object or approved
`max_credits`. For a failed `editor.scene.generate` or `editor.scene.regenerate` summary that
advertises `retry_generation`, call `retry_segment_generation { slug, segment_id }`. Both tools
re-read the product and echo the run/revision/step fences; never call them from product projection fields
or guess a fence. Re-read status after the command.

## Prerequisites

- The `hubfluencer` MCP server must be connected (tools like `make_video`, `get_credits`, `wait_for_completion` are available).
- The user must have an access token configured (via `HUBFLUENCER_API_TOKEN`, or by having run `npx -y @hubfluencer/mcp@0.18.0 login` — a scoped token is then saved to `~/.hubfluencer/credentials.json` and the server reads it automatically).
- **Never invent a token, and never echo a token in your output.** If no token is configured, tell the user to create one in the app (Settings → Access tokens, "Generate videos" scope) or to run `npx -y @hubfluencer/mcp@0.18.0 login`, then stop.

## Native macOS Studio

If the user connected the bearer-gated `hubfluencer-studio` localhost MCP server, use its advertised
local editing tools for that open project. Call `get_project_digest` first and send its exact required
`doc_rev` with every call. Read+edit is the default surface; generation is opt-in and always leaves a
visible approval card in Studio. Let the app handle explicit cloud-context disclosure—do not bypass it
with a direct Director REST request.

## The simple path (use this by default)

Call **`make_video({ prompt, save_path })`**. It creates the project, starts generation, polls to completion, and downloads the finished MP4 in one step. Shorts charge atomically at launch; editor Autopilot authorizes `max_credits` and spends incrementally as work starts. Unless the user already approved the spend, run `make_video({ prompt, dry_run: true })` first (free; returns the estimate + a draft `slug`), quote the credits and ~dollar cost, and generate only after a clear yes.

- `prompt`: a vivid description of the video the user wants.
- `save_path`: an absolute path where the MP4 should be written.
- `kind` defaults to `auto` — a fast single-clip **short** for simple prompts, a multi-scene **editor** ad for story / multi-scene prompts. Override only when the user is explicit: `kind: "short"` or `kind: "editor"`.

If `make_video` returns `terminal: false`, the render is still going. Call **`wait_for_completion({ slug, kind })`** again (reuse the `slug` and `kind` it returned). Generation takes a few minutes — that is normal, not a failure. For paid shorts, report `segments_completed / segments_total` and `active_segment_positions` from status; these are the authoritative clip-progress signals.

**Don't ship a bare short.** For a short, pass `headline` (the on-screen TITLE, ≈4 words / ≤40 chars), `subheadline` (secondary title), and a fitting `music_vibe` right in the `make_video` call — and **always `cta_text`** (a short without a CTA doesn't convert; add `offer_text` only if there's a real deal). Set `visual_language` explicitly (`kinetic_creator` is a strong default). These are SHORTS fields (`theme` applies to both kinds; `headline`/`subheadline`/`music_vibe` are ignored for editor). If copy is left blank the server auto-writes the headline + caption beats at generate time (`skip_auto_text: true` renders a deliberately bare clip). `make_video` cannot attach local files; use `create_editor_ad`'s asset paths for an editor, or the granular tools below for a short. If the user gave no title or assets, infer a sensible `headline`/`subheadline` from the prompt, or ask.

## The granular path (control / recovery)

Use this when you need fine control, or to recover after an interruption:

1. `get_credits` — check the balance before spending.
2. `create_short` **or** `create_editor_ad` — creates the draft (0 credits).
3. `generate_short` (for a short) **or** `start_autopilot { slug, max_credits }` (for an editor ad) — starts the render. A bare `start_autopilot { slug }` only returns a fresh quote; launch only after approval with a cap at least as high as that quote.
4. `wait_for_completion({ slug, kind })` — poll until done.
5. `download_result` — fetch the finished MP4 to disk.

Act on Editor batch recovery only when `generation_summary.available_actions` advertises
`retry_generation` or `cancel_generation`; then call `retry_editor_batch { slug }` or
`cancel_editor_batch { slug }` respectively. The MCP tools re-read the canonical summary before writing.
Raw REST retry callers send `generation_run_id` and `generation_revision` from that summary; cancel
callers send `generation_run_id`. Product batch fields are display projections, never command fences or
action availability. A stale 409 means re-read and issue a new command only if the action is still
advertised; never guess fences. Retry targets the exact failed canonical step. Only a scene named by
`current_step` may be repaired before retry. Resolve the child batch before starting Autopilot again;
retrying it does not authorize a new parent run. For an insufficient-credit blocker, top up or reduce
scope and follow the action in the newly read summary.

Cancellation is run-fenced. A paid short is not cancellable once `generate_short` starts, and its draft cannot be deleted until the run is terminal; a failure still refunds its 15 credits. `cancel_segment_generation { slug, segment_id, generation_claim_token }` uses the current token from `get_editor` and refunds an individually generated scene; batch/Autopilot scenes use workflow controls. `cancel_autopilot { slug }` re-reads and forwards the summary run/start fence, stopping only that parent chain while keeping completed work and already charged scenes. Re-read state after a stale fence error; never guess a fence value.

**Minimum sellable short** — check all seven before you spend:

- hook (`headline`) ≤4 words
- `text_beats` = hook → proof → payoff
- `cta_text` set (a short without a CTA doesn't convert)
- product image attached (`set_short_product`) AND described
- poster (`set_short_poster`) or closing lockup (`brand_name` / `closing_claim`) carries the ask
- `visual_language` set explicitly (`kinetic_creator` is a strong default)
- iterate: hooks are free to re-render (`update_short` → `rerender_short`, 0 credits after the first generate)

Iterate the hook before you spend: `generate_hook_variations` writes 3–6 hook options on the free assist quota. If `headline`/`subheadline`/`text_beats` are all blank, the server auto-writes the headline + caption beats at generate time (pass `skip_auto_text: true` to `generate_short` for a deliberately bare clip).

**Never re-roll — iteration is free after the first generate.** The first `generate_short` (15 credits) buys the footage + music; after that, every overlay edit re-renders over them for **0 credits**: `update_short` the fields, then `rerender_short { slug }` (REST: `POST /api/shorts/:slug/rerender`, no body, `Idempotency-Key` honored, rate limit 10/min).

- **Free to re-render:** `headline`, `subheadline`, `text_beats`, `cta_text`, `offer_text`, `badge_text`, `star_rating`, headline/subheadline/accent colors, `font_family`, `text_position`, `text_animation`, `closing_claim`, `brand_name`, poster (+ `poster_includes_lockup`), `end_card`, logo overlay (`set_short_logo` / `short_logo_position` / `short_logo_treatment`).
- **Needs `generate_short` again (15 credits):** `product_prompt`, product image, `creative_format`, `visual_language`, `theme`, `music_vibe`, `music_instruments`, `language`.

Edits that add/remove the end card (poster, `end_card`) can shift the video between 12s and 14s. The pinned music bed is reused: when it is shorter than the export it crossfades into a repeated continuation, then fades at the export end, without charging for regenerated music.

Iteration recipe: generate once → `update_short` a hook/CTA variant → `rerender_short` (free) → compare → repeat → keep the winner. **409** = a render is already running (keep polling); **422 `short_not_ready`** = nothing generated yet — run `generate_short` first; **422 `short_paid_regeneration_required`** = a footage/music input changed — run `generate_short` again. A re-render is never a credit error.

`update_short` returns a compact receipt (`updated`, `end_card_mode`, `resolved_cta_text`, `next`), not the full Short. Use `get_status` or the REST GET for authoritative state.

**Short creative fields** (`create_short` / `update_short`, also carried by `make_video`): `headline` (on-screen TITLE — ≈4 words; ≤40 chars hits hardest; hard cap 80), `subheadline` (secondary title, ≤200), `visual_language` (kinetic_creator / premium_editorial / cinematic_product / ugc_realism / startup_explainer / luxury_minimal — drives Veo direction AND the render's typographic identity), `creative_format` (problem_solution / mistake_fix / myth_vs_reality / before_after / proof_demo / product_reveal), `music_vibe` (Upbeat default / Cinematic / Minimal / Luxury / Playful / Jazz), `theme` (deprecated for shorts — legacy fallback when `visual_language` is unset), and fine styling `text_position` (top/center/bottom), `text_animation` (**auto** default / reveal / typewriter / fade_in / pop / bounce / word_stagger / word_spotlight), `font_family` (**auto** default, or one of 15 ShortFont* faces) — both default to `auto`, which follows the visual_language's typography; every concrete value (incl. fade_in / ShortFontSpaceGrotesk) is an explicit pick that wins — `headline_color` / `subheadline_color` / `accent_color`, `text_beats` (≤8 caption lines), `music_instruments` (≤10, each ≤40 chars), plus conversion graphics: **always set `cta_text`** (≤24, e.g. "Shop now" — blank `cta_text` → the server renders a neutral localized ask (e.g. "Learn more"); a truly CTA-less short is not currently supported), `offer_text` (≤16) only if there's a real deal, and `badge_text` (≤24) / `star_rating` (0–5) — opt-in, user-supplied only, never AI-written; **only ratings/badges you can substantiate — fabricated social proof is deceptive advertising**. End-card fields: `closing_claim` (≤80 — end-card claim, falls back to the headline), `brand_name` (≤40 — shown on the closing brand-lockup slate when no poster is set), `end_card` (`"auto"` default — poster or brand-lockup slate, 14s; `"none"` — no end card, 12s, loop-style). Populate at least the title/subtitle and a visual_language. `brand_profile_id` (from `list_brand_profiles`) on `create_short` seeds blank fields from a saved brand. `language` is a closed set — `en fr es de it nl pl pt ru zh ja ko ar` (no region subtags like `en-US`).

Other useful tools: `get_status` (check progress without blocking), `list_voices` (choose a voice whose normalized `languages` contains the editor language), `list_projects` (`include_completed: true` to recover a finished short's slug), `list_renders { slug }` (every editor render version plus thumbnail, duration, retryable/stale flags, snapshot hash, and presigned URL), `retry_render { slug, render_id }` (re-run a **failed** editor render from its saved snapshot, **0 credits**).

## Image carousels (sliders)

A **slider** is an image carousel (N still slides + a ready-to-post caption + hashtags), not a video — so `wait_for_completion`/`download_result` do **not** apply; poll `get_slider` instead.

1. `create_slider { prompt, mode, slide_count? }` — one prompt → a carousel draft (0 credits). `mode` is `creative` (story-driven) or `ad_driven` (facts-forward); `slide_count` 3–10.
2. `generate_slider { slug }` — render it (copy → AI backgrounds → composite). **1 credit per slide.**
3. Poll `get_slider { slug }` until `completed`, then download each `slides[].image_url` and post them with the returned `caption` + `hashtags`.

Tweak a finished carousel without re-paying: `restyle_slider { slug, ... }` (new template/accent/position — re-renders every slide, reusing the AI backgrounds) or `edit_slider_slide { slug, ... }` (rewrite one slide's headline/body/kicker and re-composite just that slide). Both **0 credits**; poll `get_slider` again until `completed`.

## Tracking-overlay videos (burn a CV overlay on your own clip)

`create_tracking_video { video_path, save_path?, classes?, color? }` — one shot: uploads a local clip, burns a computer-vision object-tracking overlay (convex-hull polygons hugging each moving subject + HUD brackets + telemetry), renders, polls, and downloads. Source clip **≤ 60s, ≤ 1080p**. Costs **1 credit** on render (refunded on failure). `classes` = COCO-80 class ids to track (e.g. `[0]` person, `[16]` dog; omit to track all subjects); `color` = `[r,g,b]` or `[r,g,b,a]`, each 0–255 (omit for white).

- On `terminal: false` (slow upload / cold GPU), call the tool **again with the returned `slug`** to resume — it reuses the in-flight upload (no re-upload, no re-charge). Or poll `wait_for_completion({ slug, kind: "tracking" })` then `download_result`.
- A completed slug is **final** (its source clip is deleted on success). For a variant (different `classes`/`color`), start a **new** project — omit `slug`, pass `video_path`. Re-uploading the same clip charges a fresh render.

## Granular control (direct every step, no autopilot)

When you want to author the ad yourself rather than let autopilot decide, drive the editor step by step. Autopilot (`create_editor_ad`) stays the default; this is the controlled path — all agent-driven, no human approval:

1. `create_editor_draft { product_prompt, language?, theme?, voice_id?, export_aspect_ratio?, project_intent? }` — creates an editor project **without** autopilot (0 credits).
2. `generate_scenario { slug, segments_count? }` (1 AI assist) **or** `set_scenario { slug, scenario_prompt }` (free) — `segments_count` is 3..10.
3. `get_editor { slug, response_format: "concise" }` — **review** scenario, `segments[]`, `narration_status`, `current_audio`, `current_music`, `latest_render`, and `ai_assist_quota` before spending. Omit the format only when you need the backward-compatible full payload.
4. `set_scene_count { slug, count }` (1..20; only removes trailing un-generated scenes). Each AI scene is a fixed **8s**, so this sets the ad's length (`count × 8s`); uploaded clips keep their own length.
5. `set_segment_prompt { slug, segment_id, prompt }` per scene (free, 1..2000 chars).
6. `generate_segment` one at a time, or `generate_all_segments { slug }` to run pending scenes sequentially (continuity preserved). 5 credits/scene; stop on first 402.
7. `set_narration_script { slug, script }` (free, ≤10000) **or** `generate_narration { slug }` (1 assist). AI narration requires every scene completed with a known positive duration and at least one scene without retained original audio; an exact retry deduplicates for free. Then add a music `description` (≤1200).
8. `generate_voice { slug, voice_id }` (3 credits; `voice_id` required — from `list_voices`); `generate_music { slug, description, mood?, genre?, tempo?, instruments? }` (5 credits). `instruments` is an array of up to 10 non-empty strings (≤80 chars each), not a comma-separated string.
9. `render { slug }` (0 credits — charges ungenerated or previously failed scenes) → `wait_for_completion({ slug, kind: "editor" })` → `download_result`.

## Bring your own media (local uploads)

An **editor** project can use the user's **own local files** — video clips on the timeline, or a product / closing / logo image — all **0 credits**:

1. `upload_video { slug, file_path, add_to_timeline? }` — uploads a local clip and waits until it's processed (`status: ready`). With `add_to_timeline` (the default) it also appends the clip as a finished scene; otherwise place it later with `add_segment_from_upload { slug, upload_id }`, or set a specific scene with `use_asset_for_segment { slug, segment_id, upload_id }` (also takes `source_segment_id` to reuse a finished scene).
2. Images: `set_product { slug, file_path, description? }`, `set_closing_image { slug, file_path }`, `set_logo { slug, file_path }`. For ordinary/new projects, the Editor product photo is a required product-identity reference for every generated AI scene; its confirmed description grounds scenario/narration, and the exact photo becomes the closing image unless a separate one was authored. Small generated label text can still vary. Replacing/removing it or changing its identity description invalidates completed AI scenes; if generation races the change and returns `editor_references_changed`, re-read and retry.

A **short** can also take an optional product reference image — `set_short_product { slug, file_path, description? }` — an exact deterministic logo overlay — `set_short_logo { slug, file_path, position?, treatment? }` (jpeg/png ≤5 MiB; never sent to the video model) — and an end-card poster — `set_short_poster { slug, file_path, poster_includes_lockup? }` (a closing still that extends the render to 14s; set `poster_includes_lockup: true` if the poster already contains brand name/CTA/badges, so the overlaid claim/CTA is suppressed and doesn't collide). Product/poster images are 0 credits and jpeg/png ≤20 MiB (20,971,520 bytes).

Video: mp4 / mov / webm / mkv, **≤500 MB decimal (500,000,000 bytes), ≤5 min**; multipart starts at **50 MiB (52,428,800 bytes)**. Editor product images: jpeg/png, **≤8 MiB (8,388,608 bytes)**. Editor closing and short images: jpeg/png, **≤20 MiB (20,971,520 bytes)**. Editor and short logos: jpeg/png, **≤5 MiB (5,242,880 bytes)**. No WebP. A freshly uploaded clip must finish processing before it can be placed — `upload_video` waits for you, so don't place an upload it reports as still processing. **Only upload files the user explicitly gave you** — never read arbitrary local paths.

An editor `video_url` may be the prior delivered MP4 after edits. Treat it as current only when normalized status has `ready:true` and `stale:false`; unknown/absent staleness is not evidence that it matches the live timeline.

## AI assists

Helper calls that have the model write content draw from a **free daily quota of 20** per account (separate from credits). Check with `get_ai_assists`; buy more with `unlock_ai_assists` (**1 credit → +10**).

- **Consume 1 assist:** `generate_scenario`, `enhance_prompt`, `suggest_next_scene`, `suggest_music_prompt`, `generate_narration` (per-segment regeneration is REST-only — `POST /api/editor/:slug/segments/:id/regenerate-narration`, not an MCP tool), `generate_short_text`.
- **Free writes:** `set_scenario`, `set_segment_prompt`, `set_narration_script`, add/reorder, `apply_scenario`, `render`.
- **Cost credits (not assists):** segment 5, regenerate 4, voice 3, music 5.

**Narration vs voice vs music are three separate meters — don't conflate them:** *drafting the narration script* (`generate_narration`) is **1 free-quota assist** (its "free" summary means free of *credits*, not quota); *synthesizing the voice-over* (`generate_voice`) is **3 credits**; *generating music* (`generate_music`) is **5 credits**. **429 `ai_assist_quota_exceeded`** = out of assists (unlock once or write it yourself; never loop); **402 `credits_insufficient`** = out of credits (stop).

## Delegate the whole content operation (plan → draft → review → schedule → measure)

Beyond one asset, you can run the surrounding content operation. **Everything through drafting is $0** — the planning tools draw the free AI-assist quota (not credits), and materializing a plan produces **0-credit drafts**. **Generating/rendering each draft is a separate, explicit, paid step** — create the drafts, then generate only the ones the user approves; never auto-spend a month of content. The whole loop is agent-scoped (`video:read` + `video:generate`); nothing needs `account:admin`.

1. **Onboard a product.** `extract_product_page { url }` pulls facts + candidate image/logo URLs from a public page (SSRF-guarded server-side, 0 credits). Persist the identity once so later drafts inherit it: `create_product_profile { name, ... }` + `create_brand_profile { name, ... }` (`list_*` / `update_*` manage them). Pull the primary image + logo in with `import_product_image { product_profile_id, url, link_as }` (`primary_image` default, or `logo`).
2. **Plan (free quota).** `plan_campaign { product_profile_id | product }` proposes angles/formats + a per-item credit **estimate**; `generate_hook_variations { ..., brand_profile_id? }` writes 3–6 draftable hook options (each a script + caption + hashtags + suggested format). On 429, `unlock_ai_assists` once or write the copy yourself.
3. **Pack & materialize ($0).** `create_campaign_pack { title?, product_profile_id?, brand_profile_id? }` is the container; `add_pack_variations { pack_id, variations, materialize: true }` persists the picks and turns each into an editable **draft** (≤12/call). `materialize_pack_item { pack_id, item_id }` (re)tries one; `get_campaign_pack { id }` / `list_campaign_packs` show each item's status + **draft slug** + estimate. Shortcut: `create_campaign { url | product_profile_id, brand_profile_id?, item_count?, formats? }` runs steps 1–3 in one call.
4. **Generate per draft (credits — a deliberate step).** For each approved video draft, call `start_autopilot { slug }` to get the free quote, then—only after approval—launch with `start_autopilot { slug, max_credits: approved_cap }`. Use `generate_slider { slug }` for an approved carousel. Nothing above this line spent video credits.
5. **Review (no-login).** `create_review_link { video_result_id | slider_id }` mints an expiring page where a human approves / requests changes / comments with **no account**. The token + URL come back **once** — surface them immediately. `list_review_links` shows activity; `revoke_review_link { id }` kills a link. Gate spend or posting on approval.
6. **Calendar (reminder-only).** `schedule_post { video_result_id, platform, scheduled_at }` stages a calendar entry that pushes a **reminder to the user** at the due time — it does **not** auto-publish. `list_scheduled_posts` is the calendar; `reschedule_post` / `cancel_scheduled_post` manage it; after the user posts, `mark_scheduled_posted { id }`. All 0 credits, all `video:generate`.
7. **Measure → double down.** `record_performance { item_id, platform, views, ... }` logs a snapshot for a pack item; `set_item_attributes { item_id, ... }` tags its creative. `get_recommendations` then returns a **cautious**, sample-size-aware read of your own best hook/format — **present its `confidence` + `note` verbatim; never claim causation, ROAS, or attribution.** `make_more_like_winner { item_id }` spins fresh hooks from a proven item → feed a chosen one back into `add_pack_variations` (step 3).
8. **Recurring shows.** `create_series { name, template, brand_profile_id?, product_profile_id? }` defines a repeating format; `plan_series_episodes { series_id }` drafts upcoming ideas (free quota); `materialize_episode { series_id, episode_id }` turns one into a 0-credit draft; `get_series_dashboard { series_id }` shows the upcoming / drafted / published lanes; `mark_episode_posted { series_id, episode_id }` closes the loop after a manual post.

**Reusable assets.** `list_assets` / `get_asset_quota` / `upload_asset { file_path, ... }` manage a cross-project media catalog (images + videos) so a clip/logo can be reused across projects. These need an **active subscription** (402 `subscription_required` otherwise) and cost **0 credits**; `upload_asset` reads are confined to `HUBFLUENCER_INPUT_DIR`.

## Field limits

| Field | Min | Max |
|-------|-----|-----|
| editor `product_prompt` (create) | 0 or 10 | 5000 |
| `scenario_prompt` | 1 | 50000 |
| `generate_scenario` `segments_count` | 3 | 10 |
| scene count (`set_scene_count`) | 1 | 20 |
| segment `prompt` | 1 | 2000 |
| `narration_script` | — | 10000 |
| music `description` (silently truncated) | — | 1200 |
| `voice_id` (`^[A-Za-z0-9_-]+$`, required) | 1 | 64 |
| `language` | 2 | 10 |
| short `headline` (title; keep ≈4 words / ≤40 chars) / `subheadline` | — | 80 / 200 |
| short `closing_claim` / `brand_name` (end card) | — | 80 / 40 |
| short `music_vibe` | — | 80 |
| AI assist daily quota | 0 | 20 |

## Credits

- **A credit is $0.99** bought singly; bulk packs and subscriptions cost less per credit. Translate credit counts into dollars for the user: a short (15cr) ≈ **$15 max**, an autopilot ad (~28cr) ≈ **$28 max**.
- **Confirm before spending:** quote the estimated credits + approximate dollar cost and get an explicit yes before the first charging call; for a batch, confirm the total once up front. Skip only when the user already approved that specific spend.
- Any draft create / edit / reorder = **0 credits**.
- Rendering a short = **15 credits**.
- Granular editor steps: generate segment 5, regenerate 4, **batch per-segment (batch of ≥3) 4** (so `render` bills any still-ungenerated scenes at the batch rate), voice 3, music 5, render 0 (charges ungenerated or previously failed scenes). Unlock AI assists = 1 credit → +10.
- Multi-scene editor ads (autopilot) ≈ **28** — preflight before rendering (the MCP surfaces this via the editor autopilot cost check; or `make_video` `dry_run`).
- Carousel render = **1 / slide** (3–10 slides); carousel restyle / per-slide edit = 0. Tracking render = **1** (refunded on failure).
- Onboarding / planning / packs / series / calendar / performance / review / profiles = **0 credits** (planning tools draw the free assist quota).
- Check the balance any time with `get_credits`.

## Result URLs

Downloaded result URLs are **presigned and expire in about 24 hours** — they are not permalinks. Download promptly via `make_video`'s `save_path` or `download_result`, and give the user the local file path.

## Failure handling

- **402 credits_insufficient** → tell the user the required vs. available credits, then **stop**. Do not loop or retry.
- **429 ai_assist_quota_exceeded** → daily AI-assist quota used up (NOT a credit problem). `unlock_ai_assists` once or write the content yourself; never loop.
- **409 already running** → a render is already in progress. **Keep polling** with `wait_for_completion` / `get_status`; do **not** start a new generation.
- **403 insufficient_scope** → the token is missing the `video:generate` scope. Ask the user to create a token with the "Generate videos" scope.
- **Long waits are normal.** A few minutes per render is expected; don't treat a slow poll as an error.

## Out of scope: publishing

Publishing directly to TikTok / Instagram / etc. requires a human-linked social account (interactive OAuth) and `account:admin`, which agent tokens never carry. **Do not auto-publish.** When the asset is ready, return the finished MP4 (or carousel images) plus a ready-to-paste caption so the user can post it themselves. You **can** stage a **reminder-only** calendar with `schedule_post` (it pushes a reminder to the user, never a publish) and close the loop with `mark_scheduled_posted` after they post — that's the supported "run my calendar" path.

**Label synthetic media.** These renders are AI-generated: TikTok requires an AI-generated content label (and attaches C2PA content credentials since 2025), and Meta requires disclosure for AI-generated/altered content in paid ads. Whenever you hand over an export or stage a posting reminder, tell the user to enable the platform's AI label.
