# AI Router

A single OpenAI-compatible API for working with language models from different providers. Free Bitrix24 models are available right away — for paid models and for your own provider keys (BYOK), connect your credentials.

**This is an API, not a chat interface.** There is no chat window for talking to a model in your Vibecode account — you don't need to look for one. You enter the request (prompt) in your AI tool, agent, or code (Cursor, IDE agent, the `openai` library, your own application), which calls this API at the address below. Free Bitrix24 models work the same way as paid and BYOK models — through the same API.

**Scope:** `vibe:ai` | **Base URL:** `https://vibecode.bitrix24.com/v1` | **Authorization:** the `X-Api-Key` header or `Authorization: Bearer YOUR_API_KEY`

**You can pay with a subscription.** Every router model can be paid for not only from the account wallet but also with the Cowork subscription — including calls from your own agent. Get the subscription key on the [Cowork](/cowork) page; configuring popular agents is covered in [Your own agent on the subscription](/docs/cowork/harness).

## OpenAI SDK compatibility

All responses are returned in the raw OpenAI format. Connect any OpenAI-compatible tool (Cursor, IDE agents, the `openai` library) through the standard settings:

```
Base URL: https://vibecode.bitrix24.com/v1
API Key:  your key vibe_api_... or vibe_app_...
```

By default the SDK passes the key in the `Authorization: Bearer` header — Vibecode accepts both (`X-Api-Key` and `Authorization: Bearer`). The request parameters (`model`, `messages`, `temperature`, `tools`, `stream`, `response_format`) and response fields (`id`, `choices`, `usage`) match the `POST /v1/chat/completions` contract from the OpenAI API.

## Documentation sections

- [Chat completions](/docs/ai/chat) — generating a model response in synchronous or streaming mode (`Server-Sent Events`)
- [Models](/docs/ai/models) — the list of available models and details of a single model
- [Speech recognition](/docs/ai/audio) — converting audio to text via BitrixGPT 5.6 Transcribe

