Для AI-агентов: markdown этой страницы — /docs-content/changelog/2026-07-29.md индекс документации — /llms.txt

Изменения API: 29 июля 2026

← Журнал изменений · Июль 2026

FIX-0729-1: комментарий к задаче больше не отбивается по правам ключа

Было

POST /v1/tasks/:taskId/comments на порталах с новой карточкой задач мог вернуть 403 с сообщением портала «Недостаточно прав доступа: отсутствует необходимый scope» — даже когда у ключа есть право task, а соседние вызовы задач в ту же секунду отвечают 200. Формулировка уводила в тупик: она читалась как «выдайте приложению доступ к задачам», хотя набор прав определяется при выпуске ключа, а не действиями пользователя.

Стало

Права на комментарий выдаются ключу в обеих формах, которых требуют старый и новый маршрутизаторы Битрикс24, поэтому вызов проходит. Ключам, выпущенным раньше, набор прав досылается на месте при первом таком отказе, и запрос повторяется — вмешательство не нужно. Если после этого портал всё равно отказывает, ответ 403 теперь прямо называет причину (у вебхука ключа набор прав уже, чем у самого ключа) и подсказывает переиздать ключ — вместо пересказа сообщения портала.

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

Менять ничего не нужно. Клиент, ловивший этот 403 как постоянную ошибку, теперь получает 201.

FIX-0729-2: изменение доступа к приложению на общем хосте доезжает до его показа в Битрикс24

Было

Для приложения на общем хосте (kind=GALAXY_APP), встроенного в интерфейс Битрикс24, изменение списка доступа доезжало до показа не всегда. Пользователь, у которого доступ отозвали, мог продолжать видеть приложение на своём месте встройки — доступ к самому приложению при этом уже был закрыт.

Это касалось смены политики доступа и правки списка через PATCH /v1/infra/servers/:id/access-policy, POST /v1/infra/servers/:id/access и DELETE /v1/infra/servers/:id/access/:accessId.

Стало

Изменение доступа отражается на показе: приложение пропадает из интерфейса Битрикс24 у тех, кто доступ потерял, и появляется у тех, кому его выдали.

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

Менять ничего не нужно. Тела запросов, ответы и коды ошибок прежние — изменился только наблюдаемый эффект вызова. Приложений на собственной виртуальной машине изменение не касается: там показ и раньше следовал за доступом.

FIX-0729-3: агенты и боты больше не засыпают по простою

Было

PATCH /v1/infra/servers/:id/sleep принимал любое значение sleepAfterMinutes, включая серверы, созданные под агента или бота (createdVia agent или bot).

Стало

Для сервера с createdVia agent или bot и ненулевым sleepAfterMinutes эндпоинт отвечает 400 с кодом AGENT_IDLE_SLEEP_FORBIDDEN. Значение null (никогда не засыпать) по-прежнему принимается.

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

Спящий агент или бот перестаёт опрашивать Битрикс24 и не просыпается на новое сообщение, поэтому прежнее значение было нерабочим — менять в рабочем сценарии нечего. Для экономии по расписанию используйте Запланированное пробуждение.

BC-0729-4: сессия в Authorization должна принадлежать приложению из X-Api-Key

Поддержка старого формата до: 29.07.2026

Было

