For AI agents: markdown of this page — /docs-content-en/ai/chat/json.md documentation index — /llms.txt
Guaranteed JSON response
The response_format parameter forces the model to return a machine-readable response instead of free text. There are two levels of strictness: json_object guarantees valid JSON of an arbitrary shape, while json_schema enforces conformance to a given schema.
`json_object` mode
With response_format: {"type": "json_object"} the model returns valid JSON in the content field. Vibecode automatically adds an instruction to the system message to return only JSON. Describe the result structure explicitly in the system message:
curl -X POST https://vibecode.bitrix24.com/v1/chat/completions \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "bitrix/bitrixgpt-5.5",
"messages": [
{"role": "system", "content": "Classify the lead. Return JSON: {\"quality\": \"high|medium|low\", \"score\": 0-100}"},
{"role": "user", "content": "Acme LLC, budget 2 million per month"}
],
"response_format": {"type": "json_object"}
}'
Response:
{
"id": "chatcmpl-acdb112cf9cdf9f9",
"object": "chat.completion",
"model": "bitrix/bitrixgpt-5.5",
"choices": [
{
"index": 0,
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "{\"quality\":\"high\",\"score\":92}"
}
}
],
"usage": {"prompt_tokens": 56, "completion_tokens": 14, "total_tokens": 70}
}
`json_schema` mode
response_format: {"type": "json_schema", "json_schema": {...}} guarantees that the model response strictly conforms to the given JSON Schema — the model cannot return extra fields, omit required ones, or mix up types. This is stricter than json_object, which guarantees only valid JSON.
Not all models support this mode. For the current list, see the data.ai.structuredOutputs.models field in the GET /v1/me response.
`json_schema` fields
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Schema name. Up to 64 characters, a-zA-Z0-9_- allowed |
schema |
object | yes | The JSON Schema itself |
strict |
boolean | no | true — strict conformance checking against the schema. Recommended value |
description |
string | no | Schema description for the model that helps it interpret the fields correctly |
Example with a schema
curl -X POST https://vibecode.bitrix24.com/v1/chat/completions \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "bitrix/openai/gpt-oss-120b",
"messages": [
{"role": "system", "content": "Return strict JSON per the schema."},
{"role": "user", "content": "Hi"}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "hello_response",
"strict": true,
"schema": {
"type": "object",
"properties": {"message": {"type": "string"}},
"required": ["message"],
"additionalProperties": false
}
}
}
}'
The choices[0].message.content field contains a string that parses into an object matching the schema exactly — for example {"message":"Hi"}.
Error response example
400 model_does_not_support_structured_outputs — the model does not support json_schema. The structuredOutputsModels field contains the list of suitable models:
{
"error": {
"message": "Model 'openai/o3' does not support response_format=json_schema. Use one of: bitrix/bitrixgpt-5.5, bitrix/bitrixgpt-5.5-thinking, openai/gpt-4o, bitrix/openai/gpt-oss-120b, openai/gpt-4o-mini.",
"type": "invalid_request_error",
"code": "model_does_not_support_structured_outputs",
"param": "response_format.type",
"structuredOutputsModels": ["bitrix/bitrixgpt-5.5", "bitrix/bitrixgpt-5.5-thinking", "openai/gpt-4o", "bitrix/openai/gpt-oss-120b", "openai/gpt-4o-mini", "openai/o3-mini"]
}
}
422 structured_output_truncated — generation was cut off at the token limit before a complete JSON document was produced:
{
"error": {
"message": "The model's response was cut off (finish_reason=length) before a complete JSON document was produced, so it does not satisfy the requested response_format. Raise max_tokens, or reduce the schema/prompt size.",
"type": "invalid_request_error",
"code": "structured_output_truncated",
"hint": "For reasoning models, max_tokens must cover the reasoning phase plus the JSON answer.",
"param": "max_tokens",
"finishReason": "length",
"suggestedMaxTokens": 2048
}
}
422 structured_output_invalid_json — the response is non-empty but does not parse as a JSON document:
{
"error": {
"message": "The model returned a non-empty response (finish_reason=stop) that is not a valid JSON document, so it does not satisfy the requested response_format. The response body was discarded rather than handed over as JSON.",
"type": "invalid_request_error",
"code": "structured_output_invalid_json",
"hint": "Retry the request, or use a model with reliable structured output; for a strict shape use response_format=json_schema. A larger token budget will not change the result.",
"finishReason": "stop"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | model_does_not_support_structured_outputs |
The model does not support json_schema. The list of suitable models is in the structuredOutputsModels field |
| 422 | structured_output_truncated |
The model cut off generation before completing the JSON or returned an empty response |
| 422 | structured_output_invalid_json |
The model returned a non-empty response that does not parse as a JSON object or array |
| 422 | reasoning_repetition |
The model got stuck in its reasoning — it kept repeating the same sentences and never started the answer. Send the request again with reasoning_effort: "none" |
The full list of common API errors — Errors.
Known specifics
A truncated response yields 422 structured_output_truncated. If the model cut off generation before completing the JSON (finish_reason: "length"), a request with response_format returns 422 with the fields finishReason and suggestedMaxTokens. Reasoning models behave this way: the reasoning phase consumes the max_tokens budget before the model writes the JSON. Handle this error and retry the request with a larger max_tokens — you can take the value from suggestedMaxTokens. For strict JSON, set max_tokens with some headroom or use a model without reasoning.
The suggestedMaxTokens and param: "max_tokens" fields are present only when finish_reason: "length" — they indicate that raising the token budget helps. If the finish reason is anything else and valid JSON is still missing, the response is also 422, but without those two fields — raising max_tokens will not help; use a model without reasoning. The hint string and the finishReason field are present in both cases, and finishReason may be null.
If a reasoning model in json_object mode finished on its own and placed a complete valid JSON document into the internal reasoning_content channel, the platform recovers that JSON and returns 200 with it in content — no extra handling is required.
An unparseable response yields 422 structured_output_invalid_json. The platform parses the answer before handing it over: if, with response_format, the model returned non-empty text that is not a JSON object or array (prose around the object, an answer wrapped in a markdown code fence, a bare value such as 42), that answer is not served as a success. The refusal carries finishReason and hint, but no param and no suggestedMaxTokens — raising the token budget does not help here; retrying does, and json_schema with strict: true does when the shape matters. Nested JSON inside a string field is not a violation: only the outer parse is checked.
Looping reasoning yields 422 reasoning_repetition, not structured_output_truncated. If a reasoning model never started the answer because it kept repeating the same sentences in reasoning_content, the refusal carries the code reasoning_repetition. It has finishReason and hint but no param and no suggestedMaxTokens: a larger token budget only makes the repetition longer. Send the request again with reasoning turned off — reasoning_effort: "none". Cycling through different wordings does not count as repetition: such a response stays structured_output_truncated. If the reasoning_content channel starts as a JSON document (with { or [), in a stream the decision is deferred while recovery is still possible (if the document is too long to be recovered, the repetition cuts the stream off at once): a complete document is recovered as described above, even with identical objects, and if nothing can be recovered, reasoning_repetition arrives. Recovery from the reasoning is done only for json_object: without response_format an answer that arrives only in reasoning_content is cut off with a reasoning_repetition event when it loops.
An attempt that ends in 422 is charged. The 422 response — whether for truncation, an unparseable answer or looping reasoning — arrives after the model has already run, so the attempt's tokens count toward the usage statistics in GET /v1/ai/usage and toward the Bitrix24 account's monthly AI quota. A retry with a raised max_tokens is another billable call.
A low max_tokens on a free reasoning model is raised automatically. If a free reasoning model is asked for a structured response and max_tokens is set explicitly below the lower bound, the platform raises it to that bound and reports it in the successful 200 response: the body carries a warnings array with an element {"code": "MAX_TOKENS_RAISED", "message": "..."}. This is not an error — read warnings as an optional field. If max_tokens is not set in the request, no adjustment is applied.
In streaming mode the error (and the recovered answer) arrives inside the stream. The service event {"error":{"code":"structured_output_truncated"}}, {"error":{"code":"structured_output_invalid_json"}} or {"error":{"code":"reasoning_repetition"}} comes before data: [DONE]. In the second case the answer chunks have already been delivered — discard them; the stream status stays 200 by protocol. JSON recovered from reasoning_content arrives as a content chunk before the terminal finish_reason chunk — read the stream to the end.
json_object does not check the structure. It guarantees only that the response parses as JSON — the set of fields and their types are left to the model. If the structure matters, use json_schema with strict: true.