## Transcribe audio

`POST /v1/audio/transcriptions`

Converts an audio file to text via BitrixGPT 5.6 Transcribe (see "Models" below) — no BYOK required. Transcription counts toward the Bitrix24 account's AI quota by audio duration: within the plan's quota nothing is charged to the balance; above the quota, usage is charged to the account's money balance at the model's base price. Accepts the file via `multipart/form-data`. The request and response format is compatible with `POST /v1/audio/transcriptions` from the OpenAI API. Transcription is not available on the international platform yet: every request, including the examples below, is rejected with `404 ai_model_not_found`.

> **The response comes in raw OpenAI format.**
>
> The `{success, data}` wrapper used by the other Vibecode endpoints — `/v1/deals`, `/v1/tasks` and others — is not returned 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.

## Models

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

When the replacement handles a request, the response carries `X-Model-Fallback: bitrix/deepdml/faster-whisper-large-v3-turbo-ct2`, `X-Model-Replacement: bitrix/bitrixgpt-5.6-transcribe` and `X-Model-Fallback-Reason: model_disabled`. The replacement's language support, parameter handling and billing apply. See [model lifecycle](/docs/ai/models/lifecycle).

The `X-Model-Fallback`, `X-Model-Replacement` and `X-Model-Fallback-Reason` headers are not on the CORS exposure list, so a browser `fetch` cannot read them — read them server-side or in the developer tools.

| | `bitrix/deepdml/faster-whisper-large-v3-turbo-ct2` | `bitrix/bitrixgpt-5.6-transcribe` |
|---|---|---|
| Display name | Whisper Large v3 Turbo (being retired; replacement from 16 November 2026) — not offered on the international platform yet: transcription is not available there, so every request, including one without `model`, is rejected with `404 ai_model_not_found` | BitrixGPT 5.6 Transcribe — not offered on the international platform yet; requests with this ID are rejected there |
| Languages | about 99 | 25 European: bg, hr, cs, da, nl, en, et, fi, fr, de, el, hu, it, lv, lt, mt, pl, pt, ro, sk, sl, es, sv, ru, uk |
| Fields that affect recognition | `language`, `prompt`, `hotwords`, `temperature`, `vad_filter` | none of these fields — they are accepted and validated, but the model does not read them; their names come back in the `X-Ignored-Params` response header |
| `timestamp_granularities[]` | `word`, `segment` | `word`; `segment` is silently dropped |
| `text`, `srt`, `vtt` formats | produced by the model; billed per call | built by the platform from `verbose_json`; all formats are billed by audio duration |
| Speaker diarization | none | none |

The `X-Ignored-Params` header is informational: the response body and error codes do not change. It is not on the CORS exposure list, so a browser `fetch` cannot read it — read it server-side or in the developer tools.

## Request fields (form-data)

