Skip to main content
Glama
hermoso-ai

Hermoso

Official

Render ad video

render_ad

Renders finished video ads from a plan_ad concept, handling timed shots, clean speech, and composited text. Produces single clips or stitched acts.

Instructions

RECOMMENDED for finished video ADS: render a plan_ad concept through the SAME quality pipeline as the Hermoso web Studio — timed shot list, exact/clean speech (no garbled words), text composited in post (never model-painted), an optional brand end card (only when the user asks), no music unless asked, real product references. UGC and cinematic plans are rendered from a painted STORYBOARD board (cleaned, then bound first, ahead of the creator and the product); a cinematic spot paints a location anchor first (and a hero when someone is on camera). A product-only commercial renders from the real product photo by default; board:true or foundations:'choose' adds a product identity sheet, a four-up moodboard and a board. Pass plan_ad’s full structured output as creative. Honors the plan’s render_plan structure/duration: a storyboard that FITS ONE CLIP OF THE RENDER MODEL renders as a single continuous pass; anything longer automatically renders as STITCHED ACTS (the fewest balanced clips, each at most one model clip) — never time-compressed into one clip. That threshold is the render model’s own maximum, not a fixed number: most models cap a clip at 15s and the longest-clip one goes to 30s, so use dryRun:true to see the act split this plan will actually get, for free, before spending. CAST A SAVED CREATOR with creator so the SAME person stars in this ad as in the last one (list_creators is the roster) — otherwise every render invents a new face. Renders take 1–3 min; keep polling get_job if it returns still-rendering. Spends credits.

Input Schema

TableJSON 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 plan’s pick). 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 exact 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 — 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.
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)
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.
resolutionNo'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.
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
moodboardPickNowhich moodboard panel (1-4, left to right, top to bottom) the storyboard follows; default 1
durationSecondsNototal 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.
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.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."
  2. 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"
  3. 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"
  4. 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."
  5. 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."
  6. 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."
      +}
  7. 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"
  8. 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
  9. First observedv0.1.0

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only declare readOnly=false, openWorld=true, idempotent=false, destructive=false; the description adds far more: credits are spent, renders take 1–3 min, act-splitting rules and model clip caps (15s/30s), silent resolution tier fallback, likeness consent/terms, refusal conditions for bad creator names, and that casting a saved creator costs less. This is rich behavioral context beyond structured fields.

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

Conciseness3/5

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

Front-loaded with the key "RECOMMENDED for finished video ADS" and generally well organized, but it is a single dense paragraph with heavy parentheticals. Multiple statements duplicate what already lives in the parameter schema descriptions (creator casting/terms, music-off default, endCard/captions defaults, the act-split and dryRun explanation), which costs it on the "every sentence earns its place" bar.

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 19-parameter mutation tool with no output schema, it covers everything an agent needs: the required `creative` input's provenance, the credit/time cost, the polling contract, the act-split threshold mechanic, model/resolution compatibility, and the full refusal/consent surface. Nothing material is left for the agent to infer.

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 description coverage is 100%, so the baseline is 3, but the description adds cross-field meaning the schema can't: pass plan_ad's full output as `creative`, pull creator ids from list_creators, model ids from hermoso_capabilities, and the warning that a named model triggers a confirmation rather than a silent swap. It also reinforces the durationSeconds re-timing limitation and the dryRun act-split preview.

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?

Opens with a specific verb+resource+scope: "RECOMMENDED for finished video ADS: render a plan_ad concept through the SAME quality pipeline as the Hermoso web Studio." It names the upstream sibling it consumes (plan_ad) and clearly separates itself from generate_video / product_sizzle / make_template_ad by being the finished-ad render path. An agent can tell what it produces without opening the schema.

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 routing throughout: when to cast a creator (and that list_creators is the roster), when to use board/foundations, when to set faceRoute ("Never set it on your own"), when to set endCard/lockup/captions (only when the user asks), and dryRun:true as a free pre-flight before spending. It also dictates the follow-up loop (poll get_job if still-rendering).

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