Shorts series: episode video and thumbnail from one Format run
YouTube Shorts series take a custom thumbnail per episode. Bind one output schema so each Sume Format run returns the video and the thumbnail together.
Yes: bind an output_schema with two SumeMediaFile fields, one for the episode video and one for the thumbnail, and one Sume Format run returns both as a single typed receipt. You then upload both to YouTube yourself. Sume generates the files and does not publish to YouTube.
The trigger is real. A platform roundup says YouTube Shorts series, with seasons, episodes, custom thumbnails and sequential playback, has been rolling out since 2026-09-23 (Orthotropy, read 2026-10-06). A series with a custom thumbnail per episode means each episode is now two assets that must stay paired. If the video comes from one call and the thumbnail from another, the pairing lives in your spreadsheet, and spreadsheets drift.
Why one run beats two calls
A Format run is one sandbox and one agent turn. If the thumbnail is made in the same turn as the video, it can reuse the same character, product and palette without you re-sending references. The receipt then carries both files under keys you named, so your publisher reads episode.video.url and episode.thumbnail.url from one object.
Pairing also makes failure handling simple. A run either delivered both fields or it did not. You never ship episode 4 with episode 3's thumbnail because a second request raced the first.
The schema
The structured-output subset is strict: the root is an object, every object sets additionalProperties to false, and every property is listed in required. Reference the built-in media shape with a $ref to SumeMediaFile#. Add the episode number as an integer so your publisher does not parse it out of a filename (Sume docs: Structured output, read 2026-10-06).
import os, requests
schema = {
"name": "acme/shorts-episode/v1",
"strict": True,
"schema": {
"type": "object",
"additionalProperties": False,
"required": ["episode_number", "video", "thumbnail"],
"properties": {
"episode_number": {"type": "integer", "minimum": 1},
"video": {"$ref": "SumeMediaFile#"},
"thumbnail": {"$ref": "SumeMediaFile#"}
}
}
}
body = {
"instruction": "Episode 4: the lighthouse keeper finds the second door. 45 seconds, 9:16. Also a 1280x720 thumbnail.",
"generation_spend_cap_usd": 12,
"output_schema": schema,
}
r = requests.post(
"https://api.sume.com/v1/formats/sume/sume-slideshow/runs",
headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Idempotency-Key": "lighthouse-s1-e4-v1"},
json=body, timeout=30)
print(r.status_code, r.json()["data"]["status_url"])What to check before you upload
Do not trust the shape alone. Read filled_by on the receipt: agent means the run submitted the object, projection means a fallback pass rebuilt it from the harvest, and the projection sees only the media and the closing text, not your input. If filled_by is projection, an episode_number you passed in input may be wrong or null, so confirm it against the episode you requested.
Then fetch the files and check them. A thumbnail field that holds a URL is not proof the image is 1280x720. Open it and check the pixels, and check the video duration against what you asked for.
| Field | What to check | Why |
|---|---|---|
| episode_number | Equals the number you requested | Projection cannot see your input |
| video.url | Plays, duration within your target | Only generated URLs pass the URL gate |
| thumbnail.url | Opens, correct aspect ratio | A URL is not a size guarantee |
| filled_by | agent, not projection | Projection is a fallback |
Keep the idempotency key per episode
Use one Idempotency-Key per episode and version, as the code does. A retry after a timeout then replays the first receipt with idempotency_hit true instead of paying for a second render. Changing the body under the same key returns 409 idempotency_conflict, which is the signal to bump the version suffix on purpose (Sume docs: Call a Format, read 2026-10-06).
Cap each episode with generation_spend_cap_usd. If you omit it, the Format's own cap applies, which defaults to $400, far more than one Short should cost.
Cost and scope
Asking for a thumbnail adds work to the run, so the cap you set should reflect two assets rather than one. Read usage on the first receipt and adjust. If your thumbnails are simple title cards, consider whether a still frame pulled from the finished video, with text added in your own tooling, is enough, since that adds no generation at all.
Sources
Related posts
More in Formats
- Sume Format run expires_at: 90 minutes, and how to set your timeout
A non-terminal Sume Format run carries an expires_at, 90 minutes from creation. Use it as your client timeout instead of inventing a number.
- Sume output schema limits: depth 10, 5,000 properties, 120,000 chars
How big can a Sume output_schema be? Depth 10, 5,000 properties and 120,000 characters, and a refusal before any spend. What to flatten and where it fails.
- Sume webhook payload is null: receipt over 1 MiB, fetch result_url
A Sume format.run.terminal webhook with payload null means the receipt was over 1 MiB. Read error.result_url and fetch the receipt instead of failing the run.
- Why did my Format run do that? Read the first message of its thread
A Format run's first thread message is the text the agent received: Format pointer, your instruction, unattended note and input file path. Read it first.
Written by Sume