# Изменения API: 18 августа 2026

[← Журнал изменений](/docs/changelog) · [Август 2026](/docs/changelog/2026-08)

### BC-0818-1: контракт управления приложениями и исходниками уточнён

> Поддержка старого формата до: не предусмотрена

**Было**

OpenAPI не описывал точные успешные ответы, лимиты метаданных и часть кодов ошибок для операций [хранилища исходников приложений](/docs/source-storage). OAuth-ключ одного приложения мог изменить другое приложение, а также читать и изменять исходники сервера того же автора, принадлежавшего другому ключу. Лимит примечания считал UTF-16 code units, поэтому часть допустимых строк с Unicode отклонялась. Временная ссылка на скачивание могла сохраняться промежуточным кешем.

**Стало**

Контракт восьми операций описывает фактические схемы ответов, лимиты, коды ошибок и матрицу доступа. OAuth-ключ приложения теперь может напрямую управлять только своим приложением и обращаться к исходникам сервера, принадлежащего этому же ключу. При публикации собственного приложения явный `sourceServerId` может выбрать сервер того же автора под личным ключом, но не сервер другого OAuth-приложения. Личные ключи автора и ключи администратора сохраняют доступ. Лимит примечания считается по Unicode code points. Ответы с временными ссылками приложения и сервера помечены `Cache-Control: private, no-store`, а ссылка и содержащийся в ней путь хранилища явно обозначены как краткоживущий bearer credential.

Переходное окно не предоставляется: прежний доступ OAuth-ключа приложения к ресурсам другого ключа был дефектом разграничения доступа.

### FIX-0818-2: `?refresh=tariff` у коробочного портала освежает и состояние подписки Маркетплейса

**Было**

`GET /v1/me?refresh=tariff` у коробочного портала перепроверял тариф, но состояние подписки Маркетплейса отдавал из прошлого снимка. Клиент, оплативший подписку прямо перед вызовом, продолжал получать прежний ответ, пока состояние не обновит фоновая проверка.

**Стало**

Запрос перепроверяет и это состояние, а ответ собирается уже по свежим данным. Оплата, прошедшая перед вызовом, видна сразу.

### NEW-0818-3: состав рабочей группы с ролями участников

Появился эндпоинт `GET /v1/workgroups/:groupId/users` — он возвращает участников рабочей группы вместе с их ролью в ней. Раньше фасад `/v1/workgroups` отдавал только владельца и число участников, поэтому приложение, которому нужно различать руководителя, модератора и рядового участника, сделать это не могло.

Каждая строка ответа несёт `userId` и `role`. Поле `role` содержит букву Битрикс24 как есть: `A` — владелец, `E` — модератор, `K` — участник. Незнакомая буква проходит насквозь, а не приводится к известной и не отбрасывается, поэтому проверяйте те значения, которые знаете, а всё остальное трактуйте как отсутствие прав.

Параметров постраничной выдачи у операции нет, состав возвращается целиком, и `meta.total` всегда равен числу строк в `data`. Пустой состав — обычный ответ, а владелец группы не обязательно присутствует среди строк: его идентификатор берите из `GET /v1/workgroups/:id`. Ответ `404` означает, что группы нет **либо** она не видна той учётной записи Битрикс24, от имени которой действует ключ, — Битрикс24 не различает эти случаи.

Эндпоинт отдаёт данные о составе, а не решение о доступе: что именно он покажет, зависит от личности за ключом, а признак администратора портала — отдельный факт, его отдаёт `GET /v1/users/me`. Вложенные ресурсы недоступны через `POST /v1/batch`, поэтому операция вызывается только по своему пути. Нужен скоуп `sonet_group`.

### FIX-0818-4: сделки: семь полей из GET /v1/deals/fields теперь принимаются в фильтре

**Было**

`GET /v1/deals/fields` перечислял поля, которые фильтр не принимал. Запрос
`POST /v1/deals/search` с `filter[leadId]`, `filter[quoteId]`, `filter[taxValue]`,
`filter[originId]`, `filter[originatorId]`, `filter[additionalInfo]` или
`filter[lastActivityBy]` отклонялся с `400 UNKNOWN_FILTER_FIELD` ещё до обращения к Битрикс24, хотя Битрикс24 эти поля
в фильтре принимает. Отчёт, отбирающий сделки по лиду, построить было нельзя.