Whisper Large v3 Turbo is being retired. From 16 November 2026, requests with `bitrix/deepdml/faster-whisper-large-v3-turbo-ct2` and requests without `model` are served by BitrixGPT 5.6 Transcribe. The old ID remains accepted and remains the default when `model` is omitted. For new integrations, explicitly set `model=bitrix/bitrixgpt-5.6-transcribe`. Transcription is unavailable on the international platform.
- [Embeddings](/docs/ai/embeddings) — converting text into vector representations (OpenAI format)
- [Your own keys (BYOK)](/docs/ai/credentials) — connecting your own provider keys
- [Usage and limits](/docs/ai/consumption) — statistics per key, the company's monthly quota, and off-peak hours
- [AI request pricing](/docs/ai/pricing) — model prices, what pays for a call, and payment refusals
- [Your own agent on the subscription](/docs/cowork/harness) — connecting a third-party agent (OpenCode, Crush, Cline, Aider) billed against the Cowork subscription instead of the account wallet
- [OpenAI SDK compatibility](#openai-sdk-compatibility) — configuring Cursor, IDE agents, and any OpenAI-compatible clients
- [Model lifecycle](/docs/ai/models/lifecycle) — the behavior of `ACTIVE` / `DEPRECATED` / `DISABLED` and the related headers
- [Migration from legacy routes](#migration-from-legacy-routes) — for projects that still call `/v1/ai/chat/completions` and similar routes

---

## Quick start

### 1. Generate a response with a free model

```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": "user", "content": "What is CRM?"}]
  }'
```

Response:

```json
{
  "id": "chatcmpl-a1a73c6eb3f180fd",
  "object": "chat.completion",
  "created": 1777289339,
  "model": "bitrix/bitrixgpt-5.5",
  "choices": [
    {
      "index": 0,
      "finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "CRM is a customer relationship management system."
      }
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 18,
    "total_tokens": 30
  }
}
```

### 2. View available models

```bash
curl -H "X-Api-Key: YOUR_API_KEY" \
  https://vibecode.bitrix24.com/v1/models
```

Returns only the models that have provider credentials configured in your account. Bitrix24 models (`bitrix/*`) are available to everyone without separate BYOK keys. Most of them are free.

### 3. Check token consumption

```bash
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://vibecode.bitrix24.com/v1/ai/usage?days=7"
```

---

## Common use cases

- **Lead classification:** request leads via [`GET /v1/leads`](/docs/entities/leads/list), run them through a [chat completion](/docs/ai/chat/completions) with `response_format: json_object`, update CRM fields via [`PATCH /v1/leads/:id`](/docs/entities/leads/update).
- **Content generation:** read products via [`GET /v1/products`](/docs/entity-api), generate descriptions, update the product cards in the catalog.
- **Data extraction:** take comments from [`GET /v1/timeline-logs`](/docs/timeline-logs), extract phone / email / company name, write them to CRM.
- **Chatbot:** register a bot via [`POST /v1/bots`](/docs/bots/management/create), receive events via [`GET /v1/bots/:botId/events`](/docs/bots/events), generate an AI response, send it via [`POST /v1/bots/:botId/messages`](/docs/bots/messages).
- **Reports:** collect data via [`POST /v1/batch`](/docs/batch), generate a summary, send a notification.

---

## Full example: call transcription → classification → timeline entry

Scenario: get a call recording, transcribe it via BitrixGPT 5.6 Transcribe, classify the lead quality via the model's `JSON` response, write the result to the deal timeline.

### Step 1. Speech recognition

```bash
curl -X POST https://vibecode.bitrix24.com/v1/audio/transcriptions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -F "file=@call-recording.mp3" \
  -F "model=bitrix/bitrixgpt-5.6-transcribe" \
  -F "response_format=json"
```

Response:

```json
{
  "text": "Hello, this is Acme LLC. We want a CRM for 50 users, budget up to 500,000 per month."
}
```

### Step 2. Lead classification with guaranteed `JSON`

```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": "Classify the lead from the call text. Return JSON: {\"quality\": \"high|medium|low\", \"score\": 0-100, \"reason\": \"...\"}."
      },
      {
        "role": "user",
        "content": "Acme LLC. CRM for 50 users, budget up to 500,000 per month."
      }
    ],
    "response_format": {"type": "json_object"}
  }'
```

Response:

```json
{
  "id": "chatcmpl-acdb112cf9cdf9f9",
  "object": "chat.completion",
  "model": "bitrix/bitrixgpt-5.5",
  "choices": [
    {
      "index": 0,
      "finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "{\"quality\":\"high\",\"score\":88,\"reason\":\"Legal entity, specific volume (50 users) and a budget of 500,000 per month.\"}"
      }
    }
  ],
  "usage": {"prompt_tokens": 92, "completion_tokens": 36, "total_tokens": 128}
}
```

### Step 3. Write the result to the deal timeline

Parse `choices[0].message.content` as `JSON` and send it to the timeline:

```bash
curl -X POST https://vibecode.bitrix24.com/v1/timeline-logs \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entityType": "deal",
    "entityId": 1234,
    "title": "AI classification: high (88/100)",
    "text": "Legal entity, specific volume (50 users) and a budget of 500,000 per month."
  }'
```

All three endpoints work with the same API key and the same authorization — no separate integrations are required.

---

## Endpoint reference

| Method | Path | Description |
|-------|------|----------|
| POST | [`/v1/chat/completions`](/docs/ai/chat/completions) | Generate a model response — synchronously or as a stream |
| GET | [`/v1/models`](/docs/ai/models/list) | List of available models |
| GET | [`/v1/models/:modelId`](/docs/ai/models/get) | Details of a specific model |
| POST | [`/v1/audio/transcriptions`](/docs/ai/audio/transcriptions) | Speech recognition via BitrixGPT 5.6 Transcribe |
| POST | [`/v1/embeddings`](/docs/ai/embeddings) | Vector embeddings of text |
| GET | [`/v1/ai/usage`](/docs/ai/consumption/usage) | AI usage statistics per key |
| GET | [`/v1/ai/quota`](/docs/ai/consumption/quota) | Percentage of the monthly AI quota and a per-model breakdown |
| GET | [`/v1/off-peak`](/docs/ai/consumption/off-peak) | Off-peak hours schedule for the AI quota |
| GET | [`/v1/ai/providers`](/docs/ai/credentials/providers) | List of providers for BYOK |
| GET | [`/v1/ai/credentials`](/docs/ai/credentials/list) | List of your own provider keys |
| POST | [`/v1/ai/credentials`](/docs/ai/credentials/create) | Connect a provider key (with verification) |
| PATCH | [`/v1/ai/credentials/:id`](/docs/ai/credentials/update) | Update a provider key |
| DELETE | [`/v1/ai/credentials/:id`](/docs/ai/credentials/delete) | Delete a provider key |
| POST | [`/v1/ai/credentials/:id/test`](/docs/ai/credentials/test) | Test a provider key |
| GET | [`/v1/ai/credentials/:id/usage`](/docs/ai/credentials/usage) | Usage statistics per key |
| POST | [`/v1/ai/credentials/:id/fetch-models`](/docs/ai/credentials/fetch-models) | Load the model catalog from a Custom provider |
| GET | [`/v1/ai/credentials/:id/models`](/docs/ai/credentials/models-list) | List of models bound to a key |
| POST | [`/v1/ai/credentials/:id/models`](/docs/ai/credentials/models-add) | Add a model to a key manually |
| DELETE | [`/v1/ai/credentials/:credId/models/:modelRowId`](/docs/ai/credentials/models-delete) | Delete a model bound to a key |

---

## Error codes

Endpoints fall into two groups by their error response format:

**Raw OpenAI format** (`/v1/chat/completions`, `/v1/models`, `/v1/audio/transcriptions`):

```json
{
  "error": {
    "message": "...",
    "type": "invalid_request_error",
    "code": "ai_model_not_found"
  }
}
```

`type` takes the following values: `invalid_request_error` (4xx), `insufficient_quota` (402), `service_unavailable` (503), `server_error` (5xx). `code` is always lowercase.

**V1 format** (`/v1/ai/usage`, `/v1/ai/credentials/*`, `/v1/ai/providers`):

```json
{
  "success": false,
  "error": {
    "code": "not_found",
    "message": "Credential not found"
  }
}
```

These endpoints return their own expected errors in the V1 format. An unexpected `5xx` server error on any `/v1/ai/*` route is returned in the raw OpenAI format. It also works the other way round: quota and pacing rejections (`402 ai_quota_exhausted`, `429 ai_pacing_limited`) on the OpenAI-compatible endpoints arrive in the envelope with `success: false`. An error handler must accept both envelopes on either route family.

| Code | HTTP | Description |
|-----|------|----------|
| `scope_missing` | 403 | The API key is missing the `vibe:ai` scope |
| `invalid_request` | 400 | Invalid request parameters |
| `no_default_model` | 400 | The portal has no models available to call |
| `invalid_image_payload` | 400 | Invalid `image_url` in the `content` array |
| `invalid_language` | 400 | The language code does not match `ISO 639` |
| `no_file` | 400 | No audio file was passed |
| `empty_file` | 400 | The file is present in the multipart request, but its body is 0 bytes. The typical cause is `curl -F "file=path"` without `@` |
| `invalid_audio` | 400 | The contents of the `file` field in an audio transcription request are clearly not audio — for example, text, a PDF document, an image or an archive. Rejected before recognition; there is no point in retrying without replacing the file |
| `ai_model_not_found` | 404 | Unknown ID or a model turned off in the catalog |
| `not_found` | 404 | The entity (BYOK key, model) was not found |
| `provider_not_found` | 404 | The provider was not found or is disabled |
| `ai_credentials_not_configured` | 402 | The model has no provider credentials — connect `BYOK` |
| `insufficient_balance` | 402 | Insufficient funds for a paid model |
| `credential_invalid` | 422 | The provider key failed verification |
| `already_exists` | 409 | A key for this provider already exists |
| `agent_turn_loop_detected` | 409 | An agent turn on `POST /v1/chat/completions` is stuck in a loop: 20 or more model answers without `tool_calls` have accumulated after the last message with the `user` role. The request is not sent to the model and nothing is charged. To continue, add a new message with the `user` role |
| `agent_turn_awaiting_confirmation` | 409 | The AI agent is waiting for the user to confirm or cancel an operation: until the user answers, its key's requests to `POST /v1/chat/completions` are refused. The request is not sent to the model and nothing is charged |
| `base_url_invalid` | 400 | The Custom provider's `baseUrl` must use `http` or `https` |
| `base_url_private` | 400 | The `baseUrl` points to a private network or does not resolve to an IP address via `DNS` |
| `not_custom_provider` | 400 | Manual model registration is allowed only for a Custom provider |
| `provider_list_models_unavailable` | 200 | The Custom provider does not support `GET /v1/models` — add models manually |
| `ai_provider_rejected` | 400 | The provider rejected the request itself (responded `400` or `422`). Retrying it unchanged will not help |
| `ai_payload_too_large` | 413 | The request was rejected because of its size. In chat with a `bitrix/*` model, the body exceeds the model cluster limit, and the message states the limit in effect. A body with images did not fit even after they were downscaled: downscale the images — [image analysis](/docs/ai/chat/vision). A body without images: shorten the message history. When the provider itself rejected the request because of its size — on any AI method, models on your own key included — the response carries `providerStatusCode: 413`. Make the request smaller: send fewer texts or split the file into parts |
| `ai_image_resize_busy` | 429 | The platform is already downscaling many images at once. Retry after the delay given in the `Retry-After` header — [image analysis](/docs/ai/chat/vision) |
| `ai_provider_unavailable` | 502 | 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 when the stream to the client is already open and the provider refused to establish the upstream one. The field is absent where there was no status: the error arrived as a frame from the body of an already open stream (the platform accepts only the `429` status from the body, and such a frame arrives as `rate_limit_exceeded`) or the failure happened while processing the provider's response |
| `ai_provider_network` | 502 | The platform could not connect to the provider, or the connection dropped before a response (chat, embeddings, speech recognition). This response carries no `providerStatusCode`: the provider never answered. Retry the request |
| `ai_provider_timeout` | 503 | The upstream did not respond in time: the transcription timeout (15 minutes), the non-streaming request budget, or a provider `408` |
| `ai_congested` | 429 | The AI cluster pool is overloaded. The request was not executed and there is no charge — retry it after the delay given in the `Retry-After` header |
| `ai_provider_cooldown` | 429 | The model cluster is temporarily unavailable and the platform pauses briefly. The request was not executed and there is no charge — retry it after the delay given in the `Retry-After` header. This response carries neither `X-RateLimit-Scope` nor `X-AI-Admission` |
| `model_unavailable` | 503 | A model with status `DISABLED` has no available replacement; type `service_unavailable`. Before recognition, without a charge |

> **Speech recognition / `/v1/audio/transcriptions`:** the processing window is up to 15 minutes. Long / noisy audio may take several minutes. For audio longer than ~30 min, consider splitting it into parts.

### System errors

Applicable to any Vibecode API endpoint, including the AI Router section:

| Code | HTTP | Description |
|-----|------|----------|
| `MISSING_API_KEY` | 401 | Neither the `X-Api-Key` header nor `Authorization: Bearer` was passed |
| `INVALID_API_KEY` | 401 | The key is not recognized — no such key exists on the platform |
| `RATE_LIMITED` | 429 | The general request limit for V1 endpoints outside the AI Router was exceeded. The pause before retrying is in `Retry-After` |
| `rate_limit_exceeded` | 429 | The per-minute AI Router limit for the API key or user was exceeded. The scope is in `error.scope` and the `X-RateLimit-Scope` header; the pause before retrying is in `Retry-After`. 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` |
| `INTERNAL_ERROR` | 500 | Internal platform error. Retry the request, and if the error persists, submit a [ticket](/docs/feedback) |

For the full list of common API errors, see [Errors](/docs/errors).

---

## Migration from legacy routes

If your code still calls routes like `/v1/ai/chat/completions`, `/v1/ai/models`, or `/v1/ai/audio/transcriptions`, they keep working in backward-compatibility mode. The response headers carry:

```
Deprecation: true
X-Deprecated-Use: /v1/chat/completions
```

`X-Deprecated-Use` indicates the canonical path. Move the calls to the canonical routes — this removes the `Deprecation` header and eliminates the risk of the old path being removed in the future.

| Legacy path | Canonical path |
|-----------------|-------------------|
| `POST /v1/ai/chat/completions` | [`POST /v1/chat/completions`](/docs/ai/chat/completions) |
| `GET /v1/ai/models` | [`GET /v1/models`](/docs/ai/models/list) |
| `GET /v1/ai/models/:modelId` | [`GET /v1/models/:modelId`](/docs/ai/models/get) |
| `POST /v1/ai/audio/transcriptions` | [`POST /v1/audio/transcriptions`](/docs/ai/audio/transcriptions) |

The routes `/v1/ai/usage`, `/v1/ai/credentials/*`, `/v1/ai/providers` have no legacy equivalent — they are canonical paths.

---

## See also

- [Limits and optimization](/docs/optimization)
- [Keys and authorization](/docs/keys-auth)
- [CRM Timeline](/docs/timeline-logs)
- [Bot platform](/docs/bots)
