# API changes: September 26, 2026

[← Changelog](/docs/changelog) · [September 2026](/docs/changelog/2026-09)

### BC-0926-1: the Cowork subscription key of an owner without a Bitrix24 account cannot be rotated, re-enabled or extended

> Old format supported until: not provided

**Before**

The owner of a Cowork subscription key who signed in from their Bitrix24 without a Bitrix24 account could rotate that key (`POST /v1/keys/:id/rotate`), and could also return a revoked key to `ACTIVE` and extend or drop its `expiresAt` (`PATCH /v1/keys/:id`).

**After**

Rotation answers `403 COWORK_BOX_SERVICE_ACCOUNT_DENIED`. `PATCH /v1/keys/:id` answers the same code only when the body moves the key to `ACTIVE` from another status, or extends or drops `expiresAt`. Every other update works as before: renaming, revoking and shortening the expiry still succeed. The previous key keeps working. Nothing changed for owners with a Bitrix24 account. Code table — [Key management](/docs/management-keys).

**What integrators should do**

If you rotate a Cowork subscription key or bring it back into service, sign in to the account with a Bitrix24 account and issue the key from it. If you only rename, revoke or shorten the expiry of keys, nothing changes for you.

### NEW-0926-2: Direct task actions through the API

14 task actions now have named routes `POST /v1/tasks/:taskId/{add-accomplices,add-auditors,approve,bind-crm-item,complete,defer,delegate,disapprove,pause,pause-timer,renew,start,start-timer,take}`. Each route calls the Bitrix24 method of the same name — `tasks.task.addAccomplices`, `tasks.task.addAuditors`, `tasks.task.approve`, `tasks.task.bindCrmItem`, `tasks.task.complete`, `tasks.task.defer`, `tasks.task.delegate`, `tasks.task.disapprove`, `tasks.task.pause`, `tasks.task.pauseTimer`, `tasks.task.renew`, `tasks.task.start`, `tasks.task.startTimer`, `tasks.task.take` — with the same parameters. Pass `taskId` in the path and the other named method arguments in the JSON body. The Bitrix24 result is returned in `data`. The key must allow writes and have the `task` scope.

**Routes and documentation**

- [POST /v1/tasks/:taskId/add-accomplices](/docs/entities/tasks/add-accomplices)
- [POST /v1/tasks/:taskId/add-auditors](/docs/entities/tasks/add-auditors)
- [POST /v1/tasks/:taskId/approve](/docs/entities/tasks/approve)
- [POST /v1/tasks/:taskId/bind-crm-item](/docs/entities/tasks/bind-crm-item)
- [POST /v1/tasks/:taskId/complete](/docs/entities/tasks/complete)
- [POST /v1/tasks/:taskId/defer](/docs/entities/tasks/defer)
- [POST /v1/tasks/:taskId/delegate](/docs/entities/tasks/delegate)
- [POST /v1/tasks/:taskId/disapprove](/docs/entities/tasks/disapprove)
- [POST /v1/tasks/:taskId/pause](/docs/entities/tasks/pause)
- [POST /v1/tasks/:taskId/pause-timer](/docs/entities/tasks/pause-timer)
- [POST /v1/tasks/:taskId/renew](/docs/entities/tasks/renew)
- [POST /v1/tasks/:taskId/start](/docs/entities/tasks/start)
- [POST /v1/tasks/:taskId/start-timer](/docs/entities/tasks/start-timer)
- [POST /v1/tasks/:taskId/take](/docs/entities/tasks/take)

### FIX-0926-3: looping model reasoning is cut off with the reasoning_repetition code

**Before**

If a reasoning model started repeating the same sentences in `reasoning_content`, the `POST /v1/chat/completions` stream was held until the service deadline or the `max_tokens` limit, and no answer ever came. On a request with `response_format` the refusal arrived as `422 structured_output_truncated` advising to raise `max_tokens`, with a `suggestedMaxTokens` field — on a looping model a larger budget only made the repetition longer, and the attempt was billed once more.

**After**

Repetition of the same sentences in the reasoning before the answer starts is stopped without waiting for the service deadline or the `max_tokens` limit — both on the main stream and on the fallback model stream when the quota is exhausted. In a stream the event `{"error":{"code":"reasoning_repetition","type":"invalid_request_error"}}` arrives before `data: [DONE]`, and the stream status stays `200` by protocol; a synchronous request with `response_format` gets `422` with the code `reasoning_repetition`, the `finishReason` and `hint` fields and no `param` or `suggestedMaxTokens`. The way out is to send the request again with `reasoning_effort: "none"`.

Only repetition of the same sentences is cut off, not a run of different wordings, and only in what reaches the client as `reasoning_content`: a repetitive answer in `content`, such as an array of identical objects, is delivered in full. If the model sends the same text into both channels at once, what counts is how it reaches the client: for a reasoning model this text arrives in `content` and is delivered as the answer; if it arrives only as `reasoning_content`, its repetition is cut off the same way as reasoning.

With `response_format: {"type": "json_object"}`, when the model sends the answer itself in `reasoning_content` as a JSON document (starting with `{` or `[`), the complete document arrives in `content` with status `200`; if the document never comes together, `reasoning_repetition` arrives, and if it is too long to be recovered, the repetition is cut off at once. Without `response_format` an answer that arrives only in `reasoning_content` is cut off with `reasoning_repetition` when it loops. A regular cut-off at `max_tokens` without repetition still answers `structured_output_truncated`.

### FIX-0926-4: the streamed chat response always ends with a finish reason

**Before**

A streamed [POST /v1/chat/completions](/docs/ai/chat/streaming) response could end with `data: [DONE]` without a single event carrying `finish_reason`. Two different cases looked like this: the model finished its answer but sent no terminal event, and the connection to the model dropped mid-answer. A client could not tell a complete answer from a cut one, and some clients did not end the turn without `finish_reason`.

**After**

If the model finished its answer without a terminal event, the platform adds one before `data: [DONE]` itself: an empty `delta` with `finish_reason: "tool_calls"` when the answer contained tool calls, or `finish_reason: "stop"` otherwise. If the connection dropped before a finish reason arrived, an error event with the code `stream_incomplete`, `type: "server_error"`, `retryable: true` and `retryAfter` arrives before `data: [DONE]`. The response status remains `200`, as for the other errors inside the stream.

**Impact on integrators**

No change is required. A client that reads the stream to the end and checks the `error` field receives `stream_incomplete` the same way as `stream_idle_timeout` and can retry the request, honoring `retryAfter`.

### FIX-0926-5: a large ERP user mapping replacement no longer answers 503

**Before**

A full mapping replacement `PUT /v1/cowork/onec/access/legacy/mappings` that touched several thousand mappings could be refused with HTTP 503, the `DB_TRANSIENT` code and a `Retry-After` header. The request was valid, yet nothing was saved.

**After**

A replacement of several thousand mappings goes through in a single request. A valid replacement within the limits is committed, and the response remains HTTP 200.

**Impact on integrators**

No change is needed. Retrying after a 503 is still safe: the replacement sets the complete mapping set.
