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.

5 min readSume
All posts

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.

Who sets each field (read 2026-10-03)
Request fieldSet byWhy
instructionYour codeFixed text, no model output
inputValidated node outputTreated as data
generation_spend_cap_usdYour codeRequired; 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

All Developers posts

Written by Sume