| Field | Type | Required | Default | Description |
|------|-----|:-----:|-----------|----------|
| `file` | file | yes | — | Audio file. The multipart part must be named exactly `file`. A missing `filename` parameter and an empty `filename=""` value are both accepted and replaced with the extensionless name `audio` — the same way for both `Content-Type: audio/mpeg` and `Content-Type: application/octet-stream`. The filename extension is NOT checked: the recognition service detects the container from the content, so a name without an extension and rare voice-recorder formats are accepted just like `mp3`, `mpeg`, `mpga`, `mp4`, `m4a`, `wav`, `ogg`, `oga`, `flac`, `webm`, `opus`, `aac`, `amr`, `3gp`, `3gpp`, `wma` — these are listed because we pass the recognition service a format hint for them when the request carries none. Content that is clearly not audio — for example text, a PDF document, an image or an archive — is rejected with `400 invalid_audio` before recognition, whatever the filename and `Content-Type`. A file in an audio format that the recognition service still could not read comes back as `400 ai_provider_rejected` when the service answered with status 400 or 422, and as `502 ai_provider_unavailable` when the service answered with an internal error. Maximum size — 25 MB |
| `model` | string | no | `deepdml/faster-whisper-large-v3-turbo-ct2` | Set `bitrix/bitrixgpt-5.6-transcribe` for new integrations. The `bitrix/` prefix is optional. The default ID is unchanged; from 16 November 2026 it is served by Transcribe, as is an explicit Whisper ID. An unknown ID or a model turned off in the catalog returns `404 ai_model_not_found`. A model with status `DISABLED` is served by an available replacement; without one, it returns `503 model_unavailable` (type `service_unavailable`). Both errors are returned before recognition, without a charge. Transcription is unavailable on the international platform, including when `model` is omitted |
| `language` | string | no | auto-detection | Language code per `ISO 639` (2-3 letters): `en`, `de`, `fr`, `ru`, `zh`, etc. Whisper uses the specified language; Transcribe detects it automatically. For Transcribe: accepted and validated, ignored during recognition; reported in `X-Ignored-Params`. |
| `prompt` | string | no | — | Context hint: the conversation topic, style, correct spelling of terms. Up to 2000 characters; Whisper uses only the last ~224 tokens. For Transcribe: accepted and validated, ignored during recognition; reported in `X-Ignored-Params`. |
| `hotwords` | string | no | — | Comma-separated special words and terms that improve Whisper's recognition accuracy for rare names and brands. Up to 500 characters. For Transcribe: accepted and validated, ignored during recognition; reported in `X-Ignored-Params`. |
| `temperature` | number | no | `0` | Whisper decoder temperature from `0` to `1`. `0` gives a deterministic result, higher values add variability. Out-of-range values are rejected. For Transcribe: accepted and validated, ignored during recognition; reported in `X-Ignored-Params`. |
| `vad_filter` | boolean | no | — | `true` enables the VAD filter: Whisper cuts out silence before recognition — fewer hallucinations on recordings with pauses. For Transcribe: accepted and validated, ignored during recognition; reported in `X-Ignored-Params`. |
| `timestamp_granularities[]` | string | no | `segment` | Timestamp granularity: `word` or `segment` (the field can be repeated). Only with `response_format=verbose_json`. With `word`, each segment is augmented with a `words` array carrying the timing and probability of each word. With Transcribe, `segment` is silently dropped; `word` works. |
| `response_format` | string | no | `json` | Result format: `json`, `text`, `srt`, `vtt`, `verbose_json` |

## Examples

### curl — personal key

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

### curl — OAuth application

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

### JavaScript — personal key

```javascript
const formData = new FormData()
formData.append('file', audioFile)  // File or Blob object
formData.append('model', 'bitrix/bitrixgpt-5.6-transcribe')
formData.append('response_format', 'json')

const res = await fetch('https://vibecode.bitrix24.com/v1/audio/transcriptions', {
  method: 'POST',
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
  body: formData,
})

const result = await res.json()
console.log('Recognized text:', result.text)
```

### JavaScript — OAuth application

```javascript
const formData = new FormData()
formData.append('file', audioFile)
formData.append('model', 'bitrix/bitrixgpt-5.6-transcribe')

const res = await fetch('https://vibecode.bitrix24.com/v1/audio/transcriptions', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
  body: formData,
})

const result = await res.json()
console.log('Text:', result.text)
```

## Response fields

The response structure depends on `response_format`. The `json` format (default) is the most compact; `verbose_json` adds timings.

### `response_format: json`

| Field | Type | Description |
|------|-----|----------|
| `text` | string | The full recognized text |

### `response_format: text`

The response is a plain string with the recognized text, without a `JSON` wrapper.

### `response_format: verbose_json`

