Formats

Sume Format structured output: typed JSON from a JSON Schema

Bind a JSON Schema as output_schema and a Sume Format run returns output in that shape, with each media URL checked against what the run made.

6 min readSume
All posts

To get typed JSON from a Sume Format run, send a JSON Schema as output_schema on the run request. The finished run's output comes back in that shape, with every media URL checked against the media the run actually produced, or it comes back null with an output_error that says why.

Every rule below is from Sume's Structured output and Create a run docs pages, read on 2026-09-25.

How do I bind a JSON Schema to a Format run?

Add output_schema to the body of POST /v1/formats/{handle}/{slug}/runs, the call described in What is a Sume Format?. It takes name (required, 1–64 characters; namespace it, because it shows up on every receipt), strict (defaults to true), and schema (required). A per-request schema overrides any default schema bound to the Format.

Clients built on OpenAI Chat Completions can send response_format with type: "json_schema" instead. The Responses API's flattened text.format shape is not accepted, and sending both fields is 400 invalid_request.

curl -sS -X POST "https://api.sume.com/v1/formats/acme/product-promo/runs" \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8823-hero-v1" \
  -d '{
    "instruction": "Make one hero image for the linked product.",
    "input": { "product_url": "https://shop.example.com/p/8823" },
    "output_schema": {
      "name": "acme/promo-hero/v1",
      "strict": true,
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": ["headline", "hero_image", "alt_text"],
        "properties": {
          "headline": { "type": "string" },
          "hero_image": { "$ref": "SumeMediaFile#" },
          "alt_text": { "type": ["string", "null"] }
        }
      }
    },
    "primary_output_key": "hero_image"
  }'

Which JSON Schema keywords does Sume accept?

Schemas must fit the OpenAI strict-mode subset, enforced from an allowlist: a keyword off the list is a violation, not ignored. A schema outside the subset fails at submit with 400 output_schema_invalid, details.violations[] names every problem, and nothing is charged. The rules that catch most schemas, from Supported schemas:

  • The root's type must be exactly object. Wrap a top-level array in an object.
  • Every object, including those in items and $defs, needs additionalProperties: false.
  • Every declared property must be in required. Make a field optional with a nullable union: "type": ["string", "null"].
  • Every node needs a type, $ref, or anyOf. Use anyOf, not oneOf; allOf and nullable: true are rejected.
  • $ref resolves only to root #/$defs/* entries and SumeMediaFile#.
  • Limits: 10 levels of nesting, 5000 properties, 1000 values per enum, and 120,000 characters of strings in total.
Accepted keywords, from Structured output, read 2026-09-25.
GroupAccepted keywords
Structuretype, properties, required, additionalProperties, items, $defs, $ref, anyOf
Valuesenum, const
Stringsformat, pattern, minLength, maxLength
Numbersminimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf
ArraysminItems, maxItems
Annotationtitle, description, default, examples, $schema, $id

Where does the JSON object come from?

On the preferred path, filled_by: "agent", your schema is handed to the run as a tool it must call before finishing, so the model doing the work fills your object. If the run ends without a valid object, a fallback pass, filled_by: "projection", builds one with an OpenAI-strict json_schema completion at temperature 0.

That projection sees only the media the run generated and the first 8000 characters of its closing text, never your input or instruction. So an order id you sent does not round-trip on that path: keep identifiers on your side, keyed by the run id or your Idempotency-Key, and require only what the Format actually makes.

How do I get media URLs into the output?

Reference { "$ref": "SumeMediaFile#" } wherever you want a piece of the run's media. Every field of that shape is required, and every field but type and url is nullable.

  • Every URL in a custom output must exactly match media this run generated. Anything else fails the projection, including a plausible media.sume.com URL or a file the run merely uploaded.
  • Name the one thing your UI shows with primary_output_key (up to 64 characters) and the receipt resolves primary_output_url.
SumeMediaFile fields, from Structured output, read 2026-09-25.
FieldValue
typeimage, video, audio, or file.
urlA URL this run actually produced.
content_type, file_name, size_bytes, width, heightFile metadata, or null.
duration_msVideo and audio length. Where the run recorded a length, it must agree within 10%.
expires_atnull for durable media.sume.com URLs, the normal case.

What happens when the output does not match the schema?

Check output_error before reading output. Over the API, a projection failure is a run failure: status is failed, and artifacts[] still lists every file the run made. A failed run can report a partial result on output only if your schema allows one, so use nullable fields and leave minItems off arrays you want back partially. Statuses and webhooks: Sume Format run lifecycle.

The documented codes are below, from When output cannot be produced; treat the set as open.

  • output_schema_unsatisfied: the object did not match your schema, or referenced media this run did not produce.
  • output_extraction_failed: the projection could not run. Read the run once more; that alone clears a harvest_unavailable.
  • deliverable_missing: the Format declares media the run never made.
  • primary_output_missing: the schema was satisfied, but your primary_output_key has no value.
  • unattended_blocked and agent_reported_failure: the run itself reports it did not deliver.

What does Sume structured output not do?

  • No JSON mode. Bind a schema, or take the built-in sume/action-run-output/v1: a nullable text plus images, videos, audio, and files arrays, filled deterministically with no model involved, so it cannot fail the way a custom schema can.
  • No escape hatch. strict: false is accepted and stored, and changes nothing about the subset.
  • No streamed partial JSON. output appears once, on the terminal receipt.
  • No control over what the run makes. The schema constrains how a finished run is read back, so it cannot make the Format produce a video.

Sources

Related posts

Written by Sume