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.

4 min readSume
All posts

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.

Schema choices for a variants[] output (Sume docs, read 2026-10-05)
ChoiceUse itWhy
Root typeobject with a variants arrayThe root must be an object, never a bare array
Array boundsmaxItems 5, no minItemsmaxItems is enforced; minItems would reject a partial ledger
Optional fieldstype ["string", "null"]Every property must be in required
Per-variant objectsadditionalProperties falseThe rule applies to nested objects too
Clip linkvideo_url as nullable stringMust be a URL this run produced, otherwise null
Your own idsKeep them in your databaseThe 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

All Formats posts

Written by Sume