| Field | Type | Description |
|------|-----|----------|
| `task` | string | Task type: `transcribe` |
| `language` | string | Code of the detected or specified language |
| `duration` | number | Audio duration in seconds |
| `text` | string | The full recognized text |
| `usage` | object \| null | Model usage metadata. `null` if the model does not return it |
| `words` | array | Word-level timings for the whole response. Empty array if word-level timings were not requested |
| `segments` | array | Segments with timings and recognition metadata |
| `segments[].id` | number | Segment index |
| `segments[].seek` | number | Whisper's internal recognition-window offset |
| `segments[].start` | number | Segment start in seconds |
| `segments[].end` | number | Segment end in seconds |
| `segments[].text` | string | Segment text |
| `segments[].tokens` | array | Model tokens for the segment text (integers) |
| `segments[].temperature` | number | Decoding temperature applied by the model |
| `segments[].avg_logprob` | number | Average log probability of the segment tokens — a confidence metric |
| `segments[].compression_ratio` | number | Compression ratio of the segment text |
| `segments[].no_speech_prob` | number | Probability that the segment contains no speech |
| `segments[].words` | array | Word-level timings inside the segment. Empty array if not requested |
| `segments[].emotion` | string \| null | Detected emotion of the segment. `null` if not detected |

### `response_format: srt` / `vtt`

The response is subtitles in `SubRip Text` or `WebVTT` format.

## Response example

`response_format: json`:

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

`response_format: verbose_json`:

```json
{
  "task": "transcribe",
  "language": "en",
  "duration": 8.42,
  "text": "Hello, this is Acme LLC. We want a CRM for 50 users, budget up to 500,000 per month.",
  "usage": null,
  "words": [],
  "segments": [
    {
      "id": 0,
      "seek": 0,
      "start": 0.0,
      "end": 3.2,
      "text": "Hello, this is Acme LLC.",
      "tokens": [50365, 2425, 11, 341, 307],
      "temperature": 0.0,
      "avg_logprob": -0.38,
      "compression_ratio": 1.12,
      "no_speech_prob": 0.02,
      "words": [],
      "emotion": null
    },
    {
      "id": 1,
      "seek": 320,
      "start": 3.2,
      "end": 8.42,
      "text": "We want a CRM for 50 users, budget up to 500,000 per month.",
      "tokens": [50414, 1003, 13767, 295, 1500],
      "temperature": 0.0,
      "avg_logprob": -0.41,
      "compression_ratio": 1.20,
      "no_speech_prob": 0.01,
      "words": [],
      "emotion": null
    }
  ]
}
```

## Error response example

`400 no_file` — the `file` field was not provided:

