Create a run
One POST starts a run. This is the shape production integrations send: your data in
input, a schema for the result, a per-run spend cap, and a webhook so you do not have to
poll.
202 Accepted:
Two status codes mean success. 202 is a fresh run. 200 is an idempotent replay, the same
Idempotency-Key with the same body, and returns the original run with
idempotency_hit: true. Both carry the full receipt: store data.id and follow the URLs on
it. Runs and results covers what happens next.
Build your own call
Fill in the fields to rewrite the cURL, TypeScript, JavaScript and Python snippets.
Operations in the TypeScript SDK resolve with { data, error, response } rather than
throwing, so check error before reading data.
Create a Format run
POST /v1/formats/{handle}/{slug}/runs
Required
Address a Format
| Shape | Example | Use it when |
|---|---|---|
{handle}/{slug} | POST /v1/formats/acme/live-commerce/runs | Every new integration. It is the address the Format detail page shows, and the shape production callers use. |
{format_id} | POST /v1/formats/skl_…/runs | A stored URL that must survive a handle or slug rename. Permanent, identical behaviour. |
sume/{slug} | POST /v1/formats/sume/sume-product-promo/runs | The Formats by Sume catalog. Any key with the scopes may call it; the run belongs to that key. |
The two shapes resolve to the same Format and run the same pipeline: same body, headers,
idempotency, caps and receipt. The receipt's format.id is always the opaque skl_… id,
whichever shape you used. A renamed handle keeps resolving for 90 days.
A team Format lives at the team's handle, and any key created in that workspace with the
right scopes may POST it. See Team Formats need a team key.
An unknown handle, an unknown slug, and a handle you cannot see all answer the same
404 format_not_found.
GET /v1/formats/{handle}/{slug} and GET /v1/formats/{handle}/{slug}/runs take the same
address.
Request body
Every field is optional on its own, but the body must name at least one of instruction,
input, previous_run_id or attachments. {} or {"input": {}} is
400 invalid_request, not a run on the Format's default. Unknown top-level fields are
400 unknown_parameter, with a suggestion when the name is close (webook_url →
webhook_url).
| Field | Notes |
|---|---|
instruction | The task in your words, up to 8000 characters. Omit it to run the Format's own default instruction. Composed after the Format body, so it wins where they disagree. See What is accepted and what is carried. |
input | A JSON object of caller data: at most 64 top-level keys and 2 MiB (2097152 UTF-8 bytes, compact). You choose the shape; the Format reads the keys it knows. Media URLs inside it share the run's attachment budget. See input caller data. |
attachments | Up to 30 images the agent can see: { "type": "input_image", "image_url": … } or { …, "asset_id": … }. See Attachments. |
output_schema | Bind a JSON Schema and output comes back in that shape. response_format is the OpenAI-shaped alias; sending both is 400 invalid_request. Rules and failure modes: Structured output. |
primary_output_key | The key in output whose URL becomes primary_output_url. Up to 64 characters. |
generation_spend_cap_usd | This run's generation ceiling, up to the platform maximum of $500. Omit it to inherit the Format's cap; a number above the Format's cap is honored, not clamped; null runs at $500; 0 is rejected. See Spend caps. |
communication.webhook_url | Public HTTPS URL that receives one signed format.run.terminal POST when the run completes or fails. callback_url is an accepted alias; top-level webhook_url / callback_url / mode are normalized into communication. Delivered on both hosts. See Webhook. |
communication.mode | async (default) or webhook. Descriptive only: supplying a URL is what arms delivery. |
previous_run_id | Continue an earlier run of this Format as another turn of the same conversation instead of starting fresh. See Continue a run. |
on_active_run | What to do when a run of this Format is already in flight. Default allow (runs concurrently; workspace generation concurrency still applies). skip records a skipped run instead; reject answers 409 format_run_in_progress. Scheduled Actions default to skip, so do not copy their bodies here. |
model | Agents catalog id for the LLM that orchestrates the run, for example gpt-5.6-sol, the default. Selects the orchestrator only; image, video and audio models are chosen by the Format's tools. An id outside the catalog is 400 invalid_request. The receipt echoes the id that ran. |
idempotency_key | Body spelling of the Idempotency-Key header. The header wins when both are sent. |
Headers: Authorization: Bearer $SUME_API_KEY or x-api-key: $SUME_API_KEY (one, never
both), Content-Type: application/json, and Idempotency-Key on every create. The request
body is capped at 4 MiB (413 payload_too_large).
caller data
input is the JSON object your service hands to the run. It is not a wire schema and Sume
publishes no field list for it: you choose the shape and the Format's recipe reads the keys it
recognises. Two integrations calling the same Format may send completely different objects,
and both are correct. The example bodies on these pages are one integrator's convenient
shape, not a contract.
What the API checks, and nothing else:
| Check | Rule | On failure |
|---|---|---|
| Type | A JSON object. Arrays, strings and numbers are refused; null and omission both mean no input. | 400 |
| Property count | At most 64 top-level keys. Nested keys are not counted, so grouping is free. | 400 |
| Size | At most 2097152 UTF-8 bytes (2 MiB) on the compact serialization. | 400 |
| Media references | HTTPS URLs to image, video or audio files, at any depth, share the run's attachment budget: 30 in total, at most 30 images, 10 videos, 10 audio. See Media referenced from input. | 400 invalid_attachment |
Keep the two JSON fields apart in your head: input is loose data going in, output_schema
is a strict contract coming out. input is written whole to a file in the run's workspace and
the agent is told it is caller-supplied data, not instructions. That is exactly where scraped
product copy, a customer's message or a supplier's field belongs, rather than concatenated into
instruction. It is a trust boundary, not a sandbox: runs are spend-capped, so the blast
radius of a hostile payload is bounded by the cap, but do not pass raw untrusted text through
on purpose.
input does not reach the structured output. output is produced from what the run made and
said, so a value you sent, an order id or a SKU, cannot be echoed back unless the run repeats
it. Keep your identifiers on your side, keyed by data.id or by your Idempotency-Key. See
Where your object comes from.
What is accepted and what is carried
| Field | Accepted | Carried to the run |
|---|---|---|
instruction | 8000 characters | The first ~4000 characters, as prompt text. Keep it well inside that and put data in input. |
input | 2 MiB | All of it, as a file the agent reads. Never truncated. |
| The Format body | No cap beyond 100 MiB per package file | All of it, attached as files. |
An empty input ({}) adds no file and no block at all, byte-identical to omitting the field.
A Format that says "read product_url from the input" then has nothing to read.
Idempotency
Send Idempotency-Key on every create and derive it from the thing being made, your order id
and a version you bump when you deliberately want a re-run, not from the moment of asking. A
uuidgen per request makes the header decorative.
| Replay | Result |
|---|---|
| Same key, same body | 200 with the original receipt and idempotency_hit: true. No second run, no second charge. |
Same key, different body, including a different instruction or attachment list | 409 idempotency_conflict. Nothing runs. |
| Same key, two requests at the same moment | One wins; the other gets 409 idempotency_key_in_use, which is retryable. Wait about a second and resend to receive the original run. |
Same key after a create that failed (402, 503, …) | The key was released. Fix the cause and retry with the same key. |
Keys are scoped to one Format: the same key sent to two Formats starts two runs. A key is up to 255 characters.
Spend caps
Every Format carries a generation spend cap and a run can never spend past its own effective
cap. Read the Format's cap from generation_spend_cap_usd_micros on
GET /v1/formats/…; a Format that never named one reports the
platform default of $400.
generation_spend_cap_usd on the request names this run's own ceiling:
| You send | The run's cap |
|---|---|
| Nothing | The Format's cap. |
| A number up to 500 | That number. Above the Format's own cap is honored, not clamped. |
null | The platform maximum, $500. It lifts the ceiling; it does not remove it. |
0, or above 500 | 400. A run that cannot spend cannot deliver. |
The effective cap comes back on every receipt as usage.generation_spend_cap_usd_micros, and
what the run actually spent against it as usage.billable_amount_usd_micros. Production
live-commerce integrations run with caps around $120; a single-scene retry on the same thread
needs a fraction of that. What counts against the cap is metered at the rates on the
API pricing page.
Errors and spend covers what happens at the wallet.
Keys and scopes
Scopes
| Scope | Needed for |
|---|---|
formats:read | List and read Formats, read and list runs, read queues. |
formats:write | Create a run, create a bulk queue, cancel a run, redeliver a webhook. |
Scopes are fixed when a key is minted. Keys created before the Formats API shipped do not carry
these, and a key missing one fails every Format request with 403 insufficient_scope, never a
404. Create a new key at API keys and rotate to
it.
Service-account keys cannot create Format runs. They fail with 403 insufficient_scope and
details.reason of service_account_format_runs_unsupported.
Team Formats need a team key
A Format owned by a team workspace is invoked with an API key created in that workspace.
Membership is not enough: a personal key held by a team member is refused with
403 workspace_key_required, and details.workspace_id names the workspace the key has to
come from.
The rule follows the money. A team Format's runs bill the team wallet, count against the
team's generation concurrency, and read their media back through the team workspace. A
personal key would split those, and used to produce runs that made a real video and then
reported output_schema_unsatisfied with nothing harvested. Create the key from the team's
dashboard. Personal keys stay right for personal Formats.
Reading is keyed the same way: a team key lists that workspace's Formats for every member, and
never your personal ones. A team handle you are not a member of is 404, indistinguishable
from one that does not exist, so a 403 workspace_key_required always means "right team,
wrong key".
Running a Format another workspace shared with you
A team Format can be shared with another workspace — never a user — the way a GitHub
repository takes an outside collaborator. The owner workspace adds your team handle on the
Format's Access tab (live immediately), or invites it with
POST /v1/formats/{handle}/{slug}/grants and your admin accepts with
POST /v1/format-grants/{grant_id}/accept using a key created in your workspace. From then on
you call the Format at the owner's address with your own team key:
There is no workspace field on the request: the key is the actor. The run, its spend, its
concurrency slot and its media are your workspace's, not the owner's, and GET …/runs with
your key lists your runs only. The owner still decides whether the Format takes API calls at
all — its Status and API call trigger apply to every caller — and can remove your access, after
which the address is a 404 for you again. A personal key is refused the same way it is on any
team Format, and membership of the owner workspace does not stand in for a grant. If you hold
seats in both workspaces, the key you bring decides: each workspace keeps its own run history
and its own bill, and a key from one never reads the other's runs.
Runs over the API are unattended
A Format written for chat may pause and wait for a person: "approve these stills before I make the video" is a deliberate quality gate in the Agents UI. Over the API nobody is there, so the run is told those approvals are already granted and to carry on to the paid step within its spend cap.
A run that genuinely cannot finish comes back failed, never a half-finished completed:
One qualification: a completed run always did real work and always fills artifacts[], but
it can still carry output_error when the projection did not match your output_schema.
Check output_error before reading output. See
When output cannot be produced.
401 vs 403 vs 404
HTTP class is the first branch; error.code is the second. A missing key is
401 unauthorized. A known key missing formats:read / formats:write is
403 insufficient_scope, never 404 format_not_found. Scopes cannot be patched onto an
existing key; mint a new one and rotate.
| HTTP | error.code | When |
|---|---|---|
| 401 | unauthorized | No key, malformed key, two credentials at once, revoked, or unknown. next_action is authenticate. |
| 403 | insufficient_scope | Valid key missing formats:read / formats:write; details.required_scope names it. Also a service-account key on a run create or package write, with details.reason set. next_action is authenticate. |
| 403 | workspace_key_required | You are a member of the team workspace but brought a personal key. details.workspace_id names the workspace to mint one in. A team key from another workspace is judged by the grant instead: it runs when that workspace holds an accepted grant, and is a 404 when it does not. |
| 404 | format_not_found | Unknown, archived, outside this key's workspace, a team handle you are not a member of, or a shared Format whose grant is still pending or was removed. A member's team key on the right handle is never this 404. |
| 404 | format_run_not_found | Unknown run id, or a run belonging to another owner. |
| 404 | format_run_queue_not_found | Unknown bulk queue, or another owner's. |
| 404 | previous_run_not_found | previous_run_id is unknown or not yours. |
| 404 | format_content_not_found | The Format exists for this key but that package path does not. |
Status classes follow RFC 9110; insufficient_scope is the RFC 6750 vocabulary. The 404 on
someone else's Format is a deliberate tenancy hide, not a hint that the Format exists
elsewhere (design record: #2393).
Errors
Everything the create call can answer with, in one table, is on Errors and spend. The ones you will meet first:
| Status | error.code | What to do |
|---|---|---|
| 400 | invalid_request | The body named none of instruction / input / previous_run_id / attachments, sent both output_schema and response_format, or failed a size cap. Read message. |
| 400 | output_schema_invalid | Your schema is outside the supported subset. details.violations[] names every problem. |
| 401 | unauthorized | Fix the header, not the body. |
| 402 | insufficient_credits | The workspace cannot fund the run. next_action is add_funds. |
| 403 | insufficient_scope, workspace_key_required | Mint the right key. See above. |
| 404 | format_not_found | Check the address and which key you are holding. |
| 409 | format_inactive, format_api_trigger_disabled | The owner workspace sets these on the Format page's API tab. They apply to every caller, including workspaces the Format is shared with. |
| 409 | idempotency_conflict, idempotency_key_in_use | Unstable key derivation, or a concurrent duplicate. See Idempotency. |
| 429 | rate_limited | Wait retry-after seconds. |
| 503 | studio_agent_upstream_unavailable | A Sume-side outage. Retry with the same Idempotency-Key. |
error.code is a lowercase token you can switch on; message is for humans and may change.
Rate limits
Every response carries ratelimit-limit, ratelimit-remaining and ratelimit-reset, and a
429 adds retry-after. Reads and writes have separate budgets and the read budget is forty
times the write one, so a poll loop cannot starve your own creates. A 429 names the budget
it came from in error.details.scope. Full table:
Errors and spend.
Next
- Runs and results: the receipt, polling, webhooks, continuing and canceling
- Structured output: bind a schema and get typed JSON back
- Errors and spend: every code, credits, and rate limits
- Cookbook: real-shaped bodies you can paste
- Bulk runs: the same body, up to 100 times, with a concurrency window

