---
title: "Structured output"
subtitle: "Make a prompt return JSON that matches a schema."
---

Attach a JSON schema to a prompt, and every call to that prompt returns JSON in that shape. The schema is saved with the prompt version, so your code doesn't send it.

## Set it up in the editor

<Steps>
<Step title="Open the model settings">

Go to **Gateway** › **Prompts** and open your prompt. In the side panel's **Settings** tab, click the settings button next to **Model settings**.

</Step>
<Step title="Choose a response format">

Set **Response format**:

| Option | Output |
| --- | --- |
| **Text** | Free text (the default). |
| **JSON Object** | Valid JSON, with no fixed shape. |
| **JSON Schema** | JSON that matches your schema. |

</Step>
<Step title="Add the schema">

For **JSON Schema**, open the **Schema** menu and pick a saved schema, or click **New** and paste a definition. **Examples** loads a sample schema, and **Generate** writes one from a description. To save the schema for reuse, fill in **Name**. Click **Save**.

```json
{
  "name": "ticket_triage",
  "strict": true,
  "schema": {
    "type": "object",
    "properties": {
      "category": { "type": "string", "enum": ["billing", "login", "bug", "feature_request", "other"] },
      "urgency": { "type": "string", "enum": ["low", "medium", "high"] },
      "summary": { "type": "string" }
    },
    "required": ["category", "urgency", "summary"],
    "additionalProperties": false
  }
}
```

The editor requires a top-level `name`, and a `schema` of `"type": "object"` with at least one property. `required` can only list properties that exist. If `additionalProperties` is missing, the editor sets it to `false`.

</Step>
<Step title="Turn on Enforce format">

Set **Enforce format** to **True**. API calls use the response format only when this is on.

<img src={"/docs/images/prompts-advanced/model-settings-json-schema-light.png"} width="300" className="block dark:hidden" alt="Model settings with Response format set to JSON Schema, the ticket_triage schema selected, and Enforce format set to True" />
<img src={"/docs/images/prompts-advanced/model-settings-json-schema-dark.png"} width="300" className="hidden dark:block" alt="Model settings with Response format set to JSON Schema, the ticket_triage schema selected, and Enforce format set to True" />

<Warning>
**Run** in the editor always applies the response format, so a test run can return JSON while your API calls don't. Check **Enforce format** before you commit.
</Warning>

</Step>
<Step title="Commit and deploy">

Commit the version and deploy it, as in the [Quickstart](/docs/documentation/features/prompt-management/prompt-management-quickstart). Calls without a `version` use the deployed version.

</Step>
</Steps>

## Call the prompt

Call the prompt as usual, then parse the message content as JSON.

<Tabs>
<Tab title="Python">
```python
import json
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.respan.ai/api/",
    api_key=os.environ["RESPAN_API_KEY"],
)

response = client.chat.completions.create(
    model="gpt-5.5",  # the prompt's saved model is used
    messages=[],
    extra_body={
        "prompt": {
            "prompt_id": "YOUR_PROMPT_ID",
            "schema_version": 2,
            "variables": {"ticket_body": "I was charged twice for the Team plan this month."},
        }
    },
)

triage = json.loads(response.choices[0].message.content)
print(triage["category"], triage["urgency"])
```
</Tab>
<Tab title="TypeScript">
```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.respan.ai/api/",
  apiKey: process.env.RESPAN_API_KEY,
});

const response = await client.chat.completions.create({
  model: "gpt-5.5", // the prompt's saved model is used
  messages: [],
  // @ts-expect-error `prompt` is a Respan field
  prompt: {
    prompt_id: "YOUR_PROMPT_ID",
    schema_version: 2,
    variables: { ticket_body: "I was charged twice for the Team plan this month." },
  },
});

const triage = JSON.parse(response.choices[0].message.content ?? "{}");
console.log(triage.category, triage.urgency);
```
</Tab>
</Tabs>

The content is a JSON string in your schema's shape:

```json
{
  "category": "billing",
  "urgency": "high",
  "summary": "Customer was charged twice for the Team plan and wants the duplicate refunded."
}
```

For what `schema_version: 2` does, see [Prompt schema](/docs/documentation/features/prompt-management/advanced#prompt-schema).

## Saved schemas

A schema saved with a **Name** shows up in the **Schema** menu of every prompt. Picking one copies it into the prompt version, so later edits to the saved schema don't change prompts that already use it.

To add, edit, or delete saved schemas, click **Schemas** at the top right of the prompt editor, or **Manage** in the **Schema** menu. In the **Schemas** list, click **New schema** to add one, click a schema to edit it, or open its **⋯** menu to delete it. You can also use the [response format presets API](/docs/apis/prompts/create-response-format-preset).

## Troubleshooting

- **The response isn't JSON.** Open the deployed version and check that **Enforce format** is **True**. Also check that the model supports structured output.
- **The provider rejects the schema.** With `"strict": true`, OpenAI models need every property listed in `required` and `"additionalProperties": false`. For an optional field, use a type like `["string", "null"]`.
- **The call uses an older schema.** Deploy the version with the new schema, or pin it with `version`.
