Skip to main content
Glama
Hermoso

Hermoso

Official

Render ad video

render_ad

Renders a finished MP4 video ad, Reel, TikTok ad, UGC clip, or commercial from a creative plan, using brand/product assets, with timed shots, clean speech, and optional captions, music, or end card.

Instructions

Use this when the user asks for a video ad, Reel, TikTok ad, UGC video or commercial for their business or product. It renders a finished ad on a real video model, on their brand (real product photos, brand voice), as an mp4 ready to post: timed shots, clean speech, text set in post, no music, captions or end card unless asked. Pass plan_ad’s full output as creative (plan_ad turns a one-line ask into the concept). UGC and cinematic plans render from a painted STORYBOARD (a cinematic spot paints a location anchor first, and a hero when someone is on camera); a product-only spot renders from the real product photo, and board:true or foundations:'choose' adds an identity sheet, a moodboard and a board. A plan that fits one clip of the render model renders as one continuous pass; a longer one as STITCHED ACTS (the fewest balanced clips), never time-compressed. Most models cap a clip at 15s and the longest at 30s: dryRun:true shows the act split free, before spending. CAST A SAVED CREATOR with creator (list_creators) so the same person stars in every ad; otherwise each render casts a new face. The user’s own pictures (creative.inserts, or inserts) are laid in full screen at their seconds. An app shown on a device screen with no real screens to lay there returns needsAppScreens, nothing charged: ask, then pass screenChoice or screens. 1–3 min; poll get_job while it renders. Spends credits.

Input Schema

