# API changes: July 7, 2026

[← Changelog](/docs/changelog) · [July 2026](/docs/changelog/2026-07)

### FIX-0707-1: smart-processes: linkedUserFields accepts Y/N and boolean values

**Before**

[POST /v1/smart-processes](/docs/entities/smart-processes/create) and [PATCH /v1/smart-processes/:entityTypeId](/docs/entities/smart-processes/update) with `linkedUserFields` only worked when the flag value was strictly `"true"`/`"false"`. A value in the `"Y"`/`"N"` convention (used by every other smart-process field) or a boolean `true`/`false` was silently ignored: the request returned `success: true`, but the display in the user field was not enabled.

**After**

`linkedUserFields` values are normalized the same way as the nested `relations[].isChildrenListEnabled` flag: `true`/`"Y"`/`"yes"`/`1` → enabled, `false`/`"N"`/`"no"`/`0` → disabled. Existing calls with `"true"`/`"false"` keep working unchanged.

**Impact on integrators**

No action needed — calls that previously "silently did nothing" with `"Y"` are now applied correctly.

### BC-0707-2: Order-card nested fields normalized

> Old format supported until: 06.01.2027

**Before**

`GET /v1/orders/{id}` returned the nested `clients`, `payments`, `basketItems` arrays in raw Bitrix24 shape: boolean fields as `"Y"` and `"N"` strings (`payments[].paid`, `clients[].isPrimary`, `basketItems[].vatIncluded`, and others), dates inside `payments` and `basketItems` with a `+03:00` offset, and `companyId` set to `0` when no company is bound. The `accountNumber` field was silently ignored on create and update.

**After**

Nested Y/N fields now arrive as boolean (`true` or `false`); nested dates are normalized to UTC (ending in `Z`); `companyId` is `null` when no company is bound instead of `0`; `accountNumber` became read-only — sending it on create or update returns `400` with code `READONLY_FIELD`.

**What integrators should do**

Read nested Y/N fields as boolean instead of comparing to the string `"Y"`; treat `null` instead of `0` as "no company bound"; stop sending `accountNumber` in the create and update body — the number is assigned automatically.

### FIX-0707-3: The /v1/openapi.json spec now matches actual runtime

**Before**

The machine OpenAPI spec was generated from static entity metadata and drifted from real responses: no field was marked nullable, nested order-card arrays were typed as a string, list methods were missing `filter` and `select`, and operations declared only success codes and `403`.

**After**

The spec now reflects the contract. Nullable fields are emitted as `type: ["<type>", "null"]`. Object and array-of-object fields are typed honestly, including the nested `clients`, `payments`, `basketItems`, `propertyValues` of `GET /v1/orders/{id}`. List methods declare the `filter` and `select` query params. Operations carry the standard error codes `400`, `401`, `404`, `422` in the single `{ success:false, error:{ code, message } }` envelope. `*Input` schemas declare create-required fields. Additionally `GET /v1/orders/fields` returns `clients` as an array instead of object. An SDK generated from the spec now types responses correctly.

### BC-0707-4: /search: auto-windowed search now returns the real Bitrix24 error on total failure

> Old format supported until: 07.09.2026

**Before**

Any failed auto-windowed `POST /v1/{entity}/search` returned `502 { "error": { "code": "WINDOWED_SEARCH_FAILED" } }` with a generic "add autoWindow:false".

**After**

The response matches the same query at a narrow range — the real code and message: a rejected filter/sort field → `400 UNKNOWN_FILTER_FIELD` / `400 INVALID_PARAMS`; no access → `403`; request limit / queue overload → `429` + `Retry-After`; timeout → `503`; Bitrix24 unavailable → `502 BITRIX_UNAVAILABLE`. Partial window failure (status `200`) now carries `meta.windowErrorSample` `{ code, message }`.

**What integrators must do**

If you branched on `error.code === "WINDOWED_SEARCH_FAILED"` (e.g. to retry with `autoWindow:false`) — branch on the real codes instead. The `autoWindow:false` workaround remains; it is useful where it actually helps (the `429 QUEUE_TIMEOUT` hint names it). The total-failure response no longer carries the `meta` block (`autoWindowed`/`windowCount`/`windowErrors`) — the signal is now in the error code/message itself; `meta.windowErrorSample` remains on partial failure (status `200`).

