SumeMediaFile in a Format output_schema: fields and URL rules

Put a run's image or video in typed JSON with $ref SumeMediaFile#: the nine fields, which are nullable, and why url must be media the run generated.

5 min readSume
All posts

To get a run's video or image into typed JSON, reference Sume's built-in media shape with { "$ref": "SumeMediaFile#" } inside your output_schema. It returns type, url, content_type, file_name, size_bytes, width, height, duration_ms and expires_at, and the url must be a file this run actually generated.

The shape comes from Structured output, read on 2026-10-02. The same page says the exact request and response schemas live in the live OpenAPI at https://api.sume.com/reference/json, so treat the tables below as a readable summary, not a second schema.

What fields does SumeMediaFile contain?

Every field is required in the object, and every field except type and url is nullable. That matters because the strict subset has no optional properties: a field the run could not measure comes back as null, not missing.

Read 2026-10-02 from docs.sume.com
FieldTypeNotes
typeimage, video, audio or fileAlways present
urlstring (uri)Must be a URL this run produced
content_typestring or nullFor example video/mp4
file_namestring or nullName of the stored file
size_bytesinteger or nullNull when not measured
width, heightinteger or nullImages and video
duration_msinteger or nullVideo and audio
expires_atdate-time or nullNull for durable media.sume.com URLs

How do I use it in a schema?

Reference it by name wherever a field should hold media. The root must be an object, every object needs additionalProperties: false, and every declared property goes in required. Name the field you show in your UI with primary_output_key so the receipt also resolves primary_output_url.

{
  "output_schema": {
    "name": "acme/promo-hero/v1",
    "strict": true,
    "schema": {
      "type": "object",
      "additionalProperties": false,
      "required": ["headline", "hero_image"],
      "properties": {
        "headline": { "type": "string" },
        "hero_image": { "$ref": "SumeMediaFile#" }
      }
    }
  },
  "primary_output_key": "hero_image"
}

Why is expires_at null?

Sume-hosted media is served from media.sume.com and does not expire, so expires_at is null for the normal case. It is only populated when a signed URL is returned. You can store the URL next to your own record and render it later, with no refresh step.

The same page warns that a durable URL is also a public URL. Anyone holding it can fetch the file, so if your product needs per-customer access control, copy or proxy the file rather than handing out the Sume URL.

What does the run check about the url?

Before output reaches you, every URL in it is compared with the set of media the run generated, using exact string equality. One pass collects every http(s) string at any depth. A second pass collects the url of every SumeMediaFile-shaped value whatever it contains, so a placeholder such as none or an empty string cannot slip through.

A plausible-looking media.sume.com URL that this run did not make fails the projection. You get output: null plus an output_error, not a schema-shaped guess. The gate also only admits media the run generated, so a file the run merely uploaded is not in the set, and a schema field holding an upload URL fails the whole projection.

What about duration_ms?

A duration_ms inside a SumeMediaFile comes from the same ledger that fills artifacts[]. Where the ledger recorded a length, the value in output must agree within 10 percent or the projection fails. Where nothing was recorded, nothing is checked, and null means not measured, not zero.

So a duration_ms you read back is either the artifact's own or unverified. A separate duration_seconds number you declared yourself is written by the projection pass and is not checked, so do not bill or schedule from it.

What does Sume not do here?

SumeMediaFile# is one of only two $ref targets that resolve, along with #/$defs/* declared at the root. External refs and $ref: "#" are rejected with 400 output_schema_invalid, which costs nothing because nothing runs. Read Errors and spend for the codes, and fall back to artifacts[] when output is null.

Checklist before you ship a schema with media

Walking this list once costs less than debugging an output_schema_unsatisfied receipt, because that failure arrives only after the run has already spent money on generation.

  • Every object in the schema, including ones inside $defs, has additionalProperties: false.
  • Every declared property appears in required; use a nullable union for anything that may be missing.
  • Require only media the Format actually makes. A required image on a text-only Format fails on every run.
  • Do not expect values from input to come back. The projection path never sees your input.
  • Handle output: null with output_error set, on both failed and completed runs, and fall back to artifacts[].

Which keys does the array form take?

A schema can hold several files by putting SumeMediaFile# inside an array with items, for example a list of clips next to an assembled cut. Declare items on every array; an array without it is a missing_items violation. Leave minItems off if you want to receive a partial list, because the keyword is enforced and a list that demands three clips rejects a run that made two.

Keep nesting under 10 levels and the whole document under 5000 properties. The total string length budget is 120,000 characters across names, keys and string values, so long descriptions on a big schema can use it up even when no single string is large.

What should I read when it still fails?

Look at output_error.code first. output_schema_unsatisfied with rejected_urls[] (the first ten only) means a URL was not produced by this run. With violations[] it is the shape. The harvested count by media type tells you how many images, videos, audio and files the run really made, so you can compare it with what the schema requires.

If the media exists, you still have it in artifacts[]. Showing that media and logging the shape failure is better than showing an error to a customer whose video exists.

Sources

Related posts

More in Formats

All Formats posts

Written by Sume