# 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 the credentials.

**This is an API, not a chat interface.** There is no dialog window for talking to a model in your Vibecode account — you don't need to look for one. The request (prompt) is entered 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` (added automatically to every API key) | **Base URL:** `https://vibecode.bitrix24.com/v1` | **Authorization:** the `X-Api-Key` header or `Authorization: Bearer YOUR_API_KEY`

## 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 Whisper Large v3 Turbo
- [Embeddings](/docs/ai/embeddings) — converting text into vector representations (OpenAI format)
- [Your own keys (BYOK)](/docs/ai/credentials) — connecting your own provider keys
- [Consumption and limits](/docs/ai/consumption) — statistics per key, the company's monthly quota, and off-peak hours
- [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 legacy 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 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 Whisper, 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 "language=en" \
  -F "response_format=json"
```

Response:

```json
{
  "text": "Hello, Acme LLC. We want CRM for 50 users, budget up to 500 thousand 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 thousand 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 thousand 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 thousand 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 Whisper |
| 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"
  }
}
```

The V1 format carries the regular errors of these endpoints themselves. An unexpected server error `5xx` 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 `@` |
| `ai_model_not_found` | 404 | The model was not found or is disabled |
| `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 |
| `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_provider_unavailable` | 502 | The external provider returned `401`/`403`/`5xx` or a network error |
| `ai_provider_timeout` | 503 | The upstream did not respond in time: the Whisper timeout (15 minutes), the non-streaming 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 |
| `model_unavailable` | 503 | The model is disabled and there is no fallback |

> **Whisper / `/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 does not exist or was revoked |
| `RATE_LIMITED` | 429 | The general V1 endpoint request limit 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` |
| `INTERNAL_ERROR` | 500 | Internal platform error. Retry the request, and if the error persists — submit a [ticket](/docs/feedback) |

The full list of common API errors — [Errors](/docs/errors).

---

## Migration from legacy routes

If your code still has calls like `/v1/ai/chat/completions`, `/v1/ai/models`, `/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)
- [Key management](/docs/keys-auth)
- [Timeline comments](/docs/timeline-logs)
- [Bot platform](/docs/bots)