```json
{
  "error": {
    "message": "Audio file is required. Send as multipart/form-data with field \"file\".",
    "type": "invalid_request_error",
    "code": "no_file"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `no_file` | The `file` field was not provided in `multipart/form-data`, or the file part is named something else (for example `audio`). The size of such a part does not affect the response: the name is read from the part header, before the contents |
| 400 | `empty_file` | The `file` field was provided, but the file is empty (0 bytes). Common cause: `curl -F "file=path"` without the `@` prefix — curl sends the path string instead of the file contents |
| 400 | `invalid_prompt` | The `prompt` field is longer than 2000 characters |
| 400 | `invalid_hotwords` | The `hotwords` field is longer than 500 characters |
| 400 | `invalid_temperature` | The `temperature` field is not a number or is outside the 0..1 range |
| 400 | `invalid_vad_filter` | The `vad_filter` field is neither `true` nor `false` |
| 400 | `invalid_timestamp_granularities` | The value is not `word`/`segment`, or the response format is not `verbose_json` |
| 400 | `invalid_language` | The language code does not match the `ISO 639` format (2-3 letters) |
| 400 | `invalid_idempotency_key` | The `Idempotency-Key` header fails validation: its length is outside 1–255 characters or it contains a character outside `[A-Za-z0-9_.:-]`. Nothing is transcribed or charged. The `param` field of the response is `Idempotency-Key` |
| 400 | `invalid_audio` | The contents of the `file` field are clearly not audio — for example text, a PDF document, an image or an archive. The check reads the contents before recognition; the filename and `Content-Type` do not affect it, and nothing is charged. The `param` field of the response is `file`. There is no point in retrying without replacing the file |
| 400 | `ai_provider_rejected` | The recognition service rejected the request contents themselves (its response was 400 or 422). The `providerStatusCode` field carries the original status. There is no point in retrying the request unchanged |
| 402 | `ai_credentials_not_configured` | The `bitrix` provider is not configured on the platform |
| 402 | `insufficient_balance` | The PREPAY account is past its overdraft limit — checked BEFORE the recognition call (PLATFORM/PORTAL keys only, BYOK is free and not checked) |
| 402 | `account_frozen` | The billing account is frozen due to a debt and the transcription goes beyond the plan's monthly AI quota — spend above the quota is paid from the balance. Transcription within the quota, with a Cowork subscription key, and with your own provider key keeps working under the freeze. Until the narrowed refusal is rolled out to the portal, the freeze blocks transcription entirely and the endpoint responds with the V1 envelope carrying the code `ACCOUNT_FROZEN` in upper case — [what the refusal covers](/docs/errors) |
| 402 | `ai_quota_exhausted` | The portal's monthly AI quota is exhausted — see "Known specifics" below |
| 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 can raise. The rejection arrives only on calls that draw on the portal balance. Transcription inside the plan quota, with a Cowork subscription key and with your own provider key (BYOK) keeps working |
| 403 | `scope_missing` | The API key is missing the `vibe:ai` scope |
| 404 | `ai_model_not_found` | Unknown ID or a model turned off in the catalog. On the international platform transcription is unavailable and every request returns this error. Checked before recognition, without a charge |
| 409 | `idempotency_in_progress` | The first request with this `Idempotency-Key` and this file is still being transcribed. Nothing is charged. Repeat with the same key after the number of seconds in `Retry-After` — at most 10 |
| 422 | `idempotency_key_reused` | This `Idempotency-Key` has already been used with a different file within the last 72 hours. Nothing is transcribed or charged — new audio needs a new key. The `param` field of the response is `Idempotency-Key` |
| 503 | `model_unavailable` | A model with status `DISABLED` has no available replacement. The error type is `service_unavailable`. Checked before recognition, without a charge. When a replacement is available, it serves the request |
| 413 | `request_too_large` | One of the multipart limits was breached: the file is over 25 MB, or the request carries more than 16 text fields or 24 parts, or a single field is over 64 KB |
| 413 | `ai_payload_too_large` | The recognition service rejected the request by size. The `providerStatusCode` field carries the original status `413`. Retrying the same request returns the same response — split the file into parts |
| 502 | `ai_provider_unavailable` | The recognition service returned an authentication or internal error, including on a damaged audio file with a valid format header. The original status arrives in the `providerStatusCode` field when the service answered with an HTTP status. A rejection of the request contents arrives under its own `ai_provider_rejected` code |
| 502 | `ai_provider_network` | The platform could not connect to the recognition service, or the connection dropped before a response. This response carries no `providerStatusCode`: the service never answered. Retry the request |
| 503 | `ai_provider_timeout` | The recognition service did not respond within 15 minutes — the file is too long or the service is overloaded. For recordings longer than ~30 minutes, split them into parts |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not provided |
| 429 | `ai_provider_cooldown` | The transcription cluster is temporarily unavailable and the platform pauses 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` |
| 429 | `ai_congested` | The AI cluster pool is overloaded. The request was not executed and nothing was charged — retry it per 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 pacing window is exceeded. This is not quota exhaustion — repeat the request after the time in the `Retry-After` header. See [Pacing (day/week smoothing)](/docs/ai/consumption/quota#pacing-day-week-smoothing) |

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

## Idempotency

Send the optional `Idempotency-Key` header to repeat the transcription of the same file safely. If the response to the first request was lost (a dropped connection, a timeout), a repeat with the same key and the same file transcribes the audio again but charges nothing a second time: neither the Bitrix24 account's AI quota, nor money from the balance, nor the Cowork subscription quota. The response of such a repeat carries the `Idempotent-Replayed: true` header.

- The key is a string of 1–255 characters from `[A-Za-z0-9_.:-]`. For a recording you split into parts, use the form `<recording id>:<track id>:<part number>`, for example `rec-8f2c:mic:12`.
- The key is scoped to the Bitrix24 account and the user the call is billed to, or to the API key when there is no user. A new API key of the same user does not break the repeat.
- A repeat is free for 72 hours after a successful charged call. Later, the same key is processed as a new call and charged.
- A free repeat goes through even when the quota is exhausted: this file has already been paid for.
- At most three free repeats are allowed per file. The fourth and later repeats with the same key are processed and charged as a regular call, with all quota and balance checks.
- If the first call ended with an error, the key is released, and a repeat is processed as a new charged call.
- If the first call charged nothing — for example, it ran on your own provider key — a repeat is also processed as a regular call, without the `Idempotent-Replayed` header.
- The platform stores neither the transcript nor the audio — only fingerprints of the key and the file. So the response to a repeat is a fresh transcription of the same file.
- Without the `Idempotency-Key` header the behaviour is unchanged.

Refusals related to the key — `400 invalid_idempotency_key`, `409 idempotency_in_progress` and `422 idempotency_key_reused` — are described in [Errors](#errors).

## Known specifics

**Transcribe detects the language automatically.** It supports the 25 European languages listed above, compared with about 99 for Whisper. It does not separate speakers. `language`, `prompt`, `hotwords`, `temperature` and `vad_filter` are validated but do not affect recognition; sent field names are reported in `X-Ignored-Params`. Hints that influenced Whisper no longer do so when Transcribe serves the request. Check your recordings and required languages before migrating.

**Within the quota — no charges, above the quota — the model's base price.** BitrixGPT 5.6 Transcribe runs on Bitrix24 infrastructure. All its formats, including platform-rendered `text`, `srt` and `vtt`, are priced by audio duration and count toward the Bitrix24 account's monthly AI quota. As long as usage stays within the plan's quota, nothing is charged to the account balance. Above the quota, usage is charged to the account's money balance at the model's base catalog price. On accounts with quota control enabled, a request may return `402 ai_quota_exhausted` when the monthly limit is exhausted. The `reason` field distinguishes three cases: `breaker` — the hourly over-quota spending breaker fired, `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 is the moment when requests will start passing 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 treat them as optional. The `hint` field in this response is a string. When the Bitrix24 account has no paid AI access, a `reason: "wallet_off"` response additionally carries `details.learnMoreUrl` — a page that describes the paid Bitrix24 plan that includes AI — and the `message` field names that plan. For other `wallet_off` causes `details.learnMoreUrl` is absent.

**A Cowork subscription key is metered separately.** A call made with a key carrying the `vibe:cowork` scope does not count toward the account's AI quota. If the subscription is active and a platform administrator has set a per-minute or per-call price for a transcription model available to you, the call draws on the subscription quota: when the window is exhausted the endpoint responds with `402` and the code `cowork_quota_exhausted`, a `Retry-After` header and the fields `window` (`5h`, `week` or `month`), `resetAt` and `nextTier`. Until a price is set, transcription with such a key is not metered. With Whisper the `text`, `srt` and `vtt` formats do not report a duration, so they are billed at the per-call price; with BitrixGPT 5.6 Transcribe they are built by the platform and billed by duration.

Disabling Cowork at the platform level controls product features, but does not change AI billing for an already-issued key. With an active subscription, transcription continues to use its quota, just like chat; disabling the product does not move this usage to the account wallet. Without an active subscription, transcription still does not use the subscription quota, and standard billing applies as before.

**Size limit — 25 MB.** A file over the limit is rejected outright — `413 request_too_large`, nothing is charged. For long recordings, split the file into parts of up to 25 MB and stitch the results on the client side. One hour of `mp3` at 128 kbit/s is about 60 MB — you will have to split it.

**Request timeout — 15 minutes.** Transcription takes roughly 5-15% of the audio length. A 5-minute file (5-7 MB `mp3`) transcribes in 15-45 seconds. On a timeout, `503 ai_provider_timeout` is returned.

## See also

- [Speech recognition](/docs/ai/audio)
- [Create a chat completion](/docs/ai/chat/completions)
- [AI Router](/docs/ai)
