JSON schema for AI video output: 5 templates to copy

Copyable JSON Schemas for AI video output on Sume: one video, video plus copy, a poster, aspect ratios, and caption cues, all in the strict subset.

5 min readSume
All posts

A JSON Schema for AI video output on Sume is an output_schema you bind to a Format run: the finished run's output comes back in that shape, with each media file typed as { "$ref": "SumeMediaFile#" } and checked against the media the run made. The 5 templates below fit Sume's strict subset, so they pass the check at submit.

Each template follows the rules in Sume's Structured output docs, read on 2026-09-27. Send one as output_schema on POST /v1/formats/{handle}/{slug}/runs, or nest it under json_schema in the OpenAI-shaped response_format alias with "type": "json_schema"; sending both is 400 invalid_request. Sume Format structured output explains the rules; this post gives schemas to copy.

How do I get back one finished video?

The smallest useful schema: one required media file. Send it with "primary_output_key": "video" and primary_output_url on the receipt is that file's URL. SumeMediaFile covers image, video, audio, and file, so the key name does not force a video: read type when you use it. A run that generated no media cannot fill the key, so over the API it ends failed rather than completed with an empty output.

{
  "name": "acme/video-only/v1",
  "strict": true,
  "schema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["video"],
    "properties": {
      "video": { "$ref": "SumeMediaFile#" }
    }
  }
}

How do I get the video plus its post copy?

Copy fields are nullable on purpose. If the run does not submit the object itself, a projection pass fills it from only two facts: the run's generated media and its closing text. It never sees your input or instruction, so prose that depends on your brief can come back null there. hashtags has no minItems, so an empty list is still legal. maxItems and pattern are enforced like every keyword you write: an eleventh hashtag, or one without #, does not satisfy the schema.

{
  "name": "acme/video-with-copy/v1",
  "strict": true,
  "schema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["video", "title", "caption", "hashtags"],
    "properties": {
      "video": { "$ref": "SumeMediaFile#" },
      "title": { "type": ["string", "null"] },
      "caption": {
        "type": ["string", "null"],
        "description": "Post caption written for the video"
      },
      "hashtags": {
        "type": "array",
        "maxItems": 10,
        "items": { "type": "string", "pattern": "^#[A-Za-z0-9_]+$" }
      }
    }
  }
}

How do I ask for a poster frame that may not exist?

A media file that may be missing is an anyOf of SumeMediaFile# and null. Put any constraint inside a branch, never next to the anyOf: siblings of anyOf and $ref carry no meaning. The URL gate admits only media the run generated, so a file the run merely uploaded fails the projection. Keep uploads out of the schema.

{
  "name": "acme/video-with-poster/v1",
  "strict": true,
  "schema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["video", "poster"],
    "properties": {
      "video": { "$ref": "SumeMediaFile#" },
      "poster": {
        "anyOf": [{ "$ref": "SumeMediaFile#" }, { "type": "null" }]
      }
    }
  }
}

How do I get several aspect ratios from one run?

Name one key per ratio and share a definition through $defs, which must sit at the root of the schema. The schema only shapes how a finished run is read back, so ask for the three ratios in your instruction. Pair it with "primary_output_key": "vertical": a run that satisfies the schema but leaves that key null ends failed with primary_output_missing, while the other two may be null.

{
  "name": "acme/aspect-set/v1",
  "strict": true,
  "schema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["vertical", "square", "landscape"],
    "properties": {
      "vertical": { "$ref": "#/$defs/maybe_video" },
      "square": { "$ref": "#/$defs/maybe_video" },
      "landscape": { "$ref": "#/$defs/maybe_video" }
    },
    "$defs": {
      "maybe_video": {
        "anyOf": [{ "$ref": "SumeMediaFile#" }, { "type": "null" }]
      }
    }
  }
}

How do I get timed caption lines with the video?

Objects inside arrays and inside $defs need additionalProperties: false too. Know what is checked: URLs, duration_ms on media files (within 10% of the file's recorded length), and the shape. Caption text and cue times are the run's own account of its work, grounded in what it reported but not verified against the file.

{
  "name": "acme/video-with-cues/v1",
  "strict": true,
  "schema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["video", "cues"],
    "properties": {
      "video": { "$ref": "SumeMediaFile#" },
      "cues": { "type": "array", "items": { "$ref": "#/$defs/cue" } }
    },
    "$defs": {
      "cue": {
        "type": "object",
        "additionalProperties": false,
        "required": ["start_ms", "end_ms", "text"],
        "properties": {
          "start_ms": { "type": "integer", "minimum": 0 },
          "end_ms": { "type": "integer", "minimum": 0 },
          "text": { "type": "string" }
        }
      }
    }
  }
}

Which template should I start from?

Pick by what your record needs, then rename the keys. If an edit leaves the strict subset, the create fails with 400 output_schema_invalid and nothing is charged; the violations guide maps each details.violations[] rule to a fix.

The 5 templates above, each inside the strict subset in Structured output, read 2026-09-27.
TemplateRequired keysMay be null or emptyOther keywords used
One finished videovideoNothingNone
Video plus post copyvideo, title, caption, hashtagstitle and caption may be null; hashtags may be []description, maxItems, pattern
Optional poster framevideo, posterposter may be nullanyOf
Several aspect ratiosvertical, square, landscapeAll three may be null, but a null vertical fails the run when it is the primary_output_key$defs, anyOf
Timed caption cuesvideo, cuescues may be []$defs, minimum

What if a finished run cannot fill the schema?

Over the API the run fails: status is failed, output_error says why, and artifacts[] still lists every file the run made, as Sume Format run failure codes explains code by code.

Sources

Related posts

More in Formats

All Formats posts

Written by Sume