For AI agents: markdown of this page — /docs-content-en/ai/chat.md documentation index — /llms.txt

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/tasks and 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. enabledfalse disables reasoning. excludetrue 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: minimallow, xhighmax. reasoning.enabled: true or chat_template_kwargs.thinking: true without a level gives medium. reasoning.max_tokens without effortmedium 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

Terminal
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

Terminal
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

javascript
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

javascript
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

JSON
{
  "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:

JSON
{
  "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.

See also