Guides
Structured output
A run's answer as an object in your schema: zod, TypeBox, pydantic or JSON Schema
View as Markdown · Docs index for coding agents
Give a run an output schema and its answer comes back as an object in that
shape, as run.output. The schema can be zod, TypeBox or plain JSON Schema, or a
pydantic model in Python. The agent works as usual (tools, code, files) and then
answers by calling a final_output tool whose arguments are your schema. The
system prompt tells it to answer that way. camelRun checks the call against
the schema. If it does not fit, the model gets the call back with what is wrong
and tries again. Once it fits, the run ends.
import { z } from "zod";
const Triage = z.object({
category: z.enum(["bug", "billing", "question"]),
priority: z.number().int().min(1).max(4),
summary: z.string(),
});
const run = await agent.run("Triage ticket 123", { output: Triage });
run.output; // { category: "bug", priority: 2, summary: "…" }, typed z.infer<typeof Triage>Schemas
- The schema must describe an object (
type: "object"), at most 64 KB as JSON. For a list or a single value, wrap it:z.object({ items: z.array(…) }). - zod needs version 4.2 or later. The SDK sends zod's JSON Schema
(
~standard.jsonSchema) and then parses the answer with zod, so refinements and transforms apply. TypeBox (schema.Object(…), exported by the SDK) and plain JSON Schema are sent as they are. Any Standard Schema library that can produce JSON Schema also works. - pydantic models are sent as
model_json_schema()(nested models as$defs) and parsed withmodel_validate, so validators apply. - camelRun checks what the JSON Schema can express. A check that only zod or
pydantic can run, such as a refinement or a validator, happens in the SDK. If it
fails, the run fails with
output_invalid.
How the run ends
| Outcome | What you get |
|---|---|
The model called final_output with arguments that fit | status: "completed", output set, and text with anything the model said alongside |
| The model answered in prose | That answer is discarded and the model is asked once more, with a reminder. If it answers in prose again: status: "failed", error.code: "output_missing", and text with what it said |
| The answer fit the JSON Schema but failed your zod or pydantic schema | status: "failed", error.code: "output_invalid" |
| It waits on a person (an approval, a question) | input_required. Answering resumes the run, which still ends with output |
Calls whose arguments do not fit are not failures. The model sees them as tool errors and calls again.
Notes
- Per run.
outputbelongs to the prompt that starts a turn. It cannot be combined withwhileRunning: "steer". Eachrun()with anoutputschema declaresfinal_outputwith that schema. The tool stays declared until a run withoutoutput, so a series of runs with the same schema keeps the model's tool set unchanged and its prompt cache intact. Switching schemas, or switching between structured and plain runs, changes the tool set once. - History. The
final_outputcall and its result are ordinary messages in the agent's history. Later turns can refer back to the answer. - Durable. A structured run that is resumed on another node still ends with
output. - An agent with its own tool named
final_outputcannot take an output schema.