Design your output schemas | Diaphora Docs | Diaphora
Design your output schemas
A session's schema is a contract: it's the exact JSON shape the model must return, and it's what every downstream session and your application reads. A good schema makes the model more accurate (it knows precisely what to produce) and makes the blueprint's output easy to consume. This guide covers the full syntax and the design habits that keep schemas clean.
Prerequisites
- The Anatomy of a session guide — you should know where the
schemasits in a session.
The basics
A schema block lists fields and their types:
schema {
summary: string
keyPoints: string[]
published: bool
}
Syntax rules:
- Field names are unquoted identifiers —
summary, not"summary". - Types:
string,int,float,bool, nested{ }objects,Type[]arrays, and$ComponentNamerefs. - Mark a field optional by appending
?:url?: string. Everything else is required.
Comments are instructions, not decoration
This is the highest-leverage habit in the whole language. Inline # comments on schema fields are read by the model and used as field descriptions:
schema {
summary: string # one concise paragraph, no bullet points
sentiment: string # exactly one of: positive, neutral, negative
score: int # confidence 0-100
}
Those comments meaningfully steer the output. Always add one to every field unless the name is completely self-explanatory — they're the cheapest quality win available.
Keep the root flat
The session name is already the outer wrapper for its output. A session called overview publishes under context.overview, so wrapping the fields in another overview object just creates redundant nesting:
# AVOID — produces context.overview.overview.summary
schema {
overview: {
summary: string
keyPoints: string[]
}
}
# PREFER — produces context.overview.summary
schema {
summary: string
keyPoints: string[]
}
Nest only when the data genuinely has sub-structure:
# FINE — metadata is a real sub-object
schema {
summary: string
metadata: {
author: string
createdAt: string # ISO 8601 timestamp
}
}
Scalar and array shorthand
When a session's whole output is a single value or a flat list, skip the block and declare the type directly:
schema string[] # the output is a list of strings
schema int # the output is a single integer
This pairs naturally with iterate: an iterating session runs once per element and collects the results into an array, so its schema must be an array, either schema type[] or a block with []:
session("elaborate",
after="gather",
iterate="context.gather.keyPoints") {
- Expand the key point "{{ .it }}" into a paragraph.
schema string[] # array required when iterate is set
}
Reuse shapes with components
When the same structure appears in more than one session, define it once in a root-level components block and reference it with $Name:
components {
schema("SourceRef") {
id: string # unique identifier of the source
title: string # human-readable title
url?: string # optional URL; omit if unavailable
}
}
session("gather") {
- Extract the sources used.
schema {
summary: string
sources: $SourceRef[] # reuse the component as an array
}
}
Component schemas follow the exact same syntax as session schema: fields, optionals, comments, nesting. Reference one anywhere a type is expected: sources: $SourceRef[] or primary: $SourceRef.
Sessions without a schema
A schema is optional. Omit it and the session still runs — its output is just the model's raw text response instead of a structured object:
parameter("query", type=string, title="Search Query")
session("search_web") {
use search
+ Search the internet for information about: {{ .params.query }}
- Provide a comprehensive answer based on the search results.
}
The session's result lands in context.search_web (and in the blueprint output) as a single string — the model's free-form answer verbatim:
{
"search_web": "Spain won the 2026 FIFA World Cup, held across Canada, Mexico, and the United States, defeating Argentina 1–0 in the final. Ferran Torres scored the winning goal in the 106th minute of extra time at New York/New Jersey Stadium on July 19, 2026."
}
Skip the schema when the session's job is to produce prose for a person to read, or when a downstream session will consume the text as free-form input. Add a schema as soon as you need to reference specific fields (context.search_web.answer), enforce a shape, or hand structured data to another tool — a downstream session can't reach into a raw string.
Common mistakes
| Mistake | Fix |
|---|---|
| Wrapping fields in a top-level object named after the session | Define fields flat at the root — the session name is the wrapper |
| Quoting field names | Use unquoted identifiers: summary: string |
A non-array schema on an iterate session |
Use schema type[] or schema { ... }[] |
| Skipping field comments | Add a # description to every non-obvious field, the model reads them |
Referencing $Name with no components block |
Define the type in a root-level components { } block |
Next steps
- Feed schema output between sessions in Data flow: params, vars & context.
- Produce one result per item with
iteratein Control the flow of a blueprint.
Shape a session's typed output — field syntax, comments the model reads as instructions, flat-vs-nested structure, scalar shorthand, and reusable $Component refs.