# API changes: August 14, 2026

[← Changelog](/docs/changelog) · [August 2026](/docs/changelog/2026-08)

### NEW-0814-1: thirty-one more live operations are now in the machine schema

Thirty-one V1 operations that were already live and documented are now included in `GET /v1/openapi.json`: smart-process custom fields, requisite links, CRM card configuration, configurable activities, mail, task time tracking, server icon and unstick operations, bot transfer and human-resources node search, plus app blueprints. The methods and their responses did not change — only the machine-readable descriptions were added for clients and agents that build integrations from the schema.

### BC-0814-2: port pinning refuses instead of confirming falsely

> Old format supported until: not provided

**Before**

On a pinned server [PATCH /v1/infra/servers/:id/port](/docs/infra/deploy/port) wrote the requested port into the agent settings without checking whether any process on the machine was listening on it. The public address could stop answering entirely after such a change, and the only way back was the non-obvious `port: 0`. A `verified: true` reply meant no more than that the agent had come back online: an agent that came up with automatic port detection, that is without the pin in force, produced exactly the same confirmation. Two calls in a row could leave the agent on the port of the other request, and both answered with success.

**After**

A port nothing listens on is refused with `409 PORT_NOT_APPLIED` before the settings are rewritten; the message lists the ports the agent sees listening. Entry NEW-0812-3 stated that a pinned server never receives this code — it does, in exactly this case and without the `agentError` field.

The confirmation is stricter: `verified: true` now means that the agent settings carry the requested port, that the agent really did restart, and that it came up with the pin in force rather than with automatic detection. The reply additionally carries `data.pinned` — whether the machine is still pinned after the call.

While a port change is still in progress, a second call for the same server receives `409 SERVER_BUSY`. The endpoint itself now has a rate limit of 10 requests per minute.

**What integrators should do**

Start the process on the target port BEFORE changing the port: the order "pin the port first, launch the application on it afterwards" now answers with a refusal rather than a success. Do not send a port change in parallel with a deployment or with another port change of the same server — wait for the previous call to answer. Stay within 10 requests per minute.

### FIX-0814-3: the dimensions parameter for the bitrix/embeddings model

**Before**

The `dimensions` parameter was declared in the `POST /v1/embeddings` schema, but any request carrying it for the `bitrix/embeddings` model received `400 ai_provider_rejected` — whatever the value, including the dimensionality the model already returns.

**After**

For `bitrix/embeddings` the parameter works: an integer from 32 to 4096 is accepted, and the response carries a vector of that dimensionality re-normalised to unit length. A value outside the range is rejected with `400 invalid_request` and a `param` field. Requests without the parameter are unchanged: the full dimensionality is 4096.

A smaller dimensionality neither reduces input-token usage nor speeds up processing — the saving is on the integrator's side, in index size and search speed. Vectors of different dimensionality must not be mixed in one similarity index. Details — [Create embeddings](/docs/ai/embeddings).

### FIX-0814-4: leading service markers are no longer included in model content

**Before**

After a tool call, the final answer from [POST /v1/chat/completions](/docs/ai/chat/completions) sometimes started with service markers before the text. A `response_format` request whose answer was only those markers returned `200` and a non-empty `content`.

**After**

Leading service markers are removed from `content`. If no text remains, `content` is `null` — the same as a textless answer. For a `response_format` request this is the already documented empty-`content` case without `tool_calls`: HTTP `422` and code `structured_output_truncated`. If text remains after the markers are removed, the response stays `200` with the cleaned `content`.

**Impact on integrators**

In the common case no client change is required: the answer is the same minus the prefix, and stripping the prefix on your side stays safe. The change does affect you if you relied on a `200` for a `response_format` request whose answer carried no text: that response now comes back with `422` and `structured_output_truncated`, as described on the method page. The markers are removed in streaming mode as well. The `usage` field is not recalculated.

### FIX-0814-5: the blocking server wake returns WAKE_TIMEOUT far less often on a cold start

**Before**

The blocking wake — [POST /v1/infra/servers/:id/wake](/docs/infra/lifecycle/wake) with `?wait=true`, and the automatic wake of a sleeping server on [POST /v1/infra/servers/:id/deploy](/docs/infra/deploy) — returned `503 WAKE_TIMEOUT` in two cases that had nothing to do with how long it waited. When the cloud lost the start command, the platform never re-issued it and eventually reported that the machine had not come up. And when the agent connected its tunnel before the platform refreshed the server status, readiness was still not recognised, so a machine that was already up was put back to sleep.

**After**

