Five ad variants in one Format run: a variants[] output schema
Bind a schema with a variants array, a nullable video_url per variant and maxItems 5, so a partial run still returns the variants it finished.

To get five ad variants back from one Sume Format run, bind an output_schema whose root object has a variants array with maxItems: 5, and give each variant a hook, a nullable caption and a nullable video_url. Leave minItems off, so a run that finishes three of five still returns those three instead of output: null.
Why the schema matters here
A Format run is one agent turn, so five variants are one run, one spend cap and one receipt. That is the right shape when the variants share a brief and you want them judged side by side. The schema is what turns the receipt into rows you can write to a database.
Two rules for a partial result
The Sume docs state two rules for partial results. Optional means nullable, not absent, so every property is listed in required and a missing value is ["string", "null"]. And minItems is really enforced, so do not put it on an array you want to receive partially. maxItems is also enforced, after the run, which makes it a cheap guard against a run that over-delivers.
Why video_url is a string
The video field is a plain nullable string on purpose. Sume checks every URL in output against the media the run really produced, so a variant whose clip was never made has to be null, not a placeholder. The docs example for a partial ledger uses the same pattern.
| Choice | Use it | Why |
|---|---|---|
| Root type | object with a variants array | The root must be an object, never a bare array |
| Array bounds | maxItems 5, no minItems | maxItems is enforced; minItems would reject a partial ledger |
| Optional fields | type ["string", "null"] | Every property must be in required |
| Per-variant objects | additionalProperties false | The rule applies to nested objects too |
| Clip link | video_url as nullable string | Must be a URL this run produced, otherwise null |
| Your own ids | Keep them in your database | The projection cannot see input, so variant ids may come back null |
The request
The request below calls a catalog Format at the reserved sume handle. It sends the brief as input data and the contract as output_schema, with a per-run spend cap you choose and an Idempotency-Key derived from the batch, not from the clock. Read the Format with GET /v1/formats/sume/{slug} first, because its io profile says what input it expects.
import json, os, urllib.request
VARIANT = {"type": "object", "additionalProperties": False,
"required": ["hook", "caption", "video_url"],
"properties": {"hook": {"type": "string"},
"caption": {"type": ["string", "null"]},
"video_url": {"type": ["string", "null"]}}}
SCHEMA = {"type": "object", "additionalProperties": False,
"required": ["variants"],
"properties": {"variants": {"type": "array", "maxItems": 5, "items": VARIANT}}}
def main():
body = {"instruction": "Make five vertical ad variants with different hooks.",
"input": {"product_name": "Aurora Headphones"},
"output_schema": {"name": "acme/ad-variants/v1", "strict": True, "schema": SCHEMA},
"generation_spend_cap_usd": 25}
req = urllib.request.Request(
"https://api.sume.com/v1/formats/sume/sume-product-commercial/runs",
data=json.dumps(body).encode(), method="POST",
headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": "aurora-variants-batch-1"})
with urllib.request.urlopen(req) as r:
print(json.load(r)["data"]["status_url"])
main()Reading the result
Read output_error before you read output, and fall back to artifacts[] when the shape failed, because the media exists either way. Because the schema asks for no single deliverable, primary_output_url stays null unless you name a key. That is fine for a set of variants. Your UI reads output.variants and shows a tile for each non-null video_url.
To fill in the missing variants without paying for the finished ones again, continue the run. The post on retrying only the missing variants shows how.
Sources
Related posts
More in Formats
- Format grant 404 workspace_not_found: only team handles resolve
workspace_not_found on POST .../grants means the handle matches no team workspace. User handles are not grantable, so pass a team handle or an org_ id.
- Format grant 409: exists, self, or workspace_required
A 409 on POST .../grants is format_grant_exists, format_grant_self or format_workspace_required. Each has its own fix, and none is a retry.
- Format run spend caps: the $500 ceiling and the null trap
A Sume Format run cannot spend past its cap. Omit it to inherit the Format's cap, send up to 500 to set one, and know null means $500, not no limit.
- Gemini Omni edit: direct Video Router call or a Sume Format run?
Use the Video Router edit call for one precise change to one clip. Use a Format run when the job has steps, a schema and a spend cap around the edit.
Written by Sume