> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gloo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Lesson 3: Structured Output

> Get JSON back from a model that your code can rely on: ask for a schema, force a tool call, and validate the result before you use it.

## When Your Code Is the Reader

So far, a person has read every answer. But often your code is the reader. You want the model to pull a date out of an email, sort a message into a category or turn a request into a list of steps, and then your program does something with the result.

Your code can't read a friendly paragraph. It needs data in a fixed shape, usually **JSON**: named fields with values your program can read directly. Getting that reliably is called **structured output**.

***

## Core Concepts

### Three Levels of Reliability

**Ask in the prompt.** "Reply only with JSON in this shape." This often works, but nothing enforces it. The model may add a sentence before the JSON, wrap it in a code block or leave out a field.

**Give the model a schema.** A **JSON Schema** is a description of the shape you want: which fields, what type each one is, which are required. When you send a schema with the request, the model is steered to answer in that shape.

**Check it yourself.** Whatever you send, **validate** the result in your code before you use it: parse the JSON and confirm it matches the shape. This is the step that makes structured output safe.

### Two Ways to Send a Schema on Gloo

**1. A JSON schema on the guarded Responses endpoint.** The guarded [Responses](https://docs.gloo.com/api-guides/responses.md) endpoint takes `text.format`, as on OpenAI's Responses API. It's documented there, not on Direct Responses, so this example uses the guarded endpoint, which gives you Gloo's guardrails at the model's rate plus 6.5%:

```json theme={null}
"text": {
  "format": {
    "type": "json_schema",
    "name": "event_details",
    "schema": { ... },
    "strict": true
  }
}
```

It also accepts `{"type": "json_object"}` for any valid JSON, or `{"type": "text"}` for plain text. The [API reference](https://docs.gloo.com/api-reference/responses/governed.md) documents the field.

**2. A tool the model must call.** Describe the shape as a function's parameters and set `tool_choice` to `"required"`. The model answers by "calling" your function, and the arguments are your JSON. This works on both the Responses and Chat Completions endpoints. The [Tool Use tutorial](https://docs.gloo.com/tutorials/completions-tool-use.md) builds a full example this way.

Support varies by model and provider, so test with the model you pin. Either way, validate the result.

### Why Validate Even With a Schema

A schema makes the right shape much more likely. It doesn't make the content correct. The model can still:

* return a date in the right format that is the wrong date
* stop partway when it reaches the output limit, leaving JSON that won't parse
* fill a required field with a guess when the input didn't contain the answer

Validation always catches the cut-off answer. It catches a wrong value only if you check values as well as types. For guesses, give the model a way to say "not found", such as allowing `null`.

***

## Get Started

This example pulls event details out of a message with a JSON schema, then validates the result with [Pydantic](https://docs.pydantic.dev/), a popular Python library for checking data.

```bash theme={null}
pip install openai pydantic
```

```python Python theme={null}
import os
from openai import OpenAI
from pydantic import BaseModel, ValidationError

client = OpenAI(
    api_key=os.environ["GLOO_API_KEY"],
    base_url="https://platform.ai.gloo.com/ai/v2/guarded",
)


class EventDetails(BaseModel):
    title: str
    date: str | None       # YYYY-MM-DD, or null if the message doesn't say
    location: str | None
    needs_childcare: bool


schema = {
    "type": "object",
    "properties": {
        "title": {"type": "string"},
        "date": {"type": ["string", "null"], "description": "YYYY-MM-DD, or null if not stated"},
        "location": {"type": ["string", "null"]},
        "needs_childcare": {"type": "boolean"},
    },
    "required": ["title", "date", "location", "needs_childcare"],
    "additionalProperties": False,
}

message = (
    "Hi! We're planning the Fall Harvest Dinner on 2026-11-14 in the fellowship hall. "
    "Could someone help with the little ones during the meal?"
)

response = client.responses.create(
    model="gloo-openai-gpt-5-mini",
    instructions="Extract event details from the message.",
    input=message,
    text={
        "format": {
            "type": "json_schema",
            "name": "event_details",
            "schema": schema,
            "strict": True,
        }
    },
)

if response.status != "completed":
    print("The answer was cut off. Raise max_output_tokens and try again.")
else:
    try:
        event = EventDetails.model_validate_json(response.output_text)
        print(event)
    except ValidationError as err:
        print("The model's JSON didn't match the shape:", err)
```

```bash cURL theme={null}
curl -X POST 'https://platform.ai.gloo.com/ai/v2/guarded/responses' \
  -H "Authorization: Bearer $GLOO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "gloo-openai-gpt-5-mini",
    "instructions": "Extract event details from the message.",
    "input": "Hi! We're planning the Fall Harvest Dinner on 2026-11-14 in the fellowship hall. Could someone help with the little ones during the meal?",
    "text": {
      "format": {
        "type": "json_schema",
        "name": "event_details",
        "schema": {
          "type": "object",
          "properties": {
            "title": { "type": "string" },
            "date": { "type": ["string", "null"], "description": "YYYY-MM-DD, or null if not stated" },
            "location": { "type": ["string", "null"] },
            "needs_childcare": { "type": "boolean" }
          },
          "required": ["title", "date", "location", "needs_childcare"],
          "additionalProperties": false
        },
        "strict": true
      }
    }
  }'
```

### Using a Tool Instead

To use the tool approach, put the same schema in a function's `parameters` and require the call. The JSON arrives in the `arguments` of a `function_call` item. Tool definitions use the nested Chat Completions schema, `{"type": "function", "function": { ... }}`, even on the Responses endpoint:

```python theme={null}
import json

response = client.responses.create(
    model="gloo-openai-gpt-5-mini",
    input=message,
    tools=[{
        "type": "function",
        "function": {
            "name": "save_event",
            "description": "Save the event details found in the message.",
            "parameters": schema,
        },
    }],
    tool_choice="required",
)

call = next((item for item in response.output if item.type == "function_call"), None)
if response.status != "completed" or call is None:
    print("No finished tool call came back, so there is nothing to save. Try the request again.")
else:
    event = EventDetails.model_validate(json.loads(call.arguments))
```

***

## Good to Know

* **Don't stream JSON you need to parse.** You can't validate half an object. Wait for the full response, then parse it.
* **Leave room in the output limit.** If the answer stops at `max_output_tokens`, the response's `status` is `incomplete` and the JSON is cut off. Reasoning models use the same limit for their reasoning, so give them extra room.
* **Check values, not only types.** A schema can confirm that `date` is a string. Your code should confirm it's a real date in the range you expect.
* **Fail safely.** When validation fails, retry once or ask the user, and never act on data that didn't pass. The [Bible Translations tutorial](https://docs.gloo.com/tutorials/bible-translation.md#use-structured-output-with-typed-segments) shows a design that renders nothing it couldn't verify.

***

## Key Takeaway

When your code reads the answer, ask for structured output. Send a JSON schema with `text.format` on the Responses endpoint, or require a tool call whose parameters are your schema. Then validate the result before you use it: a schema shapes the answer but doesn't guarantee it's right.

> [!TIP]
>
> **Try it on Gloo:** the [Completions with Tool Use](https://docs.gloo.com/tutorials/completions-tool-use.md) tutorial turns a plain-English goal into a structured plan, with complete scripts in several languages.

***

## Learn more

* [Responses API reference](https://docs.gloo.com/api-reference/responses/governed.md): the `text` and `response_format` fields
* [Completions with Tool Use](https://docs.gloo.com/tutorials/completions-tool-use.md): structured output through a required tool call
* [Tool Use](https://docs.gloo.com/api-guides/tool-use.md): tool definitions on the Responses and Chat Completions endpoints

***

## What's next

Next, you'll let the model call your functions for real, and connect your coding assistant to Gloo's docs.

> [card] [Lesson 4: Tools and MCP](https://docs.gloo.com/ai-learning-center/gloo-ai-104/tools-and-mcp.md)
>
> The tool loop, and what MCP is.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.