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 }, no response wrapper. If you declared fields sender_name and topic, 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 _raw if you need to handle malformed output gracefully.
  • score → { score: number } (0-100, clamped).
  • category → { category: string } — matched against your declared categories, case-insensitive: an exact match wins, otherwise the longest declared category the answer contains (so "not spam" is never read as "spam"); 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 } — the generated image, stored and returned as a URL. The image model is picked automatically (see Config → Which model runs); usage.provider, usage.model and usage.modelSource say which one ran. No tokens are reported, since it isn't a chat completion.
  • audio → { url, voice, speech } — the text is generated normally, then spoken by a speech model and stored as an audio file. speech is { provider, model, modelSource } for the speech model, voice is the voice actually used, and usage describes the text model.

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.

text
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 }]