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

# JSON mode and result validation

> Run Chat and Responses json_object examples and distinguish valid JSON, field constraints, and business correctness.

## Represented fields

| Protocol | JSON mode | Strict Schema mode |
| - | - | - |
| Chat | `response_format: {type: json_object}` | `response_format.json_schema` |
| Responses | `text: {format: {type: json_object}}` | json\_schema configuration inside `text.format` |

The DTO represents these fields and current Chat ↔ Responses text-format converters map json\_object. Actual model/channel support is still required; not every compatible model supports JSON. Function-declaration strict limitations do not automatically apply to final-text formatting.

JSON mode suits integrations needing JSON without server-side Schema constraints. When json\_schema is supported and appropriate, see [structured output](/en/guides/structured-output). Do not silently downgrade failed Schema requests while business code assumes stronger validation.

## Three validation layers

1. **Protocol completion**: Check Chat stop, role, refusal, and tools, or Responses completed status and output items. A truncated result that happens to parse still fails completion.
2. **JSON syntax**: Explicitly instruct the model to return JSON and parse complete text. Markdown fences, whitespace, adjacent objects, and malformed JSON fail; this example neither repairs nor retries them.
3. **Business shape**: Valid JSON may still be an array, null, missing fields, or mistyped values. The example requires sku=NOTEBOOK, quantity as a safe integer from 0–100, and a present notes field containing a string or null. Extra fields and coercion are rejected.

`notes: null` differs from missing notes, and string `"3"` differs from integer 3. Structural validity does not establish extraction accuracy or authorize inventory changes, payments, or other actions. Use an existing Schema validation library for complex business objects.

## Example boundaries

Set `API_JSON_PROTOCOL=chat|responses` to select one protocol; each run makes one request. Responses accepts output\_text from exactly one assistant message, allowing multiple text blocks. It does not combine independent messages into one business object. Reasoning items are ignored without logging; refusals and function calls fail.

JSON mode does not establish that OpenRouter's response-healing plugin is active on this gateway. Parsing and business validation happen in the client; the gateway does not guarantee application field semantics. Parse streamed JSON only after successful completion, not each delta individually. See [streaming](/en/guides/streaming) and [troubleshooting](/en/guides/troubleshooting).

Source: `relaykit/dto/openai_request.go` and Chat / Responses request converters. See the upstream [JSON mode guide](https://developers.openai.com/api/docs/guides/structured-outputs?api-mode=responses).

## Runnable example

Run from the documentation directory; keep `http-client.mjs` when copying. See the [REST runtime](/en/guides/http-runtime).

```sh theme={null}
export API_ROOT_URL='https://gateway.example.com'
export API_KEY='YOUR_API_KEY'
export API_MODEL='YOUR_MODEL_ID'
export API_JSON_PROTOCOL='chat'
node examples/json-mode.mjs
```

```javascript theme={null}
import { requestJSON } from './http-client.mjs';

try {
  const model = process.env.API_MODEL;
  const protocol = process.env.API_JSON_PROTOCOL || 'chat';
  if (!model?.trim() || !['chat', 'responses'].includes(protocol)) throw new Error('Invalid configuration.');
  const instruction = 'Return only a JSON object with exactly sku, quantity, notes. sku must be NOTEBOOK, quantity an integer from 0 to 100, and notes a string or null. Extract: three notebooks, no notes.';
  const result = await requestJSON(protocol === 'chat' ? '/v1/chat/completions' : '/v1/responses',
    protocol === 'chat' ? { model, messages: [{ role: 'user', content: instruction }], response_format: { type: 'json_object' } }
      : { model, input: instruction, text: { format: { type: 'json_object' } }, store: false });
  let text;
  if (protocol === 'chat') {
    const choice = result.choices?.[0];
    const message = choice?.message;
    if (!Array.isArray(result.choices) || result.choices.length !== 1 || choice?.index !== 0
      || choice.finish_reason !== 'stop' || message?.role !== 'assistant'
      || message.refusal != null || message.tool_calls != null || message.function_call != null
      || typeof message.content !== 'string') throw new Error('Incomplete text.');
    text = message.content;
  } else {
    if (result.status !== 'completed' || !Array.isArray(result.output)) throw new Error('Incomplete response.');
    const parts = [];
    let messages = 0;
    for (const item of result.output) {
      if (item?.type === 'reasoning') continue;
      if (item?.type !== 'message' || item.role !== 'assistant' || item.status !== 'completed'
        || !Array.isArray(item.content) || ++messages > 1) throw new Error('Unexpected output.');
      for (const part of item.content) {
        if (part?.type !== 'output_text' || typeof part.text !== 'string') throw new Error('Unexpected content.');
        parts.push(part.text);
      }
    }
    text = parts.join('');
  }
  // Parse only complete JSON; do not strip fences, repair truncation, or coerce field types.
  const value = JSON.parse(text);
  if (!value || Array.isArray(value) || Object.keys(value).length !== 3 || value.sku !== 'NOTEBOOK'
    || !Number.isSafeInteger(value.quantity) || value.quantity < 0 || value.quantity > 100
    || !(value.notes === null || typeof value.notes === 'string')) throw new Error('Invalid business object.');
  console.log(JSON.stringify({ sku: value.sku, quantity: value.quantity, notes: value.notes }));
} catch {
  console.error('JSON mode example failed. Check protocol, completion, and business fields. No automatic repair or retry.');
  process.exitCode = 1;
}
```

[阅读中文](/zh/guides/json-mode)


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