### FIX-0707-5: lists on an account without the module return 409 consistently, not 429

**Before**

On an account where the Universal Lists module is disabled, calls to [/v1/lists](/docs/lists) returned the clear `409 LISTS_MODULE_NOT_ENABLED` only for the first few requests. After that the built-in error-loop protection tripped and every subsequent call returned `429 ERROR_LOOP_DETECTED`, hiding the real cause (the module is not installed).

**After**

The "method unavailable on this account" signal is no longer counted by the error-loop protection, so `lists.*` calls on a module-off account return `409 LISTS_MODULE_NOT_ENABLED` consistently no matter how many times they repeat. The response stays actionable: enable the module in the account and retry.

### NEW-0707-6: error.hint on the 400 for a server create missing source and provider/plan/region

[POST /v1/infra/servers](/docs/infra/servers/create), when rejected with `400 INVALID_REQUEST` because `provider`/`plan`/`region` are missing (and no `source` was passed) on a galaxy-placement account, now additionally returns an `error.hint` object with `reason` (why the request was rejected on this account), `recovery` (the recommended one-shot path plus the working two-step alternative) and `example` (a paste-ready one-shot body skeleton). `error.code` and `error.message` are unchanged — the hint is strictly additive; accounts without galaxy placement get the previous response, without `hint`.

The hint is also returned on `400 RUNTIME_PARAM_REMOVED` (a create with `runtime` but no `source` on a galaxy-placement account), and a body carrying `placement: "dedicated"` gets a separate hint variant — for a dedicated server, keeping the intent and adding the missing `provider`/`plan`/`region` tuple, instead of steering the caller into a galaxy container.

### FIX-0707-7: Galaxy deploy checklist in /v1/me now matches the actual contract

**Before**: step 2 of `deployment.galaxyApp.checklist` instructed [POST /v1/infra/servers](/docs/infra/servers/create) `{ name }` with no `source` and no `provider`/`plan`/`region` — that call always failed with `400 INVALID_REQUEST`. The CREATE rule did not explain that the two-step path requires the full `provider`/`plan`/`region` tuple, and `newAppPlacement.note` promised a dedicated standalone VM where the create actually returns a galaxy slot with `next: "deploy"`. The favicon guide pointed to the same broken order; the never-deployed-slot reap window was stated as "~12-20 min" versus the actual ~20-25.

**After**: the recommended path is a single call — `POST /v1/infra/servers { name, source, runtime, start }` (omit `provider`/`plan`/`region`). The two-step path is documented truthfully: a create without `source` requires the full `provider`/`plan`/`region` tuple (values are informational for a galaxy — the app inherits its host), and on galaxy placement returns a slot with `next: "deploy"`; a never-deployed slot is reaped to ERROR after ~20 min (swept every 5 min). Favicon: the primary path is a self-hosted `/icon.svg` inside the archive (no id needed); the platform-hosted URL remains an alternative via the two-step order or a re-deploy. The status-polling step gained a `status=error` branch → `provisionError`/`buildLog` → re-deploy.

**Impact on integrators**: agents following the checklist now deploy on the first call. Endpoint behavior is unchanged — only the `/v1/me` texts and the `/v1/openapi.json` description were updated; existing integrations keep working as is.

### NEW-0707-8: Company AI quota is available via the API

The new [GET /v1/ai/quota](/docs/ai/consumption/quota) endpoint returns the monthly AI quota state of your Bitrix24 account: the percentage of the limit consumed (`pctUsed`, an honest value — above 100 on overspend), the exhaustion flag (`exhausted`), the reset date (`resetAt`, a rolling 30-day window), and a per-model breakdown — request counts, tokens, and each model's share of the monthly limit (`byModel[].pctOfLimit`). Absolute limit values in Vibe credits are not exposed — percentages only, same as the dashboard. Requires the `vibe:ai` scope.
