Skip to content

Structured output

Structured output makes an agent return a validated object instead of text. You define a schema, attach it to an agent or to a single call, and read the object from the result. For a worked example, see Return structured output.

Strands doesn’t ask the model to write JSON in its reply. It turns your schema into a tool, and the model returns the object by calling that tool with the field values as input. The tool gives the model a typed input schema to fill in, and gives Strands a way to send validation errors back through the agent loop.

AGENT LOOP AS A TOOL TOOL CALL ERRORS VALID Your schema Model calls the tool Structured output tool validates the arguments Validated object on the agent result

For each call with a schema:

  1. Strands adds a structured output tool, built from your schema, to the agent’s tools.
  2. The model works as usual and can call your other tools along the way.
  3. The model calls the structured output tool with the field values.
  4. Strands validates that input. If it passes, the invocation ends and the object is on result.structured_outputresult.structuredOutput. When you stream the call, it’s on the final result event.
  5. Strands removes the structured output tool when the invocation ends.

A schema lists the fields the object must have. Strands builds the tool from it, so the model reads every field description.

A schema is a Pydantic model. The tool is named after the class, so a TicketTriage schema produces a TicketTriage tool. The class docstring becomes the tool description, and each Field(description=...) describes that field to the model.

Strands adds an instruction to the tool description telling the model to call the tool last, after any other tools it needs.

A schema set when you create the agent applies to every call. A schema passed to a call applies to that call only and takes precedence over the agent’s schema.

from pydantic import BaseModel
from strands import Agent
class CustomerInfo(BaseModel):
name: str
email: str
class RefundRequest(BaseModel):
order_id: str
amount: float
agent = Agent(structured_output_model=CustomerInfo, callback_handler=None)
customer = agent("This is Dana Ruiz, dana@example.com.").structured_output
refund = agent(
"Please refund order 4512. It was $49.99.",
structured_output_model=RefundRequest,
).structured_output
print(customer)
print(refund)

Typical output:

name='Dana Ruiz' email='dana@example.com'
order_id='4512' amount=49.99

Agent.structured_output() and Agent.structured_output_async() are deprecated and emit a DeprecationWarning. Pass the schema to the call instead, as shown above.

An agent schema applies to every call, including turns that only build up context. If some calls should answer in plain text, pass the schema per call instead.

A schema can hold rules the model won’t always meet on the first try, like a check that a date is in the future. When the input fails validation, Strands doesn’t raise. It returns the validation errors to the model as the tool result, and the model can call the tool again with corrected values.

from datetime import date
from pydantic import BaseModel, field_validator
from strands import Agent
class FollowUp(BaseModel):
"""A follow-up task for a support ticket."""
action: str
due_date: date
@field_validator("due_date")
@classmethod
def must_be_in_future(cls, value: date) -> date:
if value <= date.today():
raise ValueError(f"due_date must be after {date.today().isoformat()}")
return value
agent = Agent(callback_handler=None)
result = agent(
"Schedule a follow-up call with the customer next Tuesday.",
structured_output_model=FollowUp,
)
print(result.structured_output)

Typical output:

action='Schedule a follow-up call with the customer' due_date=datetime.date(2026, 10, 20)

The model doesn’t know today’s date, so its first guess can fail the check:

Example:
1. Model calls the structured output tool with a due date of 2025-07-22
2. Validation fails, and the model receives "due_date must be after 2026-10-12"
3. Model calls the tool again with 2026-10-13
4. Validation passes, and the invocation returns the object

Put rules like this in the schema, not in your prompt. The model sees the exact error message, so write it as an instruction the model can act on.

Sometimes the model ends its turn with a text reply and never calls the structured output tool. Strands then retries once with the structured output tool required, so the model’s only option is to call it.

Before the retry, Strands adds a user message to the conversation: You must format the previous response as structured output. To replace it, pass structured_output_prompt to the agent or to a single call. A value passed to a call takes precedence over the agent’s value.

from pydantic import BaseModel
from strands import Agent
class Contact(BaseModel):
name: str
email: str
agent = Agent(
structured_output_prompt="Return the contact details you found as a Contact.",
callback_handler=None,
)
result = agent("Reach Dana Ruiz at dana@example.com.", structured_output_model=Contact)
print(result.structured_output)

Typical output:

name='Dana Ruiz' email='dana@example.com'

If the model still doesn’t call the tool after the retry, the call raises StructuredOutputExceptionStructuredOutputError with the message The model failed to invoke the structured output tool even after it was forced. Catch it where your application can fall back. Return structured output shows an example.