# API changes: July 5, 2026

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

### FIX-0705-1: Chat message send — a clear error when the text is empty

The message text goes in the `message` field. Previously a call to [POST /v1/chats/{dialogId}/messages](/docs/chats/messages/send) that carried the text under an unknown field name (e.g. `{"text": "hi"}`) silently dropped that field, and Bitrix24 returned a `422 BITRIX_ERROR` about an empty message — even though content was supplied.

**Before**

`{"text": "hi"}` → `422 BITRIX_ERROR` about an empty message, with no hint at the cause.

**After**

The same call now returns `400 MESSAGE_REQUIRED` right away and lists the unrecognized field(s), pointing at the `message` field. Empty text is still allowed together with an `attach` block (attachment-only message).

**Impact on integrators**

Correct calls that use the `message` field are unchanged. The error for a wrong field name is now precise.

### FIX-0705-2: API key in the Authorization: Bearer header — a clear error instead of INVALID_SESSION

The API key goes in the `X-Api-Key` header. Previously, if the key was mistakenly placed in `Authorization: Bearer` (that slot is for an OAuth application session token), the server returned `401 INVALID_SESSION`, so integrators chased an OAuth-session problem when the real cause was the wrong header.

**Before**

A `vibe_app_*` key in `Authorization: Bearer` → `401 INVALID_SESSION`.

**After**

The same request now returns `401 WRONG_AUTH_SCHEME` with a hint: an OAuth application key (`vibe_app_*`) goes in `X-Api-Key`, and `Authorization: Bearer` carries the session token (`vibe_session_*`) from [POST /v1/oauth/token](/docs/keys-auth); a client that can only send Bearer can use a personal key (`vibe_api_*`). Session tokens and personal keys in Bearer are unaffected.

**Impact on integrators**

Correct calls with the key in `X-Api-Key` and the session in `Authorization: Bearer` are unchanged.

### FIX-0705-3: multipart/create rejects XSS-prone content types for PUBLIC objects

**Before**

For `PUBLIC` objects the content types `text/html`, `application/javascript`, `application/x-javascript` and `image/svg+xml` were rejected on direct and presigned uploads, but not when starting a multipart upload. Calling [POST /v1/storage/objects/multipart/create](/docs/storage) with `visibility` = `PUBLIC` and such a type created a session, and once completed the object was served inline in the browser.

**After**

[POST /v1/storage/objects/multipart/create](/docs/storage) with `visibility` = `PUBLIC` and an XSS-prone content type now returns `415 STORAGE_FORBIDDEN_CONTENT_TYPE`, the same as direct and presigned uploads. No multipart session is opened. `PRIVATE` objects still accept any content type.

**Impact on integrators**

Behavior now matches what the Storage reference documents: XSS-prone content types are not allowed for `PUBLIC` objects on any upload path. To multipart-upload such a file, use `visibility` = `PRIVATE` or a safe content type.

### FIX-0705-4: binding a placement to a server's technical URL now routes through the platform handler

**Before**

POST /v1/placements/bind accepted a `handler` pointing at the app's technical Black Hole URL (app-*.vibecode…) and registered it with Bitrix24 verbatim. Bitrix24 posted the placement iframe straight to that URL, bypassing the platform: no session was minted, and on a server restricted to Bitrix24 users the placement looped on the login gate (on a public server the app returned its own 404 error).

**After**

Such a `handler` is automatically rewritten to the app's platform handler (/v1/bitrix-handler) — the placement opens and authenticates normally. The response gains `handlerRewritten: true` and `requestedHandler` with the original value. External (non-Black Hole) handlers are left unchanged. If the app's platform handler cannot be resolved, the bind is rejected with code `PLATFORM_HANDLER_UNRESOLVABLE` instead of registering an unusable URL. Additionally GET /v1/placements flags an already-misbound handler: `data.handlers[].misbound: true` plus a textual `warnings[]`.

Additionally, if the placement is already registered in Bitrix24 but missing from the app's list (drift — e.g. after an unpublish that didn't unbind on Bitrix24), the bind no longer fails with "Handler already binded": the platform clears the stale binding and retries the request once, healing the drift. If the bind fails for another reason (for example a commercial Bitrix24 plan is required), the working placement is left in place.

### NEW-0705-5: New 402 error code ai_quota_exhausted on AI endpoints

When the account monthly AI quota control is active, [POST /v1/chat/completions](/docs/ai/chat/completions), [POST /v1/embeddings](/docs/ai/embeddings) and [POST /v1/audio/transcriptions](/docs/ai/audio/transcriptions) may return 402 with `{ success: false, error: { code: "ai_quota_exhausted", type: "insufficient_quota", reason, resetAt? } }`. The `reason` field distinguishes three cases: `breaker` — the hourly over-quota spending limiter fired, `wallet_empty` — the quota is exhausted and the account balance has no funds, `wallet_off` — over-quota usage is not available for this account. `resetAt` is when requests will pass again (may be absent for a permanently disabled account). While the account quota is not exhausted, endpoint behavior is unchanged.