Сессия (vibe_session_*) выписывается одному приложению на одном аккаунте Bitrix24, но при вызове /v1/* эта привязка не проверялась. Ключ авторизации приложения B принимал сессию, выписанную приложению A, и запрос выполнялся под правами ключа B — то есть чужая сессия открывала доступ к данным через ключ другого приложения.

Стало

/v1/* проверяет, что сессия в Authorization: Bearer принадлежит тому же приложению и тому же аккаунту Bitrix24, что и ключ в X-Api-Key. Несовпадение — 403 с кодом SESSION_APP_MISMATCH. Ключ авторизации приложения, у которого приложение отвязано или удалено, сессию больше не принимает. Личных ключей vibe_api_* изменение не касается: сессию в Authorization они не читают — ни до него, ни после.

Вызовы без Authorization (только по ключу) не затронуты. Сессия, предъявленная с ключом своего приложения, работает как раньше.

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

Проверьте, что оба заголовка относятся к одному приложению: X-Api-Key — ключ авторизации того приложения, которое выписало сессию через POST /v1/oauth/token. Если сервис обслуживает несколько приложений, храните пару «ключ + сессия» вместе и не берите их из разных мест.

Проверка включена с выпуска этого изменения, без переходного периода: она закрывает доступ к данным по чужой сессии. Ошибка 403 SESSION_APP_MISMATCH описана в справочнике ошибок.

FIX-0729-5: приложение на личном ключе снова получает X-Vibe-Authorization

Было

Приложение, размещённое в Black Hole на личном ключе (vibe_api_*), теряло заголовок X-Vibe-Authorization примерно через минуту после открытия плейсмента. Первые запросы проходили с сессией, дальше она пропадала и не возвращалась ни через перезагрузку страницы, ни через повторное открытие приложения — только через новое открытие плейсмента, и опять на минуту.

Причина: сессия, которую плейсмент выписывает пользователю, привязана к приложению через его адрес (appUrl), а восстанавливалась она только по цепочке «сервер → ключ → приложение». У личного ключа приложения нет, поэтому восстановление отвечало «сервер не найден», и Gateway запоминал этот отказ. X-Vibe-User-Id при этом продолжал приходить, поэтому со стороны приложения это выглядело как «пользователь есть, а токена нет».

Стало

Когда у ключа-владельца сервера нет приложения, оно ищется по адресу приложения: берутся приложения того же аккаунта Bitrix24, созданные владельцем ключа, чей appUrl указывает ровно на этот адрес приложения. Сессия восстанавливается, и заголовок продолжает приходить на всё время жизни сессии.

Приложения на ключе авторизации (vibe_app_*) работают как раньше — у них цепочка «сервер → ключ → приложение» есть, и она остаётся главной.

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

Менять ничего не нужно. Если приложение обходило проблему повторным открытием плейсмента или собственным хранением токена — эти костыли можно снять.

NEW-0729-6: управляющий ключ владельца, аккаунт которого ждёт удаления, получает 503

Было

Заморозка аккаунта на время запроса на удаление данных действовала на веб-кабинет и на обычные ключи приложения, но не на управляющие ключи: владелец с аккаунтом в состоянии ожидания удаления продолжал выпускать, ротировать и удалять ключи через /v1/keys.

Стало

Управляющий ключ владельца, аккаунт которого находится в процессе удаления данных, получает 503 с кодом ACCOUNT_PENDING_ERASURE и заголовком Retry-After: 3600 — так же, как это давно работает для обычных ключей приложения. Если запрос на удаление отменён, ключ начинает работать снова без перевыпуска.

FIX-0729-7: деплой galaxy-приложения проверяет доступность и отдаёт шаги

Было

Успешный деплой galaxy-приложения через POST /v1/infra/servers/:id/deploy возвращал success: true, status: "running" без data.steps[] и без проверки того, что приложение действительно отвечает по HTTP. Контейнер, который поднялся, но не слушал порт, всё равно рапортовался как успех — отличить рабочий деплой от сломанного было нельзя. Плюс GET /v1/infra/servers/:id для такого приложения показывал runtime: null и порт по умолчанию — развёрнутый рантайм и порт не сохранялись.

Стало

Успешный ответ несёт data.steps[]: шаг { step: "build", status: "ok" } и — когда проба выполнялась — шаг { step: "healthcheck", status: "ok" | "warning", httpCode, healthPath }. status: "ok" означает, что приложение ответило 2xx/3xx на data.appUrl; warning — ответило 4xx/5xx (всё ещё достижимо, деплой прошёл). Контейнер, который поднялся, но не ответил по HTTP на своём порту, теперь честно завершается 502 GALAXY_APP_START_FAILED (то же семейство, что крах после старта — в теле остаётся хвост buildLog), а не рапортуется как успех. Необязательное поле healthPath (по умолчанию /) в теле деплоя задаёт путь пробы. GET /v1/infra/servers/:id теперь отражает развёрнутый runtime и порт.

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

Приложение, которое отвечает по HTTP на порту деплоя, ничего не заметит. Приложение, которое стартует, но не начинает отвечать в окне пробы, получит 502 GALAXY_APP_START_FAILED вместо ложного успеха — убедитесь, что оно слушает порт, указанный при деплое, и что healthPath возвращает ответ. Шаг build в data.steps[] и сохранение runtime/порта в GET доступны сразу; сама HTTP-проба (шаг healthcheck и 502 GALAXY_APP_START_FAILED) раскатывается — включается на платформе постепенно, поэтому до её активации деплой ведёт себя как раньше (без пробы).

FIX-0729-8: серверы просыпаются после погашения долга и на постоплатном счёте

Было

Портал с постоплатным биллингом, ушедший в минус до лимита овердрафта, глушил серверы и помечал их «заморожено биллингом». После пополнения счёта API снова отвечал 200, но серверы так и оставались выключенными: POST /v1/infra/servers/{id}/wake возвращал отказ (SERVER_WAKE_BLOCKED), и снять пометку можно было только обращением в поддержку. На предоплатном биллинге тот же сценарий отрабатывал нормально.

Стало

Как только баланс перестаёт быть отрицательным, пометка снимается и серверы поднимаются автоматически — одинаково на предоплате и постоплате. Частичное пополнение, оставляющее баланс в минусе, снова открывает API, но серверы держит выключенными: они возвращаются к работе, когда долг закрыт полностью. Серверы, остановленные по другим причинам (истёк доступ, остановка вручную), пробуждение не затрагивает.

FIX-0729-9: деплой не рапортует ложный успех после отката усиленного юнита

Было

Деплой на POST /v1/infra/servers/:id/deploy мог отрапортовать успех (healthcheck:ok, hardening:warning), обслуживая при этом ответ чужого процесса, занявшего порт приложения. Это происходило, когда усиленный (hardened) юнит падал, деплой автоматически откатывался на обычный юнит, но откат не освобождал порт — а на первой же проверке чужой держатель порта отвечал 200. В результате выкладка сообщала об успехе, хотя новая версия порт так и не заняла и в продакшене продолжала работать предыдущая.

Стало

Если после отката порт по-прежнему держит тот же процесс, что заблокировал усиленный юнит (откатанный юнит порт не занял), деплой завершается ошибкой healthcheck:error с явным сообщением о том, что порт всё ещё занят, вместо ложного healthcheck:ok. Штатный откат, при котором порт занимает уже новый экземпляр приложения, по-прежнему завершается успехом.

FIX-0729-10: кэш метаданных учитывает портал и схемы полей

Было

Кэш GET /v1/statuses был описан как привязанный к личному ключу, а схемы GET /v1/{entity}/fields не были отражены в разделе кэширования. Клиент не видел в документации, какие повторные запросы получают X-Cache: HIT и как запросить свежую схему полей.

Стало

GET /v1/statuses описан как кэш портала на 5 минут. GET /v1/{entity}/fields описан как кэш схемы полей на 5 минут с учётом портала, ключа авторизации, сущности, параметров пути, параметров запроса и языка ответа. Обход кэша через Cache-Control: no-cache доступен для всех этих чтений, а для /fields также доступен refresh=true.

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

Код менять не нужно. Повторные чтения метаданных меньше нагружают очередь портала, а заголовки X-Cache и X-Cache-Bypass-Reason показывают, был ли использован кэш.

NEW-0729-11: дельта последних диалогов по параметру updatedAfter

GET /v1/chats/recent принимает updatedAfter — момент в формате ISO 8601, начиная с которого нужно вернуть изменённые диалоги. Это отдельный режим ответа: data приходит плоским массивом диалогов, а meta несёт mode со значением delta и returned с их числом.

Размером выборки в этом режиме управляет сервер — читается одна страница до 200 диалогов. Переданный limit на неё не влияет и возвращается обратно в meta.requestedLimit вместе с применённым meta.appliedLimit. Когда дельту не удалось подтвердить полной — Битрикс24 сообщил, что за отданной страницей есть ещё диалоги, либо форму ответа не удалось разобрать, — в meta приходит truncated со значением true: в этом случае не сдвигайте updatedAfter, а получите полный список постраничным режимом с курсором lastMessageDate. Число возвращённых строк признаком полноты не является.

Граница включительна — диалог, у которого dateUpdate равен переданному моменту, попадает в ответ. Дата обязана нести явное смещение или Z: значение вида 2026-06-29 10:00:00 читается по-разному в зависимости от часового пояса сервера и отклоняется с 400 INVALID_PARAMS. Тем же кодом отклоняется сочетание updatedAfter с offset или lastMessageDate — постраничная навигация и дельта используют разные курсоры.

NEW-0729-12: транзиентный 503 на границе платформы теперь говорит, через сколько повторять

Когда платформа на мгновение недоступна — например в момент редеплоя galaxy-приложения — граница платформы отвечает 503 SERVICE_UNAVAILABLE на префиксе /v1/. В теле было только человекочитаемое «Retry in a few seconds», и машинного признака повторяемости не было: клиент не мог отличить секундную дыру от постоянной недоступности и либо ронял задачу, либо ретраил наугад.

Теперь этот ответ несёт HTTP-заголовок Retry-After: 5 и поле retryAfter: 5 внутри объекта error — рядом с code и message, ровно так же, как это делает сама платформа для своего транзиентного 503. Прежние вызовы не меняются: код SERVICE_UNAVAILABLE и статус остались теми же, добавлены заголовок и поле. Текст сообщения теперь дополнительно ссылается на заголовок — на него по-прежнему не следует опираться, ветвитесь по error.code. Полный список кодов — /docs/errors.

Тот же блок границы отвечает и на таймаут чтения от бэкенда. В этом случае запрос до бэкенда доехал и, возможно, всё ещё выполняется, поэтому для не-идемпотентных операций перед повтором сверьте состояние сущности.

Отдельно стоит сказать, чего это изменение НЕ делает: оно не устраняет причину, по которой апстримы бэкенда оказались недоступны. Оно делает ошибку честной и машинно-понятной, чтобы клиент корректно подождал и повторил.

BC-0729-13: пакетный запрос отклоняет сортировку у сущностей, чей метод Битрикс24 её не умеет

Поддержка старого формата до: 29.07.2026

Было

Одиночный список сущности и её POST /v1/departments/search уже отвечали 400 INVALID_SORT_FIELD, если метод Битрикс24 за списком не принимает порядок сортировки. Подзапрос общего POST /v1/batch этой проверки не имел: та же сортировка уходила в метод, тот её выбрасывал, и подзапрос возвращал успех с несортированным списком. Получалось расхождение на одном и том же запросе — через одиночный маршрут отказ, через пакетный молчание.

Стало

Подзапрос POST /v1/batch с действием list или search проверяет то же самое и отвечает 400 INVALID_SORT_FIELD в errors своего подзапроса; остальные подзапросы выполняются как обычно. Отказ приходит до обращения к Битрикс24. Проверяются оба написания — и sort, и order. Это касается двух сущностей: подразделений (departments) и телефонных линий (telephony-lines). Хранилищ это НЕ касается — их метод сортировку умеет, и отказ у них снят отдельной записью этого же выпуска.

Что делать интеграторам

Если подзапрос к одной из этих двух сущностей передавал сортировку — уберите её: она никогда не применялась, список приходил в порядке Битрикс24. Нужен свой порядок — сортируйте полученный список на своей стороне. Подзапросы без сортировки, а также limit, offset, select и фильтр, работают как раньше. Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что параметр молча игнорировался, — сохранять было бы нечего.

BC-0729-14: `defaultOperatorData` у открытых линий стал объектом, пустое значение — `null`

Поддержка старого формата до: 27.01.2027

Было

Поле объявлялось массивом, и незаданное значение приводилось к []. Настоящий тип другой: Битрикс24 отдаёт объект вида { "NAME": …, "AVATAR": … }, когда данные оператора по умолчанию заданы. То есть GET /v1/openline-configs и GET /v1/openline-configs/:id обещали в fields массив, а при заполненном значении присылали объект.

Стало

Тип поля — object; незаданное значение приходит как null, а не как []. Заполненное значение приходит объектом, как и раньше. Соседние kpiFirstAnswerList и kpiFurtherAnswerList — настоящие массивы строк, они по-прежнему приводятся к [].

Что делать интеграторам

Код, который считал длину или перебирал это поле (defaultOperatorData.length, .map, .forEach), сломается на null — замените проверку на if (config.defaultOperatorData) { … } и читайте поля объекта напрямую. Если вы ориентировались на fields и ждали массив — сверьтесь с новым типом object.

BC-0729-15: телефонные линии: запись `serverName`, сортировка, фильтр и смещение больше не теряются молча

Поддержка старого формата до: 29.07.2026

Было

serverName в схеме числился обычным изменяемым полем, но Битрикс24 его не хранит и не возвращает: POST /v1/telephony-lines с этим полем отвечал 201, а значение исчезало; PATCH того же поля упирался в ошибку самого Битрикс24. Отбор, порядок и смещение вели себя так же тихо: метод Битрикс24 за этим списком не принимает входных параметров вовсе, поэтому ?order[number]=desc, ?name=…, ?filter[number]=…, ?offset=50 и те же значения в теле POST /v1/telephony-lines/search отбрасывались, а список приходил 200 — и выглядел отсортированным, отфильтрованным и перелистнутым, хотя не был ничем из этого. В подзапросе POST /v1/batch тем же образом терялась сортировка.

Стало

Все четыре случая стали явной ошибкой до обращения к Битрикс24. Запись serverName — 400 READONLY_FIELD; любая сортировка — 400 INVALID_SORT_FIELD; любой фильтр — 400 UNSUPPORTED_FILTER; ненулевое смещение — 400 UNSUPPORTED_OFFSET. Отказ фильтру приходит на списке, в поиске, в POST /v1/telephony-lines/aggregate и в подзапросах обоих пакетных запросов — общего POST /v1/batch и POST /v1/telephony-lines/batch; отказ сортировке и смещению — на списке, в поиске и в подзапросе общего пакетного запроса (агрегат этих параметров не читает). В общем POST /v1/batch отказ приходит в errors своего подзапроса, а остальные подзапросы выполняются как обычно; если отклонены все подзапросы, запрос отвечает 400, и разбор по подзапросам остаётся в errors. В POST /v1/telephony-lines/batch иначе: нарушение контракта отбора отклоняет весь пакет одним 400 с номером подзапроса в сообщении, ни один подзапрос не выполняется. Само поле serverName осталось видимым в GET /v1/telephony-lines/fields с признаком «только для чтения», так что понять его назначение по-прежнему можно.

Что делать интеграторам

Если вы передавали serverName при создании или обновлении линии — уберите поле: значение всё равно никогда не сохранялось. Если полагались на сортировку, фильтр или смещение — ни одно из них никогда не применялось; список внешних линий приложения приходит целиком одной страницей, поэтому сортируйте, отбирайте и разбивайте его на страницы на своей стороне. Обычный список без этих параметров, а также limit и select, работают как раньше. Параллельная поддержка прежнего поведения не предусмотрена: прежнее поведение состояло в том, что параметр молча игнорировался, — сохранять было бы нечего.

NEW-0729-16: список хранилищ можно сортировать

Сортировка по полям хранилища работает на GET /v1/storages и в POST /v1/storages/search: ?sort=-id, ?order[name]=desc и те же значения в теле поиска. Минус перед именем поля означает убывание.

Порядок выдачи меняют id, name, entityType, entityId, rootFolderId — по каждому из них проверено на живом аккаунте, что по возрастанию и по убыванию приходят разные выдачи. Поля code и module метод тоже принимает без ошибки, но на проверенных аккаунтах значение этих колонок одинаково у всех хранилищ, поэтому порядок по ним не меняется — не полагайтесь на них как на сортировку.

Раньше любая сортировка хранилищ отклонялась с 400 INVALID_SORT_FIELD. Отказ был ошибочным: он был выведен из описания метода Битрикс24, а метод порядок сортировки принимает и применяет — это проверено на живом портале, где выдача по возрастанию, по убыванию и без сортировки различаются. Если вы обходили этот отказ, сортируя список у себя, обходной путь можно убрать; он продолжает работать.

Заодно порядок выдачи стал устойчивым. К вашей сортировке добавляется id последним ключом, а список без сортировки теперь приходит по возрастанию id — раньше он приходил в неопределённом порядке аккаунта. Это не косметика: список отдаётся страницами по 50, и при сортировке по неуникальному полю (например по названию) строка с тем же значением могла на границе страниц попасть в две страницы сразу или пропасть из выдачи вовсе. Теперь порядок полный и однозначный, поэтому и постраничный обход повторяем. Если вы сортировали по id сами, ваше направление сохраняется — второй ключ не добавляется.

Разбиение на страницы у хранилищ идёт постранично по 50 записей, поэтому при сортировке запрашивайте нужный объём одним вызовом (limit до 5000), а не листайте страницы вручную.

FIX-0729-17: перезапуск galaxy-приложения через /reboot

Было

Для galaxy-приложения (kind=GALAXY_APP) у POST /v1/infra/servers/:id/reboot не было рабочего сценария: вызов возвращал 422 VM_MISSING (у контейнера нет собственной виртуальной машины), а единственным путём восстановления зависшего приложения оставалось удаление — оно стирает постоянный том /data.

Стало

/reboot перезапускает контейнер приложения как «пинок» самовосстановления — постоянный том /data при этом сохраняется. Вызов принимается в статусе running или error и возвращает совещательный вердикт: restarted (контейнер перезапущен) и healthy (контейнер запустился и перестал перезапускаться — проверка контейнера, не HTTP-ответа приложения), а при healthy: false — поле hint о том, как залить исправленную версию. Перезапуск не снимает состояние ошибки — крашащееся приложение авторитетно чинится редеплоем исходников через POST /v1/infra/servers/:id/deploy. Новые коды ошибок для этого сценария: GALAXY_APP_REBOOT_USE_AGENT_CONTROLS (409 — приложением агента или бота управляют из его панели), GALAXY_APP_BUSY (409 — на хосте выполняется другая команда, в ответе Retry-After), GALAXY_HOST_UNREACHABLE (502 — хост недоступен). Перезагрузка обычного сервера не изменилась.

NEW-0729-18: totalDefault — умолчание по meta.total на самом ключе

У API-ключа появилась настройка totalDefault: заказывать ли подсчёт количества спискам этого ключа, когда сам запрос не передал withTotal. Значение true — присылать meta.total, false — не присылать, null — наследовать платформенное умолчание. По умолчанию у всех ключей null.

Настройка правится в кабинете на странице ключей и через PATCH /v1/keys/:id полем totalDefault (управляющий ключ vibe_live_). Перевыпуск ключа через POST /v1/keys/:id/rotate настройку сохраняет — как и режим доступа. Изменение попадает в журнал аудита.

Действующее значение видно в GET /v1/me — блок totalDefault показывает всю цепочку: key (настройка ключа), platform (платформенное умолчание), effective (что получится, если запрос не передаст withTotal) и source — откуда взялось действующее значение.

Настройка нужна там, где менять код интеграции дороже, чем один раз переключить ключ: она задаёт умолчание сразу всем спискам этого ключа. Точечно её перекрывает параметр запроса withTotal.

NEW-0729-19: meta.nextAfterId — курсор следующей страницы при сортировке по id

Ответы GET /v1/{entity} и POST /v1/{entity}/search получили необязательное поле meta.nextAfterId — идентификатор последней отданной записи, строкой.

Поле приходит, когда выполнены три условия сразу: у сущности числовой идентификатор и поддержка курсорного листания, сортировка запроса — строго id по возрастанию, и meta.hasMore равен true. На последней странице поля нет: идти уже некуда. Сегодня условиям отвечают сделки, лиды, контакты, компании, предложения и элементы смарт-процессов.

Значение передаётся обратно тем же фильтром, которым курсорное листание делалось и раньше: filter[>id]=<nextAfterId> при сортировке id по возрастанию. Нового параметра запроса не появилось — поле лишь избавляет от чтения идентификатора из последней строки ответа вручную.

Такое листание не зависит от смещения и не дорожает к концу коллекции, поэтому для обходов в десятки тысяч записей оно предпочтительнее, чем растущий offset.

NEW-0729-20: withTotal — списку можно не заказывать подсчёт количества

Списочные вызовы приняли необязательный параметр withTotal. У GET /v1/{entity} это параметр запроса ровно с двумя допустимыми значениями — true и false; у POST /v1/{entity}/search — поле тела с булевым значением. Любая другая запись читается как «параметр не передан», ошибки не будет.

withTotal=false — просьба не считать количество. Там, где платформа может её выполнить, подсчёт у Битрикс24 не заказывается и meta.total в ответе отсутствует. Там, где обойтись без подсчёта нельзя, параметр не действует и meta.total приходит как раньше. Поэтому наличие поля проверяйте, а не предполагайте.

Листать надо по meta.hasMore — он считается по полноте страницы и доводит цикл «читай, пока hasMore» до конца независимо от того, был ли подсчёт. При сортировке строго по id по возрастанию ответ дополнительно несёт meta.nextAfterId, который передаётся обратно в filter[>id].

Если параметр не передан, значение берётся из настройки ключа, а при её отсутствии — из платформенного умолчания. Действующее сейчас значение и всю эту цепочку показывает блок totalDefault в GET /v1/me.

Когда точное количество действительно нужно, спрашивайте его прямо: POST /v1/{entity}/aggregate с функцией count отдаёт число одним вызовом, без выгрузки записей. Считать количество постраничным обходом коллекции не надо — это десятки вызовов вместо одного и самый дорогой способ узнать одну цифру.

FIX-0729-21: meta.hasMore в списках считается по полноте страницы, а meta.total может отставать до минуты

Было

meta.hasMore в ответах GET /v1/{entity} и POST /v1/{entity}/search выводился из meta.total: «есть ещё» означало «offset плюс отданные строки меньше общего количества». Пока количество считалось на каждый вызов, это совпадало с правдой.

Стало

Платформа перестаёт запрашивать у Битрикс24 подсчёт количества на каждый повторный вызов с той же парой «ключ и запрос» — подсчёт непропорционально дорог для портала. Отсюда два наблюдаемых следствия.

meta.hasMore на таких ответах считается по полноте страницы: пришла полная страница — «возможно, есть ещё»; пришла неполная — список закончился. Цикл «читай, пока hasMore» по-прежнему доходит до конца всегда. Если коллекция ровно кратна limit, последний шаг вернёт пустой список — это штатный признак конца.

meta.total остаётся числом и остаётся на месте, но становится информативным: он может отставать до минуты, поэтому число отданных строк может оказаться больше него.

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

Менять ничего не нужно, если листание идёт по meta.hasMore — это рекомендованный способ. Если код опирается на meta.total как на точную границу цикла или проверяет «отдано не больше, чем total», переключитесь на meta.hasMore. Точное количество на момент запроса даёт POST /v1/{entity}/aggregate с функцией count.