**Стало**

Все семь полей объявлены в схеме сделок и принимаются в `filter` — и в
`GET /v1/deals`, и в `POST /v1/deals/search`, и в `POST /v1/deals/aggregate`.
В `select` они принимались и раньше, но с предупреждением
`UNKNOWN_SELECT_FIELD` — теперь предупреждения нет. Сортировка по ним работала
и до этого изменения и не менялась. Имена полей в ответах прежние. Набор осей
группировки `groupBy` не менялся.

Два уточнения по значениям. У `leadId`, `quoteId`, `originId`, `originatorId`
и `additionalInfo` в `GET /v1/deals/fields` и в OpenAPI теперь стоит
`nullable` — эти поля возвращают `null` у сделки, которая не создавалась из
лида, из предложения или импортом. Раньше их в спецификации не было вовсе,
так что клиент, сгенерированный по ней, обязан быть готов к `null`. И у трёх
строковых полей (`originId`, `originatorId`, `additionalInfo`) пустая строка
от Битрикс24 приводится к `null` — так же, как у всех остальных строковых
полей API. На замере 300 сделок пустая строка не встретилась ни разу, Битрикс24
отдаёт по этим полям `null`, поэтому смены поведения на реальных данных мы не
ожидаем; но если ваш код сравнивает такое поле с `""`, сравнивайте с пустым
значением.

Шесть полей — `utmSource`, `utmMedium`, `utmCampaign`, `utmContent`, `utmTerm`
и `contacts` — по-прежнему отклоняются в фильтре и сортировке: Битрикс24 сам их
там не поддерживает. Отказ оставлен намеренно, иначе выборка была бы молча
неверной. Теперь причина видна заранее — `GET /v1/deals/fields` описывает её в
поле `description`. Для контактов фильтруйте по `contactId` (основной контакт) или по `contactIds` (любой из привязанных).

У лидов, предложений и элементов смарт-процессов пять UTM-полей остаются
доступны в ответах, но больше не проходят локальную проверку фильтра и
сортировки: Битрикс24 не принимает их в `crm.item.list`. Вместо ошибки
Битрикс24 `422` запрос теперь заранее получает `400 UNKNOWN_FILTER_FIELD` или
`400 UNKNOWN_SORT_FIELD`, не обращаясь к Битрикс24. Остальные операции с этими
полями не изменились.

### FIX-0818-5: пустой результат методов рабочего дня возвращается как null

**Было**

При пустом успешном результате Битрикс24 поле `data` могло содержать служебную оболочку с полями `result`, `total` и `next` вместо значения результата.

**Стало**

[POST /v1/workday/open](/docs/workday/open), [POST /v1/workday/close](/docs/workday/close), [POST /v1/workday/pause](/docs/workday/pause), [GET /v1/workday/status](/docs/workday/status), [GET /v1/workday/settings](/docs/workday/settings) и [GET /v1/workday/schedule](/docs/workday/schedule) возвращают `data: null`, если успешный результат пуст.

**Влияние на интеграторов**

Клиентам не требуется извлекать пустое значение из служебной оболочки ответа Битрикс24.

### NEW-0818-6: справочник полей заказов и статусов заказа отдаёт названия и пояснения

**Было**

`GET /v1/orders/fields` и `GET /v1/order-statuses/fields` отвечали на каждое поле только типом и флагом только-для-чтения. Ни человекочитаемого названия, ни пояснения не было — всего 54 поля, по которым клиент мог опираться только на имя поля. Сгенерировать по такой схеме форму или типизированную модель было нечем: имя вроде `recountFlag` или `empStatusId` само по себе ничего не объясняет.

**Стало**

