outputFormat controls two things: the instruction appended to the system prompt (what you're asking the model to produce), and the exact key(s) the step's result is returned under — which is what downstream steps reference with {{yourStep....}}. Every format also always includes tokensUsed and a usage object (inputTokens, outputTokens, totalTokens, model, provider, protocol, finishReason, and truncated — a boolean, true when the response was cut off by maxTokens, worth checking if output looks incomplete).
Shape per format
- text →
{ response: string }— reference as{{step.response}}. - json → flat, spread directly —
{ ...yourDeclaredFields }, noresponsewrapper. If you declared fieldssender_nameandtopic, reference{{step.sender_name}}directly, not{{step.response.sender_name}}. If the model's output fails to parse as JSON, you get{ _raw: "<the raw text>" }instead — check for_rawif you need to handle malformed output gracefully. - score →
{ score: number }(0-100, clamped). - category →
{ category: string }— matched against your declared categories by case-insensitive substring, not exact match; falls back to the raw trimmed text if nothing matches. - boolean →
{ decision: boolean }— true only for a response starting with "yes"/"true"/"1" (case-insensitive). - list →
{ items: string[] }— reference the whole thing as{{step}}(which resolves to the{items:[...]}object) or{{step.items}}directly. - image →
{ url: string }(no tokensUsed for the completion itself, since it's a direct DALL-E call, not a chat completion). - audio →
{ url: string }(an MP3 URL — the text is generated normally, then spoken via TTS).
json is flat — this trips people up
Every other format nests its result under one named key (response/score/category/decision/items). json is the one exception — your declared fields are spread directly onto the result object. Don't reference {{step.response.fieldName}} for a json-format step; it's {{step.fieldName}}.
Using a list output as a Loop source
An ai_call step with outputFormat: list produces exactly the same {items:[...]} shape as list_entities and a collection variable — so it works as a Loop step's source the same way. In the builder, add your ai_call step, set its Output format to List, then in a following Loop step's "Collection to iterate" field, select that ai_call step directly.
s1: ai_call intent=generate outputFormat=list
contextFields=[{ label: topic, value: {{input.topic}} }]
instructions="Generate 5 short blog post title ideas."
s2: loop source={{s1}} itemAs=title
s2a: create_entity entity=draft_post fields=[{ title: {{title}} }, { status: draft }]