
## 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](./streaming.md), [guaranteed JSON response](./json.md), [function calling](./tools.md), [image analysis](./vision.md) and [rate limits](./rate-limits.md).

## 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`](/docs/ai/models/list) |
| `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](./vision.md) — 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` |
| `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](./streaming.md) of `Server-Sent Events` |
| `response_format` | object | no | — | Control the [response format](./json.md): `{"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](./tools.md) 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) |

## 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](/docs/ai/models/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

```bash
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

```bash
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 the completion's creation |
| `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 variants. Without the `n` parameter the array has a single element |
| `choices[].index` | number | Variant ordinal number |
| `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`. The field is absent when there are no warnings |
| `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](./vision.md) |
| 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](/docs/ai/credentials/create) |
| 402 | `insufficient_balance` | Insufficient funds for a paid model |
| 402 | `ai_quota_exhausted` | The portal's monthly [AI quota](/docs/ai/consumption/quota) is exhausted. The `reason` field clarifies the cause |
| 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: 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 service `reasoning_content` channel is recovered and the response stays `200`. See [guaranteed JSON response](./json.md) |
| 429 | `rate_limit_exceeded` | The [rate limit](./rate-limits.md) is exceeded. The `X-RateLimit-Scope` header indicates the level — `per-key` or `per-user` |
| 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)](/docs/ai/consumption/quota#pacing-day-week-smoothing) |
| 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 returned an error (`401`/`403`/`5xx`) or is unavailable |
| 503 | `model_unavailable` | The model is disabled and no successor is assigned for it. See [model lifecycle](/docs/ai/models/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](/docs/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]`.

**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](./streaming.md) — there 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

- [Streaming](./streaming.md)
- [Guaranteed JSON response](./json.md)
- [Function calling](./tools.md)
- [Image analysis](./vision.md)
- [Rate limits](./rate-limits.md)
- [Model lifecycle](/docs/ai/models/lifecycle)
- [List of models](/docs/ai/models/list)
- [AI Router](/docs/ai)