Каждое поле обеих сущностей несёт `label` (короткое название) и `description` (пояснение) на языке сегмента. Пояснение говорит то, чего не передаёт тип: почему `price` принимается только при создании (Битрикс24 пересчитывает сумму из позиций корзины), что `requisiteLink` приходит пустым массивом, когда связь не задана, что `clients`, `payments`, `basketItems` и `propertyValues` возвращаются только в карточке заказа, и что Битрикс24 требует поле `type` при каждом обновлении статуса. Ответ вырос, набор полей и их типы не изменились — старые запросы работают как прежде.

### NEW-0818-9: промокод в приложении Cowork/Код: проверка кода и погашение по ключу десктопа

Приложение Cowork/Код теперь принимает промокод само, не отправляя человека в кабинет. Появились два метода: `POST /v1/cowork/coupon/preview` показывает, что даёт код (тариф, срок, название кампании), ничего не меняя, а `POST /v1/cowork/coupon/redeem` его гасит и включает подаренный тариф на срок, оплаченный платформой Вайбкод.

Оба метода требуют ключ десктопа Cowork/Код с доступом `vibe:cowork`. Агентский ключ с тем же доступом получит `403 COWORK_DESKTOP_KEY_REQUIRED`: погашение необратимо и одноразово, а за таким ключом нет человека, который принимает решение.

Проверка отвечает `200` и в теле сообщает `valid: false` с причиной `COUPON_INVALID`, если код не годится. Причина одна на все случаи «код не подходит» — так метод не подсказывает подбирающему, чем именно отличается несуществующий код от отозванного. Годность здесь означает «код живой и кампания открыта»: условия самого рабочего места (уже оплачено, уже этот тариф) проверяются при погашении и могут дать отказ после успешной проверки.

Погашение возвращает выданный тариф, срок и поле `accessGranted`. `false` означает, что тариф выдан, но администратор портала ещё не открыл доступ к Cowork/Код — покажите это отдельно, иначе человек увидит успех и упрётся в закрытую дверь.

**Затронутые эндпоинты:** `POST /v1/cowork/coupon/preview`, `POST /v1/cowork/coupon/redeem`

### NEW-0818-10: витрина приложений в API: список и карточка

В API Вайбкод появилось семейство приложений: [GET /v1/applications](/docs/applications/list) отдаёт список, [GET /v1/applications/:id](/docs/applications/get) — одну карточку. Выборка идёт по владельцу ключа, а не по самому ключу, поэтому в список попадают и приложения, созданные в кабинете, — их серверы привязаны к другим ключам того же человека и в `GET /v1/infra/servers` не видны.

Параметр `scope` принимает `mine` (свои), `shared` (доступные вам чужие — как открытые лично вам, так и открытые всему порталу) и `feed` (общий список, значение по умолчанию), плюс постраничные `page` и `limit`. Карточка доступна и получателю доступа, а не только владельцу; отношение зрителя к приложению приходит в поле `viewerState`.

Рядом с `total` в ответе списка приходит `truncated`. У скоупа `feed` порядок приложений считает платформа, поэтому выборка ограничена сверху: при `truncated: true` значение `total` — это тот предел, а не полное число приложений, и страниц за ним нет. Считать число страниц как `total / limit` можно, только когда `truncated` равно `false`; при `true` за остатком следует идти в скоупы `mine` и `shared`, у которых предела нет.

Каждая карточка несёт два дополнительных блока. `sources` сообщает, есть ли сохранённые версии исходников, и отдаёт идентификатор последней в том же виде, который принимает скачивание версии, — обращаться за списком версий заранее не нужно. `activeOperation` показывает идущую операцию: вид, шаг и время старта; значение `unknown` означает, что операция начиналась, но её исход неизвестен. Оба блока наполняются только для того, кто приложением управляет: у зрителя, которому приложение просто открыли, `sources` приходит пустым (`hasVersions: false`, оба поля `null`), а `activeOperation` — `null`. Форма ответа при этом не меняется, поэтому важно не сделать неверный вывод: пустой `sources` у чужого приложения означает «эти данные не отдаются», а не «версий нет».

Важная оговорка про `activeOperation`: `null` означает «операции с сохранённой записью нет», а не «с приложением ничего не происходит». Признак покрывает выкладку, починку, изменение тарифа сервера и перенос контейнера между галактиками — остальные действия платформа не журналирует и в этом поле не показывает.

