Agent Builder structured outputs into a capped Sume Agent Completion
OpenAI advises structured outputs between nodes. Map that JSON into a Sume Agent Completion, with the spend cap set by your code, not the model.

OpenAI's safety page recommends using structured outputs between nodes in an agent workflow, so that a free-text field cannot carry instructions from one step to the next. When the next step is a Sume Agent Completion, take that further: copy values out of the structured object into input, and let your own code write the instruction and the generation_spend_cap_usd. The model then picks content, but never the cost ceiling.
The recommendation is on OpenAI's Agent Builder safety page, read on 2026-10-03; the request shape is in Sume's Agent Completions docs.
Why use structured output between nodes?
The page says to use structured outputs to control data flow, because they enforce a shape and limit what an injected string can do downstream. It also says not to place untrusted variables in developer messages and to pass them through user messages. A schema with typed fields such as product_name and duration_seconds is easier to validate than a paragraph.
How do I map it into Sume?
POST /v1/agent/completions returns 202 with an agent.run receipt. input is written to a file and treated as data, never as instructions, and messages[] rejects assistant turns. generation_spend_cap_usd is required; omit it and the request fails with 400. So the mapping is: node output into input, a fixed instruction from your code, and a cap you chose.
| Request field | Set by | Why |
|---|---|---|
instruction | Your code | Fixed text, no model output |
input | Validated node output | Treated as data |
generation_spend_cap_usd | Your code | Required; not model-controlled |
What does the glue look like?
This validates a node's JSON and builds the body. It runs offline.
import json
node_output = '{"product_name": "Cedar candle", "duration_seconds": 15}'
data = json.loads(node_output)
assert isinstance(data.get('product_name'), str) and len(data['product_name']) < 120
assert isinstance(data.get('duration_seconds'), int) and 5 <= data['duration_seconds'] <= 60
body = {
'instruction': 'Make a short product video from the input file.',
'input': data,
'generation_spend_cap_usd': 2,
}
print(json.dumps(body, indent=1))What should I check after it runs?
The receipt arrives as a 202. Subscribe to agent.run.terminal rather than polling in the workflow, and treat outcome of degraded or error as a branch. Remember that an assistant role in messages[] is rejected, so do not forward a previous model reply as a turn.
Two things to keep outside the model. The first is the spend cap: because Sume refuses a completion without generation_spend_cap_usd, a missing value shows up as a 400 in your logs rather than a surprise charge. The second is the media: result files are durable media.sume.com URLs, so the next node can take a URL string from the structured output of the finished run instead of a file.
Sources
Related posts
More in Developers
- OpenAI Agents SDK client_session_timeout_seconds with Sume jobs_wait
In the OpenAI Agents SDK for Python, client_session_timeout_seconds sets the MCP read timeout. Set it above Sume's 55-second jobs_wait cap, or 0 to disable it.
- OpenAI Agents SDK max_retry_attempts and Sume paid calls
The OpenAI Agents SDK can retry MCP list_tools and call_tool. A retried Sume create must repeat its idempotency_key or you pay twice; set max_spend_usd too.
- Move OpenAI GPT Image 2.5 calls to Sume: field-by-field mapping
Which OpenAI gpt-image-2.5 parameters carry over to Sume's POST /v1/images, which change name, and which return 400 unsupported_parameter.
- OpenAI retires gpt-5.3-codex in April 2027: retired ids on Sume runs
Pin model ids you control. On a Sume Format run, omitting model uses gpt-6-sol, a retired gpt-5.6-sol runs on gpt-6-sol, and the receipt echoes the id that ran.
Written by Sume