Para agentes de IA: markdown desta página — /docs-content-en/ai/chat.md índice da documentação — /llms.txt
Os artigos da documentação estão disponíveis atualmente em inglês.
Chat completions
Generate a model's response through a single OpenAI-compatible endpoint. Supports synchronous mode, streaming via Server-Sent Events, function calling, a guaranteed JSON response, and working with images.
Scope: vibe:ai
Create a chat completion
The response arrives in raw OpenAI format.
The
{success, data}wrapper used in the other Vibecode endpoints —/v1/deals,/v1/tasksand others — is absent here.This is done for compatibility with the OpenAI SDK. If you have a single client that checks
if (!response.success), add an exception for the AI Router.
POST /v1/chat/completions
Generates an AI model's response to an array of messages. The request and response format is compatible with POST /v1/chat/completions from the OpenAI API.
The endpoint's capabilities are documented on separate pages: streaming, guaranteed JSON response, function calling, image analysis and rate limits.
Request fields (body)
| Field | Type | Req. | Default | Description |
|---|---|---|---|---|
model |
string | no | auto |
Model ID or alias. See the "Model aliases" section below. If the field is not passed or equals auto, the request runs on the Bitrix24 account's default model. The list of available models — GET /v1/models |
messages |
array | yes | — | Array of conversation messages. Minimum 1, maximum 256 |
messages[].role |
string | yes | — | Role: system, user, assistant, tool |
messages[].content |
string | array | null | yes | — | Message text. Maximum 500,000 characters in a single message or 64 elements in the content array. For requests with images — an array with type: "text" and type: "image_url". An assistant message with tool_calls may have null content |
messages[].name |
string | no | — | Sender name (multi-agent scenarios) |
messages[].tool_calls |
array | no | — | Function calls from the assistant — present in the model's response when finish_reason: "tool_calls" |
messages[].tool_call_id |
string | no | — | Function call ID — required in a message with role: "tool" |
temperature |
number | no | per model | Generation temperature, range 0..2. Lower values are more precise and deterministic; higher values are more creative |
max_tokens |
number | no | per model | Maximum tokens in the response |
top_p |
number | no | — | Nucleus sampling, range 0..1 |
reasoning_effort |
string | no | per model | OpenAI-compatible reasoning level: none, minimal, low, medium, high, xhigh, max. Normalized to a platform step — see Reasoning control |
reasoning |
object | no | — | The canonical object. effort — a level from the same vocabulary. max_tokens — reasoning token budget, honored only on models with budgetTokens: true. enabled — false disables reasoning. exclude — true removes reasoning_content from the response; reasoning tokens are still billed. Takes precedence over reasoning_effort |
chat_template_kwargs |
object | no | — | Vendor format {"thinking": boolean, "reasoning_effort": string}. Accepted for compatibility, resolved to the same step and never forwarded to the model raw. Unknown keys inside are ignored. Lowest precedence of the three fields |
stop |
string | array | no | — | Stop sequences (up to 4, each up to 64 characters) |
stream |
boolean | no | false |
If true — the response arrives as a stream of Server-Sent Events |
response_format |
object | no | — | Control the response format: {"type": "text"} — the default, {"type": "json_object"} — valid JSON, {"type": "json_schema", "json_schema": {...}} — strict JSON Schema, requires model support |
tools |
array | no | — | Definitions of functions the model may call |
tool_choice |
string | object | no | — | auto (the model decides on its own), none (forbid calls) or {"type": "function", "function": {"name": "..."}} (force a specific one) |
Reasoning control
Reasoning models think before they answer. Reasoning tokens count towards usage.completion_tokens and are billed as output tokens at the same rate — the price lever is volume, not the rate. Vibecode describes that volume with five steps: none — answer immediately, low — a brief sketch, medium — the model's standard reasoning when it is enabled and no level is given, high — stronger than standard, max — everything the model can do.
What a particular model supports is shown by the reasoning field in GET /v1/models. Its map maps the steps onto the model's native modes; two steps sharing one native value are a declared collapse. default is the default step, budgetTokens — reasoning token-budget support. On a model with reasoning: null, control is unavailable: the parameter is ignored with the REASONING_NOT_SUPPORTED warning, and nothing is added to the model call.
Input normalization: minimal → low, xhigh → max. reasoning.enabled: true or chat_template_kwargs.thinking: true without a level gives medium. reasoning.max_tokens without effort — medium with a budget. An empty reasoning: {} or chat_template_kwargs: {} is the same as no parameter. null in any of the three fields is equivalent to omitting it. When several fields arrive, the highest-precedence one wins: reasoning first, then reasoning_effort, then chat_template_kwargs. A value outside the vocabulary, a non-positive reasoning.max_tokens or a non-object chat_template_kwargs is rejected with 400 invalid_request.
If the model lacks the requested step, the nearest step below among the reasoning steps is applied, and warnings carries REASONING_EFFORT_ADJUSTED with the requested, applied, direction fields. The none step is never chosen by rounding — reasoning can only be disabled explicitly. If nothing exists below, the nearest step above is taken. Asking for none on a model where it cannot be disabled gives the lowest step and REASONING_CANNOT_BE_DISABLED. A budget on a model without budgetTokens is ignored with REASONING_BUDGET_NOT_SUPPORTED.
Without the parameter in the request the model's behavior does not change — nothing is added to the model call, and the response's reasoning field merely reports the default step.
In the response: the reasoning field with requested, applied, native and warnings in warnings. In streaming mode the body carries no warnings — the state travels in the X-Reasoning-Applied, X-Reasoning-Native, X-Reasoning-Warnings headers, with codes comma-separated. On a synchronous response the same headers duplicate the body. A header is absent when there is nothing to report. When the model is substituted — a DISABLED redirect or a fallback to the backup model — the step is resolved against the model that ran the request, and the reasoning field and headers describe that model. reasoning.exclude is honored on every model, including one without a declaration.
Model aliases
The model field accepts three values that resolve to the Bitrix24 account's default model (currently bitrix/bitrixgpt-5.5): auto, bitrix/free and an empty string.
The models bitrix/bitrixgpt-5 and bitrix/bitrixgpt-5-vl are marked as deprecated and work until July 31, 2026. After that date, requests to them are transparently redirected to bitrix/bitrixgpt-5.5 with the X-Model-Replacement header — see model lifecycle for details.
In addition, a partial modelId is matched against the catalog by substring: if you pass gpt-4o-mini, the platform picks a model available to your key whose identifier contains that substring.
Examples
curl — personal key
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": "You are a sales expert. Classify leads by quality."},
{"role": "user", "content": "Acme LLC, 50 users, budget 500 thousand per month."}
],
"temperature": 0.3,
"max_tokens": 300
}'
curl — OAuth application
curl -X POST https://vibecode.bitrix24.com/v1/chat/completions \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "bitrix/bitrixgpt-5.5",
"messages": [
{"role": "system", "content": "You are a sales expert. Classify leads by quality."},
{"role": "user", "content": "Acme LLC, 50 users, budget 500 thousand per month."}
],
"temperature": 0.3,
"max_tokens": 300
}'
JavaScript — personal key
const res = await fetch('https://vibecode.bitrix24.com/v1/chat/completions', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'bitrix/bitrixgpt-5.5',
messages: [
{ role: 'system', content: 'You are a sales expert. Classify leads by quality.' },
{ role: 'user', content: 'Acme LLC, 50 users, budget 500 thousand per month.' },
],
temperature: 0.3,
max_tokens: 300,
}),
})
const data = await res.json()
console.log(data.choices[0].message.content)
console.log('Tokens:', data.usage.total_tokens)
JavaScript — OAuth application
const res = await fetch('https://vibecode.bitrix24.com/v1/chat/completions', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'bitrix/bitrixgpt-5.5',
messages: [
{ role: 'system', content: 'You are a sales expert. Classify leads by quality.' },
{ role: 'user', content: 'Acme LLC, 50 users, budget 500 thousand per month.' },
],
temperature: 0.3,
max_tokens: 300,
}),
})
const data = await res.json()
console.log(data.choices[0].message.content)
Response fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique completion ID for tracking |
object |
string | Always chat.completion for a synchronous response |
created |
number | Unix timestamp of when the completion was created |
model |
string | The model actually used (may differ from the request's model on automatic fallback to a backup model or a DISABLED redirect) |
choices |
array | The model's response choices. Without the n parameter the array has a single element |
choices[].index |
number | Ordinal number of the choice |
choices[].finish_reason |
string | Reason the generation finished: stop, length, tool_calls, content_filter |
choices[].message |
object | The generated message |
choices[].message.role |
string | Always assistant |
choices[].message.content |
string | null | Response text. null when finish_reason: "tool_calls" — the content is in tool_calls |
choices[].message.tool_calls |
array | List of function calls (if the model decided to call them) |
warnings |
array | Warnings about what the platform changed in the request or what to account for in the response. Every element carries at least code and message. Some warnings add more fields. Known codes: MAX_TOKENS_RAISED, COWORK_QUOTA_FALLBACK (also carries tier, nextTier, resetAt), THINKING_TRUNCATED, REASONING_EFFORT_ADJUSTED (carries requested, applied, direction), REASONING_NOT_SUPPORTED, REASONING_CANNOT_BE_DISABLED, REASONING_BUDGET_NOT_SUPPORTED, TEMPERATURE_OVERRIDDEN_BY_REASONING. The field is absent when there are no warnings |
reasoning |
object | Present on models with a reasoning declaration. requested — the normalized step from the request, null when no parameter was sent. applied — the step actually applied. native — the model's native mode for that step. See Reasoning control |
usage.prompt_tokens |
number | Input tokens |
usage.completion_tokens |
number | Response tokens |
usage.total_tokens |
number | Sum of tokens in the request and response |
Response example
{
"id": "chatcmpl-a1a73c6eb3f180fd",
"object": "chat.completion",
"created": 1777289339,
"model": "bitrix/bitrixgpt-5.5",
"choices": [
{
"index": 0,
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "Quality: HIGH\n\nRationale:\n- Legal entity (LLC) — B2B client\n- Budget 500 thousand per month — above average\n- Specific volume (50 users) — a deliberate need\n\nRecommendation: schedule a call within 24 hours."
}
}
],
"usage": {
"prompt_tokens": 42,
"completion_tokens": 85,
"total_tokens": 127
}
}
Error response example
404 ai_model_not_found — model not found or the key has no access to it:
{
"error": {
"message": "Model \"anthropic/claude-imaginary-x\" not found or disabled.",
"type": "invalid_request_error",
"code": "ai_model_not_found"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_request |
Empty messages array, invalid role, schema violation |
| 400 | invalid_image_payload |
Invalid image_url — the message contains the content element index and the reason. See image analysis |
| 400 | no_default_model |
The portal has no models available to call |
| 402 | ai_credentials_not_configured |
No provider credentials for the model — connect BYOK |
| 402 | insufficient_balance |
Insufficient funds for a paid model |
| 402 | ai_quota_exhausted |
The portal's monthly AI quota is exhausted. The reason field clarifies the cause |
| 402 | cowork_subscription_inactive |
A key carrying the vibe:cowork scope has no active Cowork/Code subscription. The subscription is resumed in the Cowork/Code section of your Vibecode account, and the key itself stays valid |
| 402 | cowork_quota_exhausted |
One of the Cowork/Code subscription quota windows is exhausted. The body carries window (5h/week/month), resetAt and nextTier; the Retry-After header holds the seconds until the window resets |
| 402 | company_budget_exhausted |
The monthly company spend budget set by the portal administrator is exhausted. The scope field names the budget that was hit: USER — the caller's own budget, PORTAL — the budget of the whole portal. The canRequest field tells whether an increase can be requested: true for a personal budget, false for the portal one, which only an administrator raises. The rejection arrives only on calls that draw on the portal balance. Calls inside the tariff quota, on a Cowork/Code subscription and on your own key keep working |
| 403 | scope_missing |
The API key is missing the vibe:ai scope |
| 404 | ai_model_not_found |
Model not found or disabled |
| 422 | structured_output_truncated |
A request with response_format did not produce a complete JSON document: generation was cut off with finish_reason: "length", or the content field is empty with any finish reason and without tool_calls. The json_object mode has an exception — a complete JSON that landed in the internal reasoning_content channel is recovered and the response stays 200. See guaranteed JSON response |
| 429 | rate_limit_exceeded |
The rate limit is exceeded. The X-RateLimit-Scope header indicates the level — per-key or per-user. The same code arrives when the model cluster itself throttled the request: the body then carries the providerStatusCode field, the scope field and the X-RateLimit-Scope header are absent, and the pause comes from Retry-After |
| 429 | ai_congested |
The AI cluster pool is overloaded. The request did not run and nothing was charged — retry it after the delay in the Retry-After header. The response carries the X-AI-Admission: shed header, not X-RateLimit-Scope |
| 429 | ai_pacing_limited |
A daily or weekly quota pacing window is exceeded. This is not quota exhaustion — retry the request after the delay in the Retry-After header. See Pacing (day/week smoothing) |
| 429 | ai_provider_cooldown |
The model cluster is temporarily unavailable and the platform backs off so retries do not pile onto it. The request was not executed and nothing was charged — retry it after the number of seconds in Retry-After. This response carries neither X-RateLimit-Scope nor X-AI-Admission |
| 400 | ai_provider_rejected |
The model rejected the request itself (for example, an unsupported parameter). Retrying it unchanged will not help. Returned when the provider responded with 400 or 422 |
| 502 | ai_provider_unavailable |
The external provider answered with an error (401/403/5xx). The original status arrives in the providerStatusCode field whenever the provider answered with an HTTP status — including a refusal to open the stream after the stream itself has already started. The field is absent where there was no status: the error arrived as a frame from the body of an already open stream (from the body the platform accepts the status 429 alone, and such a frame arrives under rate_limit_exceeded) or the failure happened while processing the provider's response |
| 502 | ai_provider_network |
The platform could not connect to the provider, or the connection dropped before a response. This response carries no providerStatusCode: the provider never answered. Retry the request |
| 503 | model_unavailable |
The model is disabled and no successor is assigned for it. See model lifecycle |
| 503 | pool_exhausted |
The platform is temporarily overloaded. Retry the request after the number of seconds in Retry-After — 3-7 seconds with random jitter |
The full list of common API errors — Errors.
Known specifics
Automatic fallback to a backup model on a provider failure. If a paid model returns a 5xx error or times out, the synchronous request is retried with the default model. The X-Model-Fallback: <original modelId> header appears in the response. In streaming mode there is no such fallback — the client receives an error in the last event before data: [DONE].
An exhausted Cowork/Code quota: the response announces the limit instead of doing the work. This is a separate state, unrelated to X-Model-Fallback above: that header marks a model substitution, whereas here the subscription quota is exhausted. When a quota window is exhausted and a backup model is configured on the platform, the request returns 200, but no work is done for it: the tools, tool_choice and response_format fields are stripped, the request's own system messages have no effect, and the model states that the limit is reached and when it resets. The markers of this state are the X-Cowork-Fallback: true header and the COWORK_QUOTA_FALLBACK warning in the warnings array. In streaming mode there is no warning in the body; the state is visible from the header. Nothing is charged for such a response and no quota is spent. Do not expect tool_calls or schema-shaped JSON here: even with response_format in the request you get plain text. When no backup model is configured — or the configured one fails to answer — 402 cowork_quota_exhausted arrives instead. The response code does not tell those two cases apart, so always handle 402 on this endpoint.
The Bitrix24 account's monthly AI quota. On Bitrix24 accounts with quota control enabled, a request may return 402 ai_quota_exhausted. The reason field distinguishes three cases: breaker — the hourly over-quota spending breaker tripped, wallet_empty — the quota is exhausted and the account balance has no funds, wallet_off — over-quota spend is not available for the account. The resetAt field holds the moment when requests will pass again. For wallet_off it may be absent. In the wallet_empty case the response may additionally carry a hint string and a topupUrl link to top up the balance — both fields appear when enforced quota control and the top-up hint are enabled on the platform, so read them as optional. The hint field in this response is a string. While usage stays within the quota, the endpoint's behavior is unchanged. Beyond the quota, if such spend is allowed for the account, requests are charged to the account's money balance at the model's base catalog price.
The processing budget for a synchronous request is about 850 seconds. Once the budget runs out, 503 ai_provider_timeout is returned. This response deliberately carries no Retry-After header: repeating the same request would hit the same budget. Reduce the request size or switch to streaming, where an idle timeout between events applies instead of a single budget for the whole call.
Request body size limit — 30 MiB. This is enough to pass a single image of up to 20 MiB: after base64 encoding it takes about 27 MiB.
Passing content as an array. For text models, pass content as a string. An array with a single text element also works but is redundant. An array is required only for requests with images.