Открывать приложение не нужно вычислять самому: карточка отдаёт готовую пару `openUrl` и `openTarget`. Значение `openTarget` сейчас одно — `app`, то есть открывается собственный адрес приложения; оба поля `null`, когда открывать нечего. Список значений закрытый и может пополниться, поэтому неизвестное значение стоит трактовать как «открывать нечем», а не как ошибку.

⚠️ У приложения, встроенного в Битрикс24, оба поля приходят `null` — платформа пока не знает адреса, по которому его открывают внутри портала. Собственный адрес такого приложения в `openUrl` намеренно НЕ подставляется: он ведёт на страницу входа шлюза, а не в приложение, и переход по нему выглядел бы удачным, не будучи им.

Различить эти два состояния — «встроено, открывается внутри Битрикс24» и «ещё не опубликовано» — позволяет поле `isEmbedded`: у обоих пара открытия пуста, а тексты для пользователя разные. Выводить встройку из наличия сервера НЕЛЬЗЯ: встроенное приложение без собственного сервера — штатное состояние (встройку сделали, код ещё не выкладывали), и признак от сервера не зависит вовсе. Поле приходит и владельцу, и тому, кому приложение открыли.

У сервера появился признак `reachable` — `true`, когда сервер и работает, и отвечает по сети. Отдельно от `status`, потому что это разные факты: сервер бывает поднят, а сетевой туннель до него не поднят, и по одному `status` такое приложение выглядит рабочим. Проверить это со стороны клиента нельзя, поэтому признак считает платформа.

Признак уже включает состояние `RUNNING`: истинным при другом состоянии сервера он не приходит, поэтому конъюнктить его со `status` не нужно. ⚠️ **У приложения в галактике (`server.kind: "GALAXY_APP"`) вторая половина признака берётся с ХОСТА галактики, а не с самого контейнера** — связность держит хост, у контейнера своего туннеля нет по устройству. Отсюда следствие, о котором лучше знать заранее: только что созданный контейнер, ещё не дошедший до `RUNNING` (а доходит он лишь после первой загрузки исходников), приедет с `reachable: false` даже при полностью живом хосте. Это означает «контейнер пока не поднялся», а не «хост недоступен» — различить два случая по одному признаку нельзя, для этого есть `status`.

У сервера появилось поле `lastDeployedAt` — момент последней успешной выкладки. Это единственный в разделе признак того, что приложением живут: `updatedAt` двигает только правка карточки, а `sources.latestSavedAt` означает «код сохранён» и приходит лишь тому, кто приложением управляет. Отметку ставит сама выкладка в момент фиксации успеха, поэтому провалившаяся выкладка её не двигает. ⚠️ Истории у поля нет: у серверов, созданных до появления поля, оно приходит `null` до первой следующей выкладки — восстанавливать историю мы не стали, потому что доступный источник покрывает только один из трёх путей выкладки, и дата появилась бы у одних видов серверов и отсутствовала у других.

Ещё две вещи, о которых лучше знать заранее. `id` приложения из этого раздела и `id` из `GET /v1/apps` — разные значения разных сущностей: здесь карточка приложения, там регистрация приложения на портале Битрикс24. И одно и то же по смыслу название приходит в них под разными именами: `name` здесь и `title` там. Переносить одно в другое нельзя, поля не синхронизируются.

Порядок в списке назван прямо, чтобы постраничный обход был воспроизводимым. У скоупа `feed`: закреплённые вами → свои → чужие, внутри по `updatedAt` от свежих к старым, при равных метках — по `id`. У `mine` и `shared`: по `createdAt` от свежих к старым, при равных метках — по `id`. Вторичный ключ здесь не формальность: у приложений, созданных пакетно, метки времени совпадают, и без него две страницы одного обхода могли пересечься или пропустить приложение. ⚠️ `updatedAt` двигается только правкой карточки — выкладка её не трогает, поэтому «свежесть» в ленте означает «когда карточку меняли», а не «когда приложение выкладывали».
