---
name: staglight-ads
description: Create and finish e-commerce ads (product photos, creator-style UGC and product videos, voiceovers, music, captions) in Spanish or English with the Staglight MCP tools and ffmpeg. Use when the user asks for a product ad, a Reels, Stories or TikTok video, product images, a voiceover, background music, captions, an ad review, new versions of the user's own clip with their characters, products or styles (Motion Remix), or to resize, subtitle or loudness-normalize an ad.
compatibility: Needs the staglight MCP server (https://staglight.ai/mcp), connected by signing in (OAuth) or with an API key, plus curl and ffmpeg (built with libass for captions).
metadata:
  version: "1.0"
  docs: "https://staglight.ai/en/docs"
---

# Staglight ads

Staglight generates ad assets from the user's prepaid credit balance (1 credit = US$0.01). The same balance pays for
the web studio, the REST API and these MCP tools. Every job has a price before it runs and failed jobs are refunded
automatically.

## Talk normally: the fastest path

The user should not need to know any tool names. They say what they want, often with a photo attached, and you do
the steps, asking only once: for the price.

Requests users type:
- "Make me a 15 second UGC ad for this serum, for Reels. Keep it under $3." (photo attached)
- "Here are 3 photos of my ceramic mug. Save it as a product and make a vertical product video in Spanish."
- "Turn this photo into a store carousel: white background, a lifestyle shot and a close-up of the label."
- "Something for TikTok that sells this water bottle to gym people. Tell me the price first."

Video ad, end to end:
1. Attached file: `upload_file` with `data` (the bytes as base64, or a `data:` URL) and `filename`. Up to
   12 MB (`upload_image` is the same tool). Bigger files: `upload_from_url` with a direct link.
2. `create_product` with `name` and `photos` (the `med_...` ids). Keep the `prd_...` id. Reuse it next time
   (`list_products`, `get_product`).
3. Price: `estimate_cost` with `tool=create_ad` and `arguments` `{format, product_id, tier: "draft", auto: true}`.
   Pick the format from the request (`list_formats`: UGC creator-style for "UGC", product hero, problem/solution...).
   Tell the user the price in credits and dollars and ask once. If they gave a budget ("under $3" = 300 credits)
   and the price fits, that is the yes.
4. `create_ad` with `auto: true`, `max_credits` = the approved price (or budget) and an `idempotency_key`. For a
   vague request, `create_ad_from_brief` with the brief, `product_id`, `auto: true` and the approved budget as
   `max_credits` (if the proposal costs more, only the planning step is charged and nothing starts).
5. Tell the user it is rendering, then `get_ad` with `wait_seconds=45` until `succeeded`. `auto` approves
   every review step with the default takes and never spends past `max_credits`: `spent` and `cap` show where it is.
6. If `auto_stopped` is set (`cap`, `balance` or `daily_limit`), the run waits at a review step and nothing more was
   charged. Tell the user why and what it needs (`auto_stop.needed`); with their OK call `approve_ad_step` with
   `auto: true` and `max_credits` = the new total for the whole run. `approve_ad_step` without `auto` continues step
   by step. A failed take (`needs_attention`) is refunded: `redo_ad_step` with `auto: true` and `max_credits` keeps
   it going on its own.
7. Download the `outputs` (`curl -L -o ads/<name> '<url>'`) and hand the file to the user.

Product images (carousel, statics, UGC photos): see "Ad Studio" below, also one question for the price.

## Money rules (always)

1. Call `estimate_cost` before every paid tool. It is free. Pass `tool` and `arguments` to price the exact call.
   Tell the user the price. Ask before spending more than they approved (default: ask above 200 credits).
2. Send `max_credits` (the approved price) and a fresh `idempotency_key` (a new UUID) with every paid call.
3. Paid tools return a `job_id` or `run_id` at once. Poll `get_job` or `get_ad` with `wait_seconds=45`
   until the status is final. Never block the user while waiting: say what is rendering and how long it may take.
4. After a timeout or an unclear error, never resend a paid call blindly. Call `list_jobs` with the same
   `idempotency_key` first. Re-sending with the same key and arguments is safe: it is not charged twice.
5. On `insufficient_credits`, call `buy_credits_link` and give the user the link. Payments only happen in the studio.
6. On `daily_limit_reached`, stop and tell the user: the key's daily cap is set at https://staglight.ai/app/api.

## Tools

| Need | Tool | Cost |
|---|---|---|
| Balance and daily cap | `get_balance` | free |
| Price of a call | `estimate_cost` | free |
| Models and their fields | `list_models` (pass `model` for allowed values) | free |
| File attached in the chat | `upload_file` (or its alias `upload_image`), base64 up to 12 MB | free |
| Product photo from a link | `upload_from_url` (direct file links only) | free |
| Saved products | `create_product`, `get_product`, `list_products` | free |
| Files | `list_media` | free |
| Product image | `generate_image` (default model `nano-banana-pro`, pass `reference_images`) | paid |
| Video clip | `generate_video` (`first_frame` = a product image) | paid |
| Voiceover | `generate_voice` | paid |
| Music | `generate_music` | paid |
| Upscale, cut out | `upscale`, `remove_background` | paid |
| Captions, merge, clean copy | `add_captions`, `merge_videos`, `clean_export` | paid, flat |
| Ad review | `analyze_ad` (report in `get_job` data) | paid, flat |
| Any other op | `create_job` with an op id from `list_models` | paid |
| Job status and files | `get_job`, `list_jobs`, `cancel_job` | free |
| Complete ad | `list_formats`, `create_ad` (`auto: true` to run to the end), `get_ad`, `approve_ad_step`, `redo_ad_step`, `cancel_ad` | paid steps |
| Ad from a written brief | `propose_ad`, `create_ad_from_brief` | paid planning step |
| New versions of the user's clip | `motion_remix`, then `get_ad`, `approve_ad_step`, `redo_ad_step` | paid per version |
| A channel's series or a shop's project, kept between takes | `studio_series`, `studio_series_save` (free), then `series` on `studio_check`, `studio_estimate`, `studio_run` | free to keep |
| The same for an ad format, Script Runner or Motion Remix | `ad_series`, `ad_series_save` (free), then `series` on `create_ad`, `motion_remix` | free to keep |
| Product images (Ad Studio) | `studio_list`, `studio_estimate` (free), `studio_run` (paid), `studio_get_run` (free) | paid per image |
| Video ad from a product link | `studio_helper` (studio `link_ad`, helper `link_scan`), then `studio_run` | 1 credit, then the video |
| Buy credits | `buy_credits_link` | free |

## One asset

1. Get the product photo: `list_products`, or `upload_from_url` with a direct image link. Keep the `med_...` id.
2. `estimate_cost` with `tool=generate_image` (or the tool you need) and the exact arguments.
3. Call the tool with `max_credits` and an `idempotency_key`.
4. `get_job` with `wait_seconds=45` until `succeeded`. Results come as links (resource_link) to https files.
5. Download: `curl -L -o ads/<name> '<url>'`. Links last about 24 h; call `get_job` again for fresh ones. Files are
   kept 30 days.

Image to video: generate or upload a still first, then `generate_video` with `first_frame` set to that media id.
Draft first: cheap models and 480p/720p for drafts, then the final render once the user likes the take.

## Complete ad (format run)

1. `list_formats`, pick one with the user (UGC creator-style, product hero, problem/solution, 3 reasons, unboxing,
   ASMR, before/after, offer static, from a script...).
2. `estimate_cost` with `tool=create_ad`. Start with `tier=draft`.
3. `create_ad` with `format`, `product_id` (or `product` with name and `photos`), `tier`, `aspect`, `lang`.
   Only the plan step is charged now.
4. `get_ad` with `wait_seconds=45`. When `status` is `awaiting_approval`, show the user the takes (links
   in the result) and the script. `next_step_credits` says what approving will start.
5. With the user's OK: `approve_ad_step` (with `max_credits`). For a different take: `redo_ad_step` with `index`
   and an optional `prompt` (charged again). To render the step at a better tier: `choices.render`.
6. Repeat until `succeeded`, then download `outputs`.

From a plain-language brief: `propose_ad` (planning step only) returns a format, options, price and reasons. Show
it, then `create_ad` with those values, or `create_ad_from_brief` to do both in one call.

## Motion Remix (new versions of the user's clip)

One clip of the user's own (2 to 30 s) and 1 to 8 images or styles in; one new version per image or style out.

Modes (`mode`):
- `move`: the character in each image performs the clip's motion. Engines: `kling_motion` (default), `wan_move`,
  `seedance_ref` (no images with human faces: better for pets, products and faceless characters).
- `replace`: the clip keeps its scene, camera and other people; the main person becomes the character. Engines:
  `wan_replace` (default, the whole person), `seedance_mask` (precise swap of what `target` names: best for mascots,
  figures, toys and cartoon or 3D characters; real faces are often refused, refunded), `wan_edit`, `omni_edit`,
  `happyhorse_edit`. Replace needs `likeness: true`: ask the user whether any real person in the images is them or an
  adult who agreed, and no public figure. Never children or teens.
- `restyle`: the whole clip in a new look, one version per entry of `styles` (a text like "claymation", or
  `{prompt, image}`). Engines: `wan_edit` (up to 10 s), `omni_edit` (up to 15 s), `happyhorse_edit` (up to 30 s);
  `auto` picks the cheapest one whose length limit fits the clip.
- `object_swap`: one object becomes the product image. Say which one in `target`, in one or two English words works
  best ("bottle", "mug"). Default engine `seedance_mask` (precise swap): the run first outlines that object on every
  frame and stops at step `mask`. Show the user the review image (top: the clip with the green outline; bottom: what
  the model gets) and the composite clip, then `approve_ad_step` with `step=mask` and `choices: {"mask": "<mask_id
  from remix.mask>"}`, or `redo_ad_step` with `step=mask` and `prompt` = another word (charged again). Drafts start
  only after that. A person or face as `target` needs `likeness: true` too.

Steps:
1. Upload the clip and the images (`upload_from_url`, or the studio). Keep the `med_...` ids.
2. Ask the user to confirm they have the right to use the clip and the images, then send `own_footage: true`.
   Never set it without asking. No celebrities, real people's likeness or known characters.
3. `estimate_cost` with `tool=motion_remix` and the exact arguments first. The result lists every engine with its
   draft price, its finishing price per version (`finish_each`, the final render plus the 5-credit finishing step)
   and its `final_mode`. Tell the user the price now and the price to finish each version.
4. `motion_remix` with `tier=draft` (default; `tier=final` finishes every version at once), `max_credits` and an
   `idempotency_key`. Only the drafts are charged now, one per version.
5. `get_ad` with `wait_seconds=45` until `awaiting_approval`. Show the drafts (links in the result).
6. With the user's pick: `approve_ad_step` with `step=drafts` and `choices: {"variants": ["v1", "v3"]}`. Only the
   picked versions are finished and charged. `final_mode: rerender` (every engine but `seedance_ref`) means the
   final is generated again at full quality and can differ from the draft: say so before the user picks.
   `upgrade` (`seedance_ref`) improves the picked draft itself.
7. A failed or refused version is refunded on its own. Retry it with `redo_ad_step` (`step=drafts` or `finals`,
   `variant`, optional `engine` from the same mode), priced first with `estimate_cost`. While a retried draft
   renders the run is `running` again: keep polling `get_ad` before you pick.
8. Download the `outputs` when `succeeded` (REST: `GET /api/v1/runs/{id}/outputs.zip` gives them all in one zip).

Sound (`audio`): `original` keeps the clip's sound, `mine` uses the user's track (`audio_file`), `none` makes a
silent video. Tip for trending sounds: finish with `audio: "none"` and add the sound in TikTok, Reels or Shorts
when posting, from the app's own licensed library. Finished videos keep the clip's shape (`aspect: auto`) and carry
a small domain watermark unless `watermark: false`.

## Ad Studio (product images)

Studios for still images: store carousel, static ads, UGC-style photos, Instagram posts, photo edits and more. Each
studio has cards (the kind of image) and inputs.
1. `studio_list` for the studios; `studio_list` with `studio` for its cards, inputs and drops (the photo slots).
2. Put the uploaded photo ids in `media` (drop key -> list of `med_...` ids), the card in `card`, the text inputs in
   `inputs`. Prices, ratings and quotes appear only when the user typed them; list their keys in `real` only after
   the user confirms they are real.
3. `studio_estimate` (free) with the same click; tell the user the price and ask once.
4. `studio_run` with `max_credits` and an `idempotency_key`. Each image is one job, charged now and refunded if it
   fails. It returns an `srun_...` id.
5. `studio_get_run` with `wait_seconds=45` until `succeeded` (or `partial`): it lists every job, follow-up
   steps included (a cut-out after a white packshot, for example), and the finished files as links. `get_job` on one
   job also names its `studio_run_id` and `next_job_id`; `list_jobs` takes `studio_run_id`.

## Video ad from a product link

When the user only gives a product page link ("make me an ad for https://..."):
1. `studio_helper` with `studio: "link_ad"`, `name: "link_scan"`, `payload: {"url": "..."}`, `max_credits: 1`
   and always an `idempotency_key` (a new one per link; reuse it on a retry, because a retry without it reads the
   page again and charges again). It reads only that page (nothing is charged when it cannot be read) and returns
   `data.photos` (the product photos, stored as the user's uploads), `data.best`, `data.name`, `data.line` and
   `data.summary`. Show the user the name, the line and the photo you will use.
2. `studio_estimate`, then `studio_run` on `link_ad` with `card` (`talk`, `demo` or `scene`),
   `media: {"product": [data.best]}` and `inputs: {"name", "description": data.line, "product_info": data.summary}`.

## Finish with ffmpeg

Recipes are in `references/ffmpeg.md`. `scripts/finish_ad.sh in.mp4 out.mp4 9:16` fits a take to the aspect, sets
loudness to -14 LUFS (two passes) and adds faststart.

Defaults: 9:16 at 1080x1920 for Reels, Stories and TikTok; 4:5 at 1080x1350 for Feed; H.264 yuv420p, AAC 48 kHz,
`-movflags +faststart`. Captions as ASS with `PlayResX: 1080` and `PlayResY: 1920`, fonts that cover ñ, á, ¿ and ¡.

## Don'ts

- Don't remove or hide the AI-generated label or disclosure. `clean_export` and the metadata recipe only remove GPS,
  device and encoder tags.
- Don't present creator-style videos as real customer reviews or testimonials.
- No celebrities, real people's likeness, copyrighted characters, competitor brand names or health and weight
  result claims.
- Motion control and lip-sync take only the user's own or licensed footage (`own_footage: true`).
- No social media downloading: `upload_from_url` takes direct file links.
- Never print or store the API key in files, logs or chat.
