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

# Structured outputs

> Constrain a model's response to a JSON schema so you get parseable, predictable JSON

## What structured outputs are

A structured output is a model response that is forced to match a [JSON Schema](https://json-schema.org/) you supply. Instead of asking for JSON in the prompt and hoping the model complies, you hand Valar the shape you want and the response comes back as JSON that conforms to it.

Reach for structured outputs when the response feeds code rather than a human:

* **Reliable parsing**: every response is valid JSON with the fields you declared, so `json.loads` never trips over prose, code fences, or trailing commentary.
* **Extraction**: pull typed fields out of unstructured text, such as turning a support email into `{ category, priority, summary }`.
* **Downstream automation**: route, store, or act on the result without a brittle post-processing step.

Structured outputs are supported on the Responses, Chat Completions, and Messages APIs. The field you set differs per API, but the JSON Schema you pass is the same.

## Defining a schema per API

Each tab points at the same base URL, `https://api.valarhq.ai/v1`, and authenticates with `Authorization: Bearer $VALAR_API_KEY`. The example extracts a support ticket into a fixed shape.

<Tabs>
  <Tab title="Responses">
    On the [Responses API](/api-reference/responses-api/create-a-response), set `text.format` to a `json_schema` object. The schema lives directly under `schema`, alongside a `name` and `strict`.

    <Note>
      `text.format.type: "json_object"` is **not** supported on the Responses API. Use `json_schema` to constrain the output.
    </Note>

    ```python theme={"system"}
    import json
    from openai import OpenAI

    client = OpenAI(
        base_url="https://api.valarhq.ai/v1", # or read OPENAI_BASE_URL from the environment
        api_key="YOUR_VALAR_API_KEY",  # or read OPENAI_API_KEY from the environment
    )

    ticket_schema = {
        "type": "object",
        "properties": {
            "category": {
                "type": "string",
                "enum": ["billing", "bug", "feature_request", "account", "other"],
            },
            "priority": {"type": "string", "enum": ["low", "medium", "high", "urgent"]},
            "summary": {"type": "string"},
        },
        "required": ["category", "priority", "summary"],
        "additionalProperties": False,
    }

    response = client.responses.create(
        model="moonshotai/Kimi-K2.7",
        input="Triage this ticket: 'I was charged twice for my May invoice and need a refund ASAP.'",
        text={
            "format": {
                "type": "json_schema",
                "name": "support_ticket",
                "schema": ticket_schema,
                "strict": True,
            }
        },
    )

    ticket = json.loads(response.output_text)
    print(ticket["category"], ticket["priority"])
    ```
  </Tab>

  <Tab title="Chat completions">
    On [Chat Completions](/api-reference/chat-completions-api/create-a-chat-completion), set `response_format` to a `json_schema` object. The schema is nested one level deeper, under `json_schema.schema`. This API also accepts `{ "type": "json_object" }` for free-form JSON without a schema.

    ```python theme={"system"}
    import json
    from openai import OpenAI

    client = OpenAI(
        api_key="YOUR_VALAR_API_KEY",
        base_url="https://api.valarhq.ai/v1",
    )

    ticket_schema = {
        "type": "object",
        "properties": {
            "category": {
                "type": "string",
                "enum": ["billing", "bug", "feature_request", "account", "other"],
            },
            "priority": {"type": "string", "enum": ["low", "medium", "high", "urgent"]},
            "summary": {"type": "string"},
        },
        "required": ["category", "priority", "summary"],
        "additionalProperties": False,
    }

    completion = client.chat.completions.create(
        model="moonshotai/Kimi-K2.7",
        messages=[
            {
                "role": "user",
                "content": "Triage this ticket: 'I was charged twice for my May invoice and need a refund ASAP.'",
            }
        ],
        response_format={
            "type": "json_schema",
            "json_schema": {
                "name": "support_ticket",
                "schema": ticket_schema,
                "strict": True,
            },
        },
    )

    ticket = json.loads(completion.choices[0].message.content)
    print(ticket["category"], ticket["priority"])
    ```
  </Tab>

  <Tab title="Messages">
    On the [Messages API](/anthropic-sdk) (`/v1/messages`), set `output_config.format` to a `json_schema` object. The schema lives directly under `schema`, alongside `name` and `strict` — the same flat shape the Responses API uses. The Anthropic Python SDK doesn't type this parameter, so pass it through `extra_body`.

    ```python theme={"system"}
    import json
    from anthropic import Anthropic

    client = Anthropic(
        auth_token="YOUR_VALAR_API_KEY",
        base_url="https://api.valarhq.ai",  # no /v1 — the SDK appends it
    )

    ticket_schema = {
        "type": "object",
        "properties": {
            "category": {
                "type": "string",
                "enum": ["billing", "bug", "feature_request", "account", "other"],
            },
            "priority": {"type": "string", "enum": ["low", "medium", "high", "urgent"]},
            "summary": {"type": "string"},
        },
        "required": ["category", "priority", "summary"],
        "additionalProperties": False,
    }

    message = client.messages.create(
        model="moonshotai/Kimi-K2.7",
        max_tokens=2048,
        messages=[
            {
                "role": "user",
                "content": "Triage this ticket: 'I was charged twice for my May invoice and need a refund ASAP.'",
            }
        ],
        extra_body={"output_config": {"format": {
            "type": "json_schema",
            "name": "support_ticket",
            "schema": ticket_schema,
            "strict": True,
        }}},
    )

    # The model may emit thinking blocks first — extract the text block and parse it.
    text = next(block.text for block in message.content if block.type == "text")
    ticket = json.loads(text)
    print(ticket["category"], ticket["priority"])
    ```
  </Tab>
</Tabs>

## Enforcing the schema with strict

Setting `strict: true` makes Valar enforce the schema during decoding, so the response is guaranteed to match the structure you declared - required fields are present, types line up, and `enum` values stay within the allowed set. Without it, the schema is treated as guidance and the model may drift.

For strict mode to hold, your schema must be one Valar can enforce. A schema that is malformed or uses an unsupported construct returns `400 invalid_request_error` rather than running the request, so validate the shape before you ship it.

<Tip>
  You rarely need to hand-write the schema. Generate it from a [Pydantic](https://docs.pydantic.dev/) model with `Model.model_json_schema()`, or from a [Zod](https://zod.dev/) schema with a JSON Schema converter, then drop the result into the `schema` field.

  ```python theme={"system"}
  from pydantic import BaseModel
  from typing import Literal

  class SupportTicket(BaseModel):
      category: Literal["billing", "bug", "feature_request", "account", "other"]
      priority: Literal["low", "medium", "high", "urgent"]
      summary: str

  ticket_schema = SupportTicket.model_json_schema()
  ```
</Tip>

## See also

* [Create a response](/api-reference/responses-api/create-a-response) - full `text.format` reference.
* [Create a chat completion](/api-reference/chat-completions-api/create-a-chat-completion) - full `response_format` reference.
* [Anthropic SDK & Claude Agent SDK](/anthropic-sdk) - the `/v1/messages` surface and `output_config.format` usage.
