Real estate listing video: a typed output schema with SumeMediaFile
Bind an output_schema to a Format run so a listing video returns as your JSON: address, headline, caption and a SumeMediaFile to store beside the MLS ID.

To get a listing video back as data you can write straight to a listings table, bind an output_schema to the Format run: the root is an object, each field is required, and the video is a SumeMediaFile reference that Sume checks was really produced by that run. The run then returns your own JSON, for example an address, a headline, a caption and the video, rather than the built-in projection.
The rules below come from Format structured output and Calling a Format. The schema is an example for a real estate team, with a placeholder acme/listing-video Format.
What does the schema have to look like?
Sume accepts a subset of JSON Schema. The root must be an object, every object needs additionalProperties: false, every property must be listed in required, and every node needs a type. Optional fields are expressed as a type union with null, such as ["string", "null"], not as a missing property. strict: false does not relax any of this.
Media fields use { "$ref": "SumeMediaFile#" }. Every field of a SumeMediaFile is required and every field except type and url is nullable, so the video's duration_ms, width and height come back for your records when the run knows them. A media field such as cover must be something the Format run itself produced; a URL the run did not generate is refused by the same check.
| Rule | Applied in the example |
|---|---|
| Root is an object | Yes: one object with five properties |
| additionalProperties false on every object | Yes |
| Every property in required | Yes; the optional caption is a string-or-null union |
| Media by SumeMediaFile reference | video and cover use the $ref |
| Name 1-64 characters, A-Z a-z 0-9 . _ / - | acme/listing-video/v1 |
How to bind it to a run
Send output_schema with a name, strict and the schema, and name the key that holds the deliverable with primary_output_key. The receipt echoes which schema applied under output_schema.source, with request_override for a per-request schema. If you send response_format as well as output_schema, the request is a 400.
Add a spend cap and an idempotency key derived from the MLS id, so a retry does not start a second paid run. The example uses the placeholder Format acme/listing-video; swap in yours. The listing facts go in input, which is data, while the style goes in instruction.
import hashlib
import os
import requests
MEDIA = {"$ref": "SumeMediaFile#"}
SCHEMA = {
"type": "object",
"additionalProperties": False,
"required": ["address", "headline", "caption", "video", "cover"],
"properties": {
"address": {"type": "string"},
"headline": {"type": "string"},
"caption": {"type": ["string", "null"]},
"video": MEDIA,
"cover": MEDIA,
},
}
def start_listing_video(mls_id, address, photo_urls):
key = hashlib.sha256(f"{mls_id}:listing-video:v1".encode()).hexdigest()[:40]
body = {
"instruction": "Vertical 9:16 tour from the photos. Use the address as written.",
"input": {"mls_id": mls_id, "address": address, "photo_urls": photo_urls},
"output_schema": {
"name": "acme/listing-video/v1",
"strict": True,
"schema": SCHEMA,
},
"primary_output_key": "video",
"generation_spend_cap_usd": 25,
}
r = requests.post(
"https://api.sume.com/v1/formats/acme/listing-video/runs",
headers={
"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Idempotency-Key": key,
},
json=body,
timeout=30,
)
r.raise_for_status()
return r.json()["data"]["id"]
What can still go wrong
The URL gate rejects a media URL that the run did not produce, so a field cannot be filled with one of the photos you uploaded. A schema the run cannot satisfy ends in an output error on the receipt instead of a half-filled object. Check primary_output_url before you publish, and keep the MLS id in your own table beside the run id.
Sume-hosted media is served from media.sume.com and does not expire, but a durable URL is also a public one. If a listing is pre-market and confidential, keep that in mind before you store or share the link.
Sources
Related posts
More in Formats
- Rebuild your best product ad with a new product: sume-recreate Format
Use the sume-recreate Format to keep a winning ad's scene order and timing, cast a new presenter and generate everything fresh with your own packshot.
- Recreate Format for Singles' Day: remake a structure, not the footage
Use Sume's recreate Format to remake a reference clip's beats and caption rhythm for your 11.11 offer, and preflight it with reference ingest.
- Resolve 21.1 batch render with AI assistants vs Formats bulk runs
Resolve 21.1 lets AI assistants like Claude batch render. Sume Formats bulk runs queue 1 to 100 items at concurrency 1 to 16 over one API call.
- Failed Format run codes: retry, continue, fix input or wait
Eleven error.code values a failed Sume Format run can carry, grouped by response: continue with previous_run_id, retry with a new key, fix input, or wait.
Written by Sume