While waiting for readiness, the platform now re-checks the machine state with the cloud and re-issues the start command if it never landed. A connected tunnel counts as proof of readiness on its own: the server moves to `running` and the response returns without waiting for the next status refresh. Error codes, the response shape and the wait window (~6.5 minutes, then `503 WAKE_TIMEOUT` and the server returns to `sleeping`) are unchanged, so clients need to change nothing. A machine that genuinely did not come up still reports `WAKE_TIMEOUT` honestly.

### FIX-0814-6: a fractional or malformed path id no longer returns a DIFFERENT record

**Before**

`GET /v1/tasks/1.5` answered `200` and returned the record with id `1`: the non-integer value was forwarded to Bitrix24 as-is, Bitrix24 truncated it, and the client got a DIFFERENT real record instead of an error. Update and delete behaved the same way — a write silently landed on the wrong record. This affected entities with an integer id; the wrong-record substitution was observed live on twelve of them (tasks, workgroups, users, departments, statuses, storages, sites, pages, folders, timelines, Open Channels configs, requisite presets), while for the rest Bitrix24 rejected the request itself. Some entities (deals, for example) already answered `400`, so the behaviour differed inside one API.

**After**

A non-integer id is rejected with `400 INVALID_PARAMS` before the Bitrix24 call — consistently on read, update and delete, in single requests and in batches (`/v1/batch`, `/v1/<entity>/batch`). The rule applies to every entity with a numeric id. Integer values work as before. Entities whose id is not a number (order statuses `N`/`P`/`F`, currencies, business-process codes) and chats, which accept the `chat1` form, keep their previous behaviour.

### NEW-0814-7: reading the notification feed and the unread counter

[GET /v1/notifications](/docs/notifications/list) is now available — it reads a user's notification feed together with the unread counter. Until now the Vibecode API could only send notifications, mark them read and delete them, so an inbox application had to keep a second, separate Bitrix24 integration just to read them.

The response carries the notification list, the cards of their authors, the total counter, the unread counter and a `hasMore` flag. Page size comes from `limit` (1 to 50, 50 by default); a value outside the range is brought to the nearest bound, and the size actually used is always visible in `meta.appliedLimit`. Paging walks the Bitrix24 cursor: `lastId` and `lastType` are sent together. To read the unread counter without pulling a page, call with `limit=1`.

The feed belongs to the token owner — the operation has no parameter selecting whose inbox to read, so a personal key returns its own owner's feed, while a given employee's feed requires an OAuth application key with an `Authorization: Bearer` header. The feed is not filtered by application: it also carries other applications' notifications and the Bitrix24 account's own system ones, so pick yours by `notifyTag` or `notifyModule`.

### NEW-0814-8: Workday history in the V1 API

A new endpoint is available — [GET /v1/workday/records](/docs/workday/records) returns one employee's workday history over a period. It reports the start and end of the day, seconds worked, break length and the approval flag, so lateness and overtime reports can be built through the Vibecode API without a separate integration with the account's time tracking.

`userId` is mandatory: the Bitrix24 account refuses the call without it. The period is set by the optional `from` and `to` in ISO-8601 with an explicit timezone offset or `Z`; with no period given, the last 7 days are returned. A bare date is rejected — a workday boundary depends on the timezone, and silently widening it to UTC would reclassify the very lateness the endpoint exists to report.

A single request returns at most 50 records; page deeper with `offset` or `page`. `meta.hasMore` signals a continuation. The `meta.total` field is present only when the page came back shorter than the requested `limit`: the size of the selection is known exactly in that case, whereas on a full page it is not, and no invented number is put there.

The scope is unchanged — `timeman`. Rights to read another employee's records are decided by the Bitrix24 account: they belong to an administrator or the employee's direct manager.

### FIX-0814-9: the INT_VIBE_PLUS_REQUIRED refusal now points at the plan page inside the account

**Before**

For the `INT_VIBE_PLUS_REQUIRED` code, `details.upgradeUrl` and `alternatives[0].url` carried the generic Bitrix24 pricing page. A Vibe+ plan is enabled inside the account itself, so that page did not show what to actually do.

**After**

Both fields now carry an address on the customer account that opens the explanation for the required plan: `https://<account domain>/online/?feature_promoter=limit_why_pay_tariff_vibe`. The `servers.create` slot of [GET /v1/me](/docs/keys-auth/me) returns the same address — it used to disagree with the refusal body.

When the account domain cannot be recognised, both fields still carry the generic pricing page: no broken address is ever returned.

**Impact on integrators**

Nothing to change. The response shape is unchanged and both fields remain address strings. A client that sent the user to `details.upgradeUrl` now lands them on the plan they need instead of a generic price list. The refusal code, the field set and the response status are unchanged.

### BC-0814-10: audio transcription accepts the file only in the file field, never truncates it silently, and returns a recognition refusal as 400

> Old format supported until: not provided

**Before**

POST `/v1/audio/transcriptions` took the first multipart file part regardless of its field name and did not check the filename extension: an unknown extension was labelled `audio/mpeg` and forwarded for recognition. A file over the 25 MB limit was not rejected but silently cut at the limit: the answer was 200 with a transcript of only the beginning of the recording, and it was billed — nothing in the response indicated the cut. Any non-2xx from the recognition service came back as 502 `ai_provider_unavailable`, including a rejection of the request body.

**After**

The file part must be named `file`, otherwise 400 `no_file` — whatever the size of the file sent. That check runs before the recognition call. The filename extension is NOT checked: the recognition service detects the container from the content, so rare voice-recorder formats, a name without an extension and a part with no name are accepted exactly as before. A file the recognition service could not read comes back as 400 `ai_provider_rejected`. A file over 25 MB is rejected outright — 413 `request_too_large`, nothing charged; a truncated transcript no longer happens. A recognition refusal with HTTP 400 or 422 is returned as 400 `ai_provider_rejected` with a `providerStatusCode` field; rate limiting on its side is returned as 429 `rate_limit_exceeded` with a `Retry-After` header; unavailability and authentication errors stay 502 `ai_provider_unavailable`.

**Integrator action**

Name the file part `file` — a previous name such as `audio` no longer works. Split recordings above 25 MB before sending: such a request is now rejected rather than partly transcribed. If your code branched on the status, note that a body rejection now arrives as 400 rather than 502, and retrying such a request cannot help. There is no need to change extensions: the list includes the formats that used to be accepted silently. The old behaviour is not coming back.

### NEW-0814-11: catalog product image metadata

Added `GET /v1/catalog-products/:productId/images` for a product-scoped snapshot of native images and `GET /v1/catalog-products/:productId/images/:imageId` to retrieve one image. The snapshot includes the detail picture, preview picture, and `MORE_PHOTO` gallery, but not files stored in other custom properties. Both methods require the `catalog` scope, return an untrusted `detailUrl`, and never expose the signed `downloadUrl`; server-side fetching requires the platform SSRF policy. [Documentation](/docs/entities/catalog-products/images).

### NEW-0814-12: audio transcription can now draw on the Cowork/Code subscription quota

Calling [POST /v1/audio/transcriptions](/docs/ai/audio/transcriptions) with a key carrying the `vibe:cowork` scope used to consume nothing: transcription was metered against the Bitrix24 account AI quota, which subscription keys skip. A platform administrator can now price the transcription model per minute of audio, and such a call draws on the subscription quota, exactly like chat does.

Until a price is set the behaviour is unchanged: the call is free and no subscription limit applies to it.

Once a price is set and the quota window is exhausted, the endpoint answers `402` with code `cowork_quota_exhausted` — the same code chat already returns — plus a `Retry-After` header holding the seconds until the window resets. The body carries `window` (`5h` / `week` / `month`), `resetAt` and `nextTier`.

Response formats that carry no duration (`text`, `srt`, `vtt`) are billed at the per-call price, because the audio length is not reported for them.

### FIX-0814-13: a wake schedule no longer shortens the auto-sleep you set

**Before**

When a server carried both an auto-sleep timeout (`sleepAfterMinutes` — 30, 60 or 240 minutes) and an enabled wake-schedule window, the idle threshold was silently replaced with the platform's short 15-minute one. The server fell asleep after 15 minutes instead of the value it was given, while `GET /v1/infra/servers/:id` and the server card kept reporting the chosen value — the divergence was not visible anywhere.

**After**

The value you set applies as set: a schedule only decides when the server wakes up. The short threshold stays exactly where it was introduced for — a server with no auto-sleep at all (`sleepAfterMinutes: null`), so that it still sleeps between windows. No integration change is needed; servers holding both an auto-sleep timeout and a schedule now stay up until their own threshold.

### NEW-0814-14: a write with a lossy-charset value now reports it

**Before**

When a title or description arrived with its non-ASCII characters already replaced by question marks, the platform stored the value silently. The catalog card then showed `????????? ????????`, and the only way to notice was to look at it.

**After**

The value is still stored, and the response now also carries a `warnings` array naming the fields that arrived with no non-ASCII character left, suggesting the text be re-sent as UTF-8. This applies to `POST /v1/infra/servers` and `PATCH /v1/infra/servers/{id}` (`displayName`, `description`), `POST /v1/infra/servers/{id}/deploy` (`displayName`, `description`), `POST /v1/apps` and `PATCH /v1/apps/{id}` (`title`), `POST /v1/apps/{id}/publish` (`catalogTitle`, `catalogDescription`). The warning is emitted only for a field the call actually applied: re-sending the same value, or a field the deploy dropped because it was already set, stays silent. The field is optional and absent when there is nothing to report, so existing clients keep working unchanged.