NameRequiredDescriptionDefault
boardNopaint the storyboard board first (default: on for UGC and cinematic spots, off for a product-only commercial); false = render from the text shot list
modelNovideo model id from hermoso_capabilities (default: the engine the router picks for the plan: seedance-2.5 for every ad, a person talking to camera included). Naming one is a DELIBERATE pick — the server asks before ever swapping it (no silent fallback)
musicNomusic bed: OFF unless asked. true = the plan's own music line; or the bed in words ('lo-fi jazz, brushed drums'); false = none
dryRunNoreturn the routing decision (single pass vs stitched acts, resolved model + act lengths) and the estimated credits the real render reserves, WITHOUT submitting a render — free, nothing charged
lockupNobrand wordmark + tagline composited over the closing seconds. DEFAULT FALSE — set true ONLY when the user asks for branding on the close
creatorNoCAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”), or a PRESET AI creator from list_creators presets by exact name or id (free, no generation). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand (casting skips the portrait we would otherwise generate). Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default when the plan has a person on camera and the account is on a paid Hermoso plan (a free account gets a fresh AI person), and the read-back says `default:true`; pass "none" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if a REAL person is cast on a free plan. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.
endCardNoappend the branded end card. DEFAULT FALSE on every recipe — set true ONLY when the user asks for an end card (a clone of a video that had none should not grow one)
insertsNothe user's own screens laid over the finished video at these seconds (each item's look says how), sound running underneath (about 2 credits). The film always opens and ends on moving footage. Omit to use plan_ad's creative.inserts; [] renders without them. plan_ad with inserts writes the board around them.
screensNoreal screenshots of the app or site for the shots that film it on a screen (URLs, upload_file URLs or Library ids), laid over them as cards
captionsNoburn the plan's per-scene on-screen words as caption pills. DEFAULT FALSE on every recipe — set true ONLY when the user asks for on-screen text or captions; no recipe turns them on by itself
creativeYesthe FULL structured output of plan_ad (must contain video_storyboard)
ttsVoiceNovoiceover voice name (e.g. Rachel / George) when the plan voices over
faceRouteNo'face_lane' records the user's statement "I own the rights to this face" for the real person in their reference (paid plans; the likeness terms apply). Set it only when the user picked that option after a render said it could not use that face, and send the same request again; it then renders through Hermoso's face library at the normal price. Never set it on your own. The other options are a different `model` or one of the user's saved `creator`s.
textStyleNoTHE LOOK of captions and the end card, only with captions or endCard and only when the user described one: the look in WORDS ("chunky yellow comic letters, purple outline"), a preset (editorial: big serif title + small italic line; bold: condensed caps, outline; minimal; handwritten; boxed; pill, the default), or fields. "TITLE · small line" puts the part after the dot on a second line.
musicModelNocompose the bed on minimax-music-3 or lyria-3.5, skipping the free library
resolutionNoOmit = the default tier, hermoso_capabilities defaultResolution (720p), the tier its `credits` prices are for. 1080p is ~2.25x, 4k more, 480p less: quote creditsByRes and pass the tier you quoted. A tier a model's `resolutions` lacks renders at its best one.
aspectRatioNooutput aspect ratio, e.g. 9:16 (default) / 1:1 / 16:9
foundationsNoproduct commercial / cinematic: 'choose' paints ONLY the foundations (identity sheet + four-up moodboard, or hero + location) and returns them, no video — then call again with moodboardPick + foundationImages
screenChoiceNothe user's answer to needsAppScreens: brand = their saved app screens, site = found on their website (saved to the brand), without = none, device kept turned away
moodboardPickNowhich moodboard panel (1-4, left to right, top to bottom) the storyboard follows; default 1
durationSecondsNototal ad length in seconds, 4–180 (clamped). Omit to keep the plan’s own length, almost always right: this only RE-TIMES the authored board; to change the ad’s length, re-run plan_ad with durationSeconds. A length that fits ONE clip renders in one pass; longer is stitched from acts of the model’s clip maximum (15s on most, 30s on the longest-clip one), remainder last. dryRun:true shows the split free.
foundationImagesNoimages a foundations:'choose' call returned, reused as-is (not repainted)
allowGenericProductNoproceed even though this brand has NO product photo on file and the ad features a product — the packaging will be INVENTED. Only pass true after telling the user that and hearing they are fine with a generic stand-in

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv0.1.525
    • changedInput schema / properties / textStyle / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "properties": {
      -      "background": {
      -        "description": "none|pill|a colour",
      -        "type": "string"
      -      },
      -      "cardColor": {
      -        "type": "string"
      -      },
      -      "color": {
      -        "description": "any CSS colour",
      -        "type": "string"
      -      },
      -      "describe": {
      -        "description": "the look in words",
      -        "type": "string"
      -      },
      -      "font": {
      -        "description": "sans|serif|elegant|condensed|hand or any Google Fonts family",
      -        "type": "string"
      -      },
      -      "italic": {
      -        "type": "boolean"
      -      },
      -      "outline": {
      -        "type": "boolean"
      -      },
      -      "outlineColor": {
      -        "type": "string"
      -      },
      -      "position": {
      -        "description": "top|center|lower|bottom or 0.05-0.95 from the top",
      -        "type": [
      -          "string",
      -          "number"
      -        ]
      -      },
      -      "preset": {
      -        "type": "string"
      -      },
      -      "shadow": {
      -        "type": "boolean"
      -      },
      -      "size": {
      -        "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction",
      -        "type": [
      -          "string",
      -          "number"
      -        ]
      -      },
      -      "subFont": {
      -        "type": "string"
      -      },
      -      "subItalic": {
      -        "type": "boolean"
      -      },
      -      "textCase": {
      -        "enum": [
      -          "as-is",
      -          "upper",
      -          "lower",
      -          "title"
      -        ],
      -        "type": "string"
      -      },
      -      "tilt": {
      -        "description": "degrees, ±45",
      -        "type": "number"
      -      },
      -      "weight": {
      -        "type": "number"
      -      }
      -    },
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "type": "string"
      +  },
      +  {
      +    "properties": {
      +      "background": {
      +        "description": "none|pill|a colour",
      +        "type": "string"
      +      },
      +      "cardColor": {
      +        "type": "string"
      +      },
      +      "color": {
      +        "description": "any CSS colour",
      +        "type": "string"
      +      },
      +      "describe": {
      +        "description": "the look in words",
      +        "type": "string"
      +      },
      +      "font": {
      +        "description": "sans|serif|elegant|condensed|hand or any Google Fonts family",
      +        "type": "string"
      +      },
      +      "highlightColor": {
      +        "description": "word-timed subtitles only: the word being said is lit in this colour (preset highlight sets it)",
      +        "type": "string"
      +      },
      +      "italic": {
      +        "type": "boolean"
      +      },
      +      "outline": {
      +        "type": "boolean"
      +      },
      +      "outlineColor": {
      +        "type": "string"
      +      },
      +      "position": {
      +        "description": "top|center|lower|bottom or 0.05-0.95 from the top",
      +        "type": [
      +          "string",
      +          "number"
      +        ]
      +      },
      +      "preset": {
      +        "type": "string"
      +      },
      +      "shadow": {
      +        "type": "boolean"
      +      },
      +      "size": {
      +        "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction",
      +        "type": [
      +          "string",
      +          "number"
      +        ]
      +      },
      +      "subFont": {
      +        "type": "string"
      +      },
      +      "subItalic": {
      +        "type": "boolean"
      +      },
      +      "textCase": {
      +        "enum": [
      +          "as-is",
      +          "upper",
      +          "lower",
      +          "title"
      +        ],
      +        "type": "string"
      +      },
      +      "tilt": {
      +        "description": "degrees, ±45",
      +        "type": "number"
      +      },
      +      "weight": {
      +        "type": "number"
      +      }
      +    },
      +    "type": "object"
      +  }
      +]
  2. Changed10 schema fields changedv0.1.519
    • changedInput schema / properties / inserts / description
      Previous value: -"the user's own screens laid over the finished video at these seconds, as a card over the playing footage by default (look 'full' covers the frame, only when asked), sound running underneath (about 2 credits). The film always opens and ends on moving footage. Omit to use plan_ad's creative.inserts; [] renders without them. plan_ad with inserts writes the board around them."New value: +"the user's own screens laid over the finished video at these seconds (each item's look says how), sound running underneath (about 2 credits). The film always opens and ends on moving footage. Omit to use plan_ad's creative.inserts; [] renders without them. plan_ad with inserts writes the board around them."
    • addedInput schema / properties / inserts / items / properties / focus
      Added value: +{
      +  "description": "the readable part to fill the frame with, as fractions of the picture from its top-left (e.g. the flagged-items table); a crop as tall as wide, or taller, reads best on a phone. Omit for the whole picture, plain margins trimmed",
      +  "properties": {
      +    "h": {
      +      "type": "number"
      +    },
      +    "w": {
      +      "type": "number"
      +    },
      +    "x": {
      +      "type": "number"
      +    },
      +    "y": {
      +      "type": "number"
      +    }
      +  },
      +  "required": [
      +    "x",
      +    "y",
      +    "w",
      +    "h"
      +  ],
      +  "type": "object"
      +}
    • addedInput schema / properties / inserts / items / properties / key
      Added value: +{
      +  "description": "true = the story needs this one (the payoff): never left out when there are more screens than the length takes",
      +  "type": "boolean"
      +}
    • changedInput schema / properties / inserts / items / properties / kind / description
      Previous value: -"screen (default): an app, a page, a dashboard, a document, a review, a chart. photo: a person, a product or a place, which is NOT laid over the video (a product photo goes in as the brand's product photo, a person to star as a saved creator)"New value: +"screen (default): an app, page, dashboard, document, review or chart. photo: a person, product or place, NOT laid over the video (a product photo goes in as the brand's product photo, a person as a saved creator)"
    • changedInput schema / properties / inserts / items / properties / look / description
      Previous value: -"card (default): a rounded card over the upper middle of the footage, which keeps playing. full: covers the whole frame, ONLY when the user asked for full-screen"New value: +"cutaway (default): a hard cut to the screen filling the frame, cropped to focus, held still, never enlarged past 1.25x its pixels (any gap is the screenshot's own background colour; a screen too small to read is left out and said). card: picture-in-picture over the playing footage, clear of the speaker's face. full: the whole picture uncropped on a blurred backdrop. card and full ONLY when the user asked"
    • changedInput schema / properties / inserts / items / properties / look / enum
      Previous value: -[
      -  "card",
      -  "full"
      -]New value: +[
      +  "cutaway",
      +  "card",
      +  "full"
      +]
    • changedInput schema / properties / inserts / items / properties / start / description
      Previous value: -"second it appears (never before 1 s: the film opens on moving footage)"New value: +"second it appears, never before 1 s (the film opens on moving footage)"
    • changedInput schema / properties / resolution / description
      Previous value: -"'1080p' default; '480p'/'720p' cheaper drafts, '4k' premium (more credits). A model renders only the tiers in its own `resolutions` list in hermoso_capabilities (the 30s model tops out at 720p); a tier it lacks renders at its best one without a word, so check before promising 1080p or 4k."New value: +"Omit = the default tier, hermoso_capabilities defaultResolution (720p), the tier its `credits` prices are for. 1080p is ~2.25x, 4k more, 480p less: quote creditsByRes and pass the tier you quoted. A tier a model's `resolutions` lacks renders at its best one."
    • addedInput schema / properties / screenChoice
      Added value: +{
      +  "description": "the user's answer to needsAppScreens: brand = their saved app screens, site = found on their website (saved to the brand), without = none, device kept turned away",
      +  "enum": [
      +    "brand",
      +    "site",
      +    "without"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / screens
      Added value: +{
      +  "description": "real screenshots of the app or site for the shots that film it on a screen (URLs, upload_file URLs or Library ids), laid over them as cards",
      +  "items": {
      +    "type": "string"
      +  },
      +  "maxItems": 6,
      +  "type": "array"
      +}
  3. Changed1 schema field changedv0.1.489
    • addedInput schema / properties / musicModel
      Added value: +{
      +  "description": "compose the bed on minimax-music-3 or lyria-3.5, skipping the free library",
      +  "type": "string"
      +}
  4. Changed4 schema fields changedv0.1.465
    • addedInput schema / properties / foundationImages / properties / hero / description
      Added value: +"hero image URL from a foundations:'choose' call; reused as-is"
    • addedInput schema / properties / foundationImages / properties / identitySheet / description
      Added value: +"identity sheet image URL from a foundations:'choose' call; reused as-is"
    • addedInput schema / properties / foundationImages / properties / location / description
      Added value: +"location image URL from a foundations:'choose' call; reused as-is"
    • addedInput schema / properties / foundationImages / properties / moodboard / description
      Added value: +"moodboard image URL from a foundations:'choose' call; reused as-is"
  5. Changed3 schema fields changedv0.1.454
    • removedInput schema / properties / music / anyOf
      Removed value: -[
      -  {
      -    "type": "boolean"
      -  },
      -  {
      -    "type": "string"
      -  }
      -]
    • addedInput schema / properties / music / type
      Added value: +[
      +  "boolean",
      +  "string"
      +]
    • changedInput schema / properties / textStyle / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "properties": {
      -      "background": {
      -        "description": "none|pill|a colour",
      -        "type": "string"
      -      },
      -      "cardColor": {
      -        "type": "string"
      -      },
      -      "color": {
      -        "description": "any CSS colour",
      -        "type": "string"
      -      },
      -      "describe": {
      -        "description": "the look in words",
      -        "type": "string"
      -      },
      -      "font": {
      -        "description": "sans|serif|elegant|condensed|hand or any Google Fonts family",
      -        "type": "string"
      -      },
      -      "italic": {
      -        "type": "boolean"
      -      },
      -      "outline": {
      -        "type": "boolean"
      -      },
      -      "outlineColor": {
      -        "type": "string"
      -      },
      -      "position": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "number"
      -          }
      -        ],
      -        "description": "top|center|lower|bottom or 0.05-0.95 from the top"
      -      },
      -      "preset": {
      -        "type": "string"
      -      },
      -      "shadow": {
      -        "type": "boolean"
      -      },
      -      "size": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "number"
      -          }
      -        ],
      -        "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction"
      -      },
      -      "subFont": {
      -        "type": "string"
      -      },
      -      "subItalic": {
      -        "type": "boolean"
      -      },
      -      "textCase": {
      -        "enum": [
      -          "as-is",
      -          "upper",
      -          "lower",
      -          "title"
      -        ],
      -        "type": "string"
      -      },
      -      "tilt": {
      -        "description": "degrees, ±45",
      -        "type": "number"
      -      },
      -      "weight": {
      -        "type": "number"
      -      }
      -    },
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "type": "string"
      +  },
      +  {
      +    "properties": {
      +      "background": {
      +        "description": "none|pill|a colour",
      +        "type": "string"
      +      },
      +      "cardColor": {
      +        "type": "string"
      +      },
      +      "color": {
      +        "description": "any CSS colour",
      +        "type": "string"
      +      },
      +      "describe": {
      +        "description": "the look in words",
      +        "type": "string"
      +      },
      +      "font": {
      +        "description": "sans|serif|elegant|condensed|hand or any Google Fonts family",
      +        "type": "string"
      +      },
      +      "italic": {
      +        "type": "boolean"
      +      },
      +      "outline": {
      +        "type": "boolean"
      +      },
      +      "outlineColor": {
      +        "type": "string"
      +      },
      +      "position": {
      +        "description": "top|center|lower|bottom or 0.05-0.95 from the top",
      +        "type": [
      +          "string",
      +          "number"
      +        ]
      +      },
      +      "preset": {
      +        "type": "string"
      +      },
      +      "shadow": {
      +        "type": "boolean"
      +      },
      +      "size": {
      +        "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction",
      +        "type": [
      +          "string",
      +          "number"
      +        ]
      +      },
      +      "subFont": {
      +        "type": "string"
      +      },
      +      "subItalic": {
      +        "type": "boolean"
      +      },
      +      "textCase": {
      +        "enum": [
      +          "as-is",
      +          "upper",
      +          "lower",
      +          "title"
      +        ],
      +        "type": "string"
      +      },
      +      "tilt": {
      +        "description": "degrees, ±45",
      +        "type": "number"
      +      },
      +      "weight": {
      +        "type": "number"
      +      }
      +    },
      +    "type": "object"
      +  }
      +]
  6. Changed6 schema fields changedv0.1.435
    • changedInput schema / properties / creator / description
      Previous value: -"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”), or a PRESET AI creator from list_creators presets by exact name or id (free, no generation). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default when the plan has a person on camera and the account is on a paid Hermoso plan (a free account gets a fresh AI person), and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if a REAL person is cast on a free plan. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."New value: +"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”), or a PRESET AI creator from list_creators presets by exact name or id (free, no generation). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand (casting skips the portrait we would otherwise generate). Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default when the plan has a person on camera and the account is on a paid Hermoso plan (a free account gets a fresh AI person), and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if a REAL person is cast on a free plan. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
    • changedInput schema / properties / dryRun / description
      Previous value: -"return the routing decision (single pass vs stitched acts, resolved model + act lengths) and the exact credits the real render reserves, WITHOUT submitting a render — free, nothing charged"New value: +"return the routing decision (single pass vs stitched acts, resolved model + act lengths) and the estimated credits the real render reserves, WITHOUT submitting a render — free, nothing charged"
    • changedInput schema / properties / durationSeconds / description
      Previous value: -"total ad length in seconds (supported range 4–180; outside that it is clamped). Omit to honor the plan’s own duration — that is almost always right. This only RE-TIMES an already-authored board (its scenes are scaled to fit), it does NOT re-write it, so to change the length of the ad the user asked for, re-run plan_ad with durationSeconds instead. A length that fits ONE clip of the render model renders as one continuous pass; longer is stitched from acts filled to that model’s clip maximum with the remainder last — the maximum is 15s on most models and 30s on the longest-clip one, so use dryRun:true to see the exact act split for free before spending."New value: +"total ad length in seconds, 4–180 (clamped). Omit to keep the plan’s own length, almost always right: this only RE-TIMES the authored board; to change the ad’s length, re-run plan_ad with durationSeconds. A length that fits ONE clip renders in one pass; longer is stitched from acts of the model’s clip maximum (15s on most, 30s on the longest-clip one), remainder last. dryRun:true shows the split free."
    • addedInput schema / properties / inserts
      Added value: +{
      +  "description": "the user's own screens laid over the finished video at these seconds, as a card over the playing footage by default (look 'full' covers the frame, only when asked), sound running underneath (about 2 credits). The film always opens and ends on moving footage. Omit to use plan_ad's creative.inserts; [] renders without them. plan_ad with inserts writes the board around them.",
      +  "items": {
      +    "properties": {
      +      "end": {
      +        "description": "second it ends: at most 2 s after start (2.5 s full-screen), never in the last 1.5 s of the film",
      +        "type": "number"
      +      },
      +      "image": {
      +        "description": "an https or upload_file URL, a Library id (L0, L1…) or a local path on the CLI",
      +        "type": "string"
      +      },
      +      "kind": {
      +        "description": "screen (default): an app, a page, a dashboard, a document, a review, a chart. photo: a person, a product or a place, which is NOT laid over the video (a product photo goes in as the brand's product photo, a person to star as a saved creator)",
      +        "enum": [
      +          "screen",
      +          "photo"
      +        ],
      +        "type": "string"
      +      },
      +      "look": {
      +        "description": "card (default): a rounded card over the upper middle of the footage, which keeps playing. full: covers the whole frame, ONLY when the user asked for full-screen",
      +        "enum": [
      +          "card",
      +          "full"
      +        ],
      +        "type": "string"
      +      },
      +      "start": {
      +        "description": "second it appears (never before 1 s: the film opens on moving footage)",
      +        "type": "number"
      +      }
      +    },
      +    "required": [
      +      "image",
      +      "start",
      +      "end"
      +    ],
      +    "type": "object"
      +  },
      +  "maxItems": 8,
      +  "type": "array"
      +}
    • changedInput schema / properties / model / description
      Previous value: -"video model id from hermoso_capabilities (default: the plan’s pick). Naming one is a DELIBERATE pick — the server asks before ever swapping it (no silent fallback)"New value: +"video model id from hermoso_capabilities (default: the engine the router picks for the plan: seedance-2.5 for every ad, a person talking to camera included). Naming one is a DELIBERATE pick — the server asks before ever swapping it (no silent fallback)"
    • changedInput schema / properties / resolution / description
      Previous value: -"'1080p' default (what we ship and bill for); '480p'/'720p' = cheaper draft passes, '4k' = premium final delivery (more credits). NOT EVERY MODEL OFFERS EVERY TIER — this enum is what the tool accepts, and each model's OWN `resolutions` list in hermoso_capabilities is what it can actually render (the longest-clip 30s model, for one, tops out at 720p). Ask for a tier the chosen model does not list and it is rendered at that model's best available tier instead, with nothing in the reply saying so — so check `resolutions` before promising anyone 1080p or 4k."New value: +"'1080p' default; '480p'/'720p' cheaper drafts, '4k' premium (more credits). A model renders only the tiers in its own `resolutions` list in hermoso_capabilities (the 30s model tops out at 720p); a tier it lacks renders at its best one without a word, so check before promising 1080p or 4k."
  7. Changed1 schema field changedv0.1.410
    • changedInput schema / properties / faceRoute / description
      Previous value: -"ONLY after a render came back saying the video model's safety check flagged a person's face: 'face_lane' is the user's choice \"I own the rights to this face\". Send the SAME request again with it and the same face is rendered on Seedance through the face library, at the normal price. Sending it is the user's confirmation that they have the rights to that face (paid plans, like every real face). Never set it on your own; the other choices that refusal names are a different model (`model`) or another creator (`creator`)."New value: +"'face_lane' records the user's statement \"I own the rights to this face\" for the real person in their reference (paid plans; the likeness terms apply). Set it only when the user picked that option after a render said it could not use that face, and send the same request again; it then renders through Hermoso's face library at the normal price. Never set it on your own. The other options are a different `model` or one of the user's saved `creator`s."
  8. Changed7 schema fields changedv0.1.366
    • addedInput schema / properties / board
      Added value: +{
      +  "description": "paint the storyboard board first (default: on for UGC and cinematic spots, off for a product-only commercial); false = render from the text shot list",
      +  "type": "boolean"
      +}
    • changedInput schema / properties / creator / description
      Previous value: -"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default on any plan with a person on camera, and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, or if an explicitly named creator is cast on a plan with nobody on camera. Casting a REAL person confirms their likeness consent."New value: +"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”), or a PRESET AI creator from list_creators presets by exact name or id (free, no generation). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default when the plan has a person on camera and the account is on a paid Hermoso plan (a free account gets a fresh AI person), and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if a REAL person is cast on a free plan. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
    • addedInput schema / properties / faceRoute
      Added value: +{
      +  "description": "ONLY after a render came back saying the video model's safety check flagged a person's face: 'face_lane' is the user's choice \"I own the rights to this face\". Send the SAME request again with it and the same face is rendered on Seedance through the face library, at the normal price. Sending it is the user's confirmation that they have the rights to that face (paid plans, like every real face). Never set it on your own; the other choices that refusal names are a different model (`model`) or another creator (`creator`).",
      +  "enum": [
      +    "face_lane"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / foundationImages
      Added value: +{
      +  "description": "images a foundations:'choose' call returned, reused as-is (not repainted)",
      +  "properties": {
      +    "hero": {
      +      "type": "string"
      +    },
      +    "identitySheet": {
      +      "type": "string"
      +    },
      +    "location": {
      +      "type": "string"
      +    },
      +    "moodboard": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / foundations
      Added value: +{
      +  "description": "product commercial / cinematic: 'choose' paints ONLY the foundations (identity sheet + four-up moodboard, or hero + location) and returns them, no video — then call again with moodboardPick + foundationImages",
      +  "enum": [
      +    "choose"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / moodboardPick
      Added value: +{
      +  "description": "which moodboard panel (1-4, left to right, top to bottom) the storyboard follows; default 1",
      +  "maximum": 4,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • changedInput schema / properties / music / description
      Previous value: -"music bed: on (default), false, or the bed described in words ('lo-fi jazz, brushed drums')"New value: +"music bed: OFF unless asked. true = the plan's own music line; or the bed in words ('lo-fi jazz, brushed drums'); false = none"
  9. Changed1 schema field changedv0.1.320
    • changedInput schema / properties / dryRun / description
      Previous value: -"return the routing decision (single pass vs stitched acts, resolved model + act lengths) WITHOUT submitting a render — free, nothing charged"New value: +"return the routing decision (single pass vs stitched acts, resolved model + act lengths) and the exact credits the real render reserves, WITHOUT submitting a render — free, nothing charged"
  10. Changed6 schema fields changedv0.1.285
    • changedInput schema / properties / creator / description
      Previous value: -"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default on any plan with a person on camera, and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if they are a REAL person with no likeness consent on file."New value: +"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default on any plan with a person on camera, and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, or if an explicitly named creator is cast on a plan with nobody on camera. Casting a REAL person confirms their likeness consent."
    • addedInput schema / properties / music / anyOf
      Added value: +[
      +  {
      +    "type": "boolean"
      +  },
      +  {
      +    "type": "string"
      +  }
      +]
    • changedInput schema / properties / music / description
      Previous value: -"licensed music bed on/off (default on)"New value: +"music bed: on (default), false, or the bed described in words ('lo-fi jazz, brushed drums')"
    • removedInput schema / properties / music / type
      Removed value: -"boolean"
    • changedInput schema / properties / textStyle / anyOf
      Previous value: -[
      -  {
      -    "enum": [
      -      "pill",
      -      "editorial",
      -      "bold",
      -      "minimal",
      -      "handwritten",
      -      "boxed"
      -    ],
      -    "type": "string"
      -  },
      -  {
      -    "properties": {
      -      "background": {
      -        "description": "\"none\", \"pill\", or a #hex box",
      -        "type": "string"
      -      },
      -      "cardColor": {
      -        "description": "#hex end card background",
      -        "type": "string"
      -      },
      -      "color": {
      -        "description": "#hex",
      -        "type": "string"
      -      },
      -      "font": {
      -        "enum": [
      -          "sans",
      -          "serif",
      -          "elegant",
      -          "condensed",
      -          "hand"
      -        ],
      -        "type": "string"
      -      },
      -      "italic": {
      -        "type": "boolean"
      -      },
      -      "outline": {
      -        "type": "boolean"
      -      },
      -      "position": {
      -        "enum": [
      -          "top",
      -          "center",
      -          "lower",
      -          "bottom"
      -        ],
      -        "type": "string"
      -      },
      -      "preset": {
      -        "enum": [
      -          "pill",
      -          "editorial",
      -          "bold",
      -          "minimal",
      -          "handwritten",
      -          "boxed"
      -        ],
      -        "type": "string"
      -      },
      -      "shadow": {
      -        "type": "boolean"
      -      },
      -      "size": {
      -        "anyOf": [
      -          {
      -            "enum": [
      -              "s",
      -              "m",
      -              "l",
      -              "xl"
      -            ],
      -            "type": "string"
      -          },
      -          {
      -            "type": "number"
      -          }
      -        ]
      -      },
      -      "subFont": {
      -        "enum": [
      -          "sans",
      -          "serif",
      -          "elegant",
      -          "condensed",
      -          "hand"
      -        ],
      -        "type": "string"
      -      },
      -      "subItalic": {
      -        "type": "boolean"
      -      },
      -      "textCase": {
      -        "enum": [
      -          "as-is",
      -          "upper",
      -          "lower",
      -          "title"
      -        ],
      -        "type": "string"
      -      },
      -      "tilt": {
      -        "description": "degrees, ±12",
      -        "type": "number"
      -      },
      -      "weight": {
      -        "type": "number"
      -      }
      -    },
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "type": "string"
      +  },
      +  {
      +    "properties": {
      +      "background": {
      +        "description": "none|pill|a colour",
      +        "type": "string"
      +      },
      +      "cardColor": {
      +        "type": "string"
      +      },
      +      "color": {
      +        "description": "any CSS colour",
      +        "type": "string"
      +      },
      +      "describe": {
      +        "description": "the look in words",
      +        "type": "string"
      +      },
      +      "font": {
      +        "description": "sans|serif|elegant|condensed|hand or any Google Fonts family",
      +        "type": "string"
      +      },
      +      "italic": {
      +        "type": "boolean"
      +      },
      +      "outline": {
      +        "type": "boolean"
      +      },
      +      "outlineColor": {
      +        "type": "string"
      +      },
      +      "position": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "type": "number"
      +          }
      +        ],
      +        "description": "top|center|lower|bottom or 0.05-0.95 from the top"
      +      },
      +      "preset": {
      +        "type": "string"
      +      },
      +      "shadow": {
      +        "type": "boolean"
      +      },
      +      "size": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "type": "number"
      +          }
      +        ],
      +        "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction"
      +      },
      +      "subFont": {
      +        "type": "string"
      +      },
      +      "subItalic": {
      +        "type": "boolean"
      +      },
      +      "textCase": {
      +        "enum": [
      +          "as-is",
      +          "upper",
      +          "lower",
      +          "title"
      +        ],
      +        "type": "string"
      +      },
      +      "tilt": {
      +        "description": "degrees, ±45",
      +        "type": "number"
      +      },
      +      "weight": {
      +        "type": "number"
      +      }
      +    },
      +    "type": "object"
      +  }
      +]
    • changedInput schema / properties / textStyle / description
      Previous value: -"THE LOOK of captions and the end card — only meaningful with captions:true or endCard:true, and only when the user described a look. Presets: editorial (a large elegant serif title mid-frame with a small italic line under it, no box), bold (tall condensed caps with a black outline), minimal (small lowercase near the bottom), handwritten (tilted marker), boxed (dark words on a white box), pill (the plain default). Pass a preset name, or an object with a preset plus overrides. A caption written \"TITLE · small line\" puts the part after the middle dot on a second line. An invalid field is refused by name before anything renders."New value: +"THE LOOK of captions and the end card, only with captions or endCard and only when the user described one: the look in WORDS (\"chunky yellow comic letters, purple outline\"), a preset (editorial: big serif title + small italic line; bold: condensed caps, outline; minimal; handwritten; boxed; pill, the default), or fields. \"TITLE · small line\" puts the part after the dot on a second line."
  11. Changed1 schema field changedv0.1.272
    • changedInput schema / properties / creator / description
      Previous value: -"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if the plan has nobody on camera, or if they are a REAL person with no likeness consent on file."New value: +"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default on any plan with a person on camera, and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if they are a REAL person with no likeness consent on file."
  12. Changed4 schema fields changedv0.1.225
    • changedInput schema / properties / captions / description
      Previous value: -"burn the plan's per-scene on-screen words as caption pills. DEFAULT FALSE — leave it off unless the user asks for on-screen text (no captions, or true subtitles of what is said; never scene or emphasis labels); a recipe whose format IS on-screen text keeps its text either way"New value: +"burn the plan's per-scene on-screen words as caption pills. DEFAULT FALSE on every recipe — set true ONLY when the user asks for on-screen text or captions; no recipe turns them on by itself"
    • changedInput schema / properties / endCard / description
      Previous value: -"branded end card on/off (default: on, except organic recipes)"New value: +"append the branded end card. DEFAULT FALSE on every recipe — set true ONLY when the user asks for an end card (a clone of a video that had none should not grow one)"
    • changedInput schema / properties / lockup / description
      Previous value: -"persistent brand-logo lockup overlay on/off"New value: +"brand wordmark + tagline composited over the closing seconds. DEFAULT FALSE — set true ONLY when the user asks for branding on the close"
    • addedInput schema / properties / textStyle
      Added value: +{
      +  "anyOf": [
      +    {
      +      "enum": [
      +        "pill",
      +        "editorial",
      +        "bold",
      +        "minimal",
      +        "handwritten",
      +        "boxed"
      +      ],
      +      "type": "string"
      +    },
      +    {
      +      "properties": {
      +        "background": {
      +          "description": "\"none\", \"pill\", or a #hex box",
      +          "type": "string"
      +        },
      +        "cardColor": {
      +          "description": "#hex end card background",
      +          "type": "string"
      +        },
      +        "color": {
      +          "description": "#hex",
      +          "type": "string"
      +        },
      +        "font": {
      +          "enum": [
      +            "sans",
      +            "serif",
      +            "elegant",
      +            "condensed",
      +            "hand"
      +          ],
      +          "type": "string"
      +        },
      +        "italic": {
      +          "type": "boolean"
      +        },
      +        "outline": {
      +          "type": "boolean"
      +        },
      +        "position": {
      +          "enum": [
      +            "top",
      +            "center",
      +            "lower",
      +            "bottom"
      +          ],
      +          "type": "string"
      +        },
      +        "preset": {
      +          "enum": [
      +            "pill",
      +            "editorial",
      +            "bold",
      +            "minimal",
      +            "handwritten",
      +            "boxed"
      +          ],
      +          "type": "string"
      +        },
      +        "shadow": {
      +          "type": "boolean"
      +        },
      +        "size": {
      +          "anyOf": [
      +            {
      +              "enum": [
      +                "s",
      +                "m",
      +                "l",
      +                "xl"
      +              ],
      +              "type": "string"
      +            },
      +            {
      +              "type": "number"
      +            }
      +          ]
      +        },
      +        "subFont": {
      +          "enum": [
      +            "sans",
      +            "serif",
      +            "elegant",
      +            "condensed",
      +            "hand"
      +          ],
      +          "type": "string"
      +        },
      +        "subItalic": {
      +          "type": "boolean"
      +        },
      +        "textCase": {
      +          "enum": [
      +            "as-is",
      +            "upper",
      +            "lower",
      +            "title"
      +          ],
      +          "type": "string"
      +        },
      +        "tilt": {
      +          "description": "degrees, ±12",
      +          "type": "number"
      +        },
      +        "weight": {
      +          "type": "number"
      +        }
      +      },
      +      "type": "object"
      +    }
      +  ],
      +  "description": "THE LOOK of captions and the end card — only meaningful with captions:true or endCard:true, and only when the user described a look. Presets: editorial (a large elegant serif title mid-frame with a small italic line under it, no box), bold (tall condensed caps with a black outline), minimal (small lowercase near the bottom), handwritten (tilted marker), boxed (dark words on a white box), pill (the plain default). Pass a preset name, or an object with a preset plus overrides. A caption written \"TITLE · small line\" puts the part after the middle dot on a second line. An invalid field is refused by name before anything renders."
      +}
  13. Changed1 schema field changedv0.1.189
    • changedInput schema / properties / captions / description
      Previous value: -"composited caption pills on/off (default: the recipe decides)"New value: +"burn the plan's per-scene on-screen words as caption pills. DEFAULT FALSE — leave it off unless the user asks for on-screen text (no captions, or true subtitles of what is said; never scene or emphasis labels); a recipe whose format IS on-screen text keeps its text either way"
  14. Changed5 schema fields changedv0.1.161
    • addedInput schema / properties / allowGenericProduct
      Added value: +{
      +  "description": "proceed even though this brand has NO product photo on file and the ad features a product — the packaging will be INVENTED. Only pass true after telling the user that and hearing they are fine with a generic stand-in",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / creator
      Added value: +{
      +  "description": "CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if the plan has nobody on camera, or if they are a REAL person with no likeness consent on file.",
      +  "type": "string"
      +}
    • changedInput schema / properties / durationSeconds / description
      Previous value: -"total ad length in seconds — omit to honor the plan’s own duration"New value: +"total ad length in seconds (supported range 4–180; outside that it is clamped). Omit to honor the plan’s own duration — that is almost always right. This only RE-TIMES an already-authored board (its scenes are scaled to fit), it does NOT re-write it, so to change the length of the ad the user asked for, re-run plan_ad with durationSeconds instead. A length that fits ONE clip of the render model renders as one continuous pass; longer is stitched from acts filled to that model’s clip maximum with the remainder last — the maximum is 15s on most models and 30s on the longest-clip one, so use dryRun:true to see the exact act split for free before spending."
    • changedInput schema / properties / resolution / description
      Previous value: -"'720p' default; '480p' = cheap fast draft pass, '1080p'/'4k' = premium final delivery (more credits)"New value: +"'1080p' default (what we ship and bill for); '480p'/'720p' = cheaper draft passes, '4k' = premium final delivery (more credits). NOT EVERY MODEL OFFERS EVERY TIER — this enum is what the tool accepts, and each model's OWN `resolutions` list in hermoso_capabilities is what it can actually render (the longest-clip 30s model, for one, tops out at 720p). Ask for a tier the chosen model does not list and it is rendered at that model's best available tier instead, with nothing in the reply saying so — so check `resolutions` before promising anyone 1080p or 4k."
    • changedOutput schema / (root)
      Previous value: -{
      -  "$schema": "http://json-schema.org/draft-07/schema#",
      -  "additionalProperties": false,
      -  "properties": {
      -    "dryRun": {
      -      "description": "true when this was a dry run (no job submitted, nothing charged)",
      -      "type": "boolean"
      -    },
      -    "input": {
      -      "description": "the assembled render input (dry run only — resolved model, duration, scenes)"
      -    },
      -    "jobId": {
      -      "description": "the render job id — poll get_job with this id to resume or inspect",
      -      "type": "string"
      -    },
      -    "jobType": {
      -      "description": "the routing decision — 'video' (single pass) or 'stitch' (acts)",
      -      "type": "string"
      -    },
      -    "model": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "the product-facing label of the model that rendered it"
      -    },
      -    "raw": {
      -      "description": "the raw job result payload (e.g. images[] for carousel template ads)"
      -    },
      -    "stillRendering": {
      -      "description": "true when the render is still in progress — keep polling get_job with jobId",
      -      "type": "boolean"
      -    },
      -    "url": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "the served URL of the finished media (absent/null while still rendering)"
      -    }
      -  },
      -  "type": "object"
      -}New value: +null
  15. First observedv0.1.0

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only say it is a mutating, non-idempotent, open-world call. The description adds what actually matters: it spends credits, takes 1-3 minutes, is polled via get_job, may return needsAppScreens with nothing charged, refuses casting under named conditions, and records real-person likeness consent. That is substantial behavior disclosure beyond the annotation set.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the trigger condition and the output, then ordered by dependency, which suits a 23-parameter tool. It is dense, though: parenthetical asides such as the cinematic location-anchor/hero clause and the stitched-acts explanation compress several distinct concepts into single sentences and slow scanning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a high-complexity, credit-spending render tool with no output schema, the description covers cost, latency, polling, refusal paths, the storyboard/foundations two-step, and safe pre-flight via dryRun. An agent could invoke this correctly and recover from the documented failure mode using only the description plus schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description earns the extra point by supplying cross-parameter workflow the schema cannot express — plan_ad output goes into `creative`, `dryRun` precedes a paid run, `foundations:'choose'` is a two-call dance with moodboardPick/foundationImages, and `creator` routes through list_creators. Some of it (casting, inserts, needsAppScreens) restates the already-exhaustive parameter descriptions, so it is additive rather than transformative.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a concrete verb and artefact: "renders a finished ad on a real video model... as an mp4 ready to post," and enumerates the request types it serves (video ad, Reel, TikTok ad, UGC video, commercial). It explicitly distinguishes itself from sibling plan_ad ("Pass plan_ad's full output as `creative`"), so an agent knows this is the execution step, not the ideation step.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit activation condition ("Use this when the user asks for a video ad..."), explicit prerequisite (must carry plan_ad output), explicit preliminary step ("dryRun:true shows the act split free, before spending"), explicit post-call flow ("poll get_job while it renders"), and explicit handling of the needsAppScreens dead end. When-to-use, when-not-to-spend, and which sibling to invoke first are all present.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools