#Error codes

Reference of Vibe API error codes: the unified response format, the full code list, common causes, and how to fix them. Applies to all /v1/... endpoints.

#Error response format

Every error response is returned in a unified shape. error is an object.

JSON
{
  "success": false,
  "error": {
    "code": "ENTITY_NOT_FOUND",
    "message": "Item not found"
  }
}
Field Type Req. Description
success boolean yes Always false on error
error.code string yes Machine-readable error code. Use it to distinguish error types in client code
error.message string yes Error description in the backend language (Russian or English)
error.hint string no Developer hint: what to try next, which limits to watch
error.userMessage string no Message for the end user in Russian. Appears in billing, infrastructure, and tariff errors
error.warning string no Appears on repeated identical errors on the same key. A signal that the client code most likely has a bug
error.retryAfter number no Seconds until the next attempt. Appears in 429 (rate-limit) and 504 (queue timeout)

Example response with extra fields (429 RATE_LIMITED):

JSON
{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "QUERY_LIMIT_EXCEEDED",
    "hint": "Wait 1-2 seconds and retry. Use POST /v1/batch to combine up to 50 calls in 1 request.",
    "retryAfter": 2
  }
}

The Retry-After HTTP header mirrors the error.retryAfter value for compatibility with the HTTP standard.

#Code summary table

The table covers the core codes that appear on any Vibe API endpoint. Domain codes (BOT_NOT_FOUND, SERVER_NOT_RUNNING, AGENT_LIMIT_REACHED, and similar) are documented on the pages of their respective sections.

#Authorization and keys

Code HTTP When it occurs
MISSING_API_KEY 401 Request without the X-Api-Key header
INVALID_API_KEY 401 Key not found in the system or its format is unrecognized
INVALID_APP_KEY 401 A vibe_app_* key was passed without an accompanying Authorization: Bearer ...
TOKEN_EXPIRED 401 The OAuth token has expired
TOKEN_REFRESH_FAILED 401 Failed to refresh the OAuth token on the Bitrix24 side
WRONG_KEY_TYPE 401 The key type does not match the endpoint: e.g. a management key on an entity route

#Permissions and scopes

Code HTTP When it occurs
SCOPE_DENIED 403 The key lacks the required scope (e.g. crm for deals, imbot for bots)
WRITE_BLOCKED_READONLY_KEY 403 The key is in read-only mode, but the call performs a write
INFRA_FORBIDDEN_FOR_COWORK_KEY 403 A Co-work subscription key (vibe:cowork) — data-plane only: provision / deploy / exec / upload / server lifecycle and catalog publishing are forbidden. Use a project key with deploy permissions
INFRA_SCOPE_REQUIRED 403 The key lacks the vibe:infra scope — infrastructure management is unavailable. Add the scope or use a key with infrastructure permissions
KEY_POLICY_READONLY_REQUIRED 403 The portal policy allows regular users to create read-only keys only — issuing a write-enabled key was rejected
MANAGEMENT_KEY_READ_ONLY 403 A management key without write permissions attempts a POST/PATCH/DELETE
MANAGEMENT_KEY_NO_ENTITY_ACCESS 403 A management key accesses an entity endpoint — an APP key with the required scope is needed
BITRIX_ACCESS_DENIED 403 Bitrix24 returned ACCESS_DENIED: the user lacks permission for the operation or entity
OAUTH_REQUIRED 403 The endpoint requires a user context — a vibe_app_* key + Bearer token is needed
WAITLIST_PENDING 403 The account is awaiting waitlist activation

#Request validation

Code HTTP When it occurs
VALIDATION_ERROR 400 The body or query failed schema validation. message contains per-field details
INVALID_PARAMS 400 Bitrix24 returned INVALID_PARAMS or the route handler found an invalid parameter value
INVALID_REQUEST 400 The request structure does not match the required one (e.g. calls in /v1/batch is an empty array or contains more than 50 elements)
MISSING_PARAMS 400 A required parameter explicitly listed in the endpoint schema was not passed
MISSING_REQUIRED_FILTER 400 A required filter for list endpoints that need context (timelines, task-comments, and similar) was not passed
INVALID_FILTER_FIELD 400 Filter on a non-existent entity field
UNKNOWN_SORT_FIELD 400 Sort on a non-existent field (for entities whose sort validator is active)
BATCH_LIMIT_EXCEEDED 400 The request contains more than 50 elements in a bulk operation (vipchats, task-comments, and similar)
INVALID_EVENT 400 The portal event subscription code does not match the ^[A-Z][A-Z0-9_]+$ format. See Portal event subscriptions
INVALID_APP_PATH 400 The appPath delivery path does not start with / or contains control characters. See Portal event subscriptions

#Portal event subscription preconditions

Code HTTP When it occurs
NOT_OAUTH_APP 400 The server is not backed by an OAuth app with an application_token — an event subscription cannot be registered
NO_USER_TOKEN 400 The app has no OAuth token — authorize the app on the portal first

The full operation reference — Portal event subscriptions.

#Resource not found

Code HTTP When it occurs
ENTITY_NOT_FOUND 404 A CRM entity record with the given id does not exist. The canonical code for /v1/deals/:id, /v1/contacts/:id, and similar
NOT_FOUND 404 GET /:id only: Bitrix24 returned success, but result is empty (applies to several smart methods)

Domain *_NOT_FOUND codes (BOT_NOT_FOUND, SERVER_NOT_FOUND, AGENT_NOT_FOUND, PORTAL_NOT_FOUND, USER_NOT_FOUND, FILE_NOT_FOUND, SUBSCRIPTION_NOT_FOUND) are documented on the pages of their respective sections.

#State conflicts

Code HTTP When it occurs
CONFLICT 409 The current resource state is incompatible with the request
ALREADY_EXISTS 409 A record with these key fields already exists
EVENT_BOUND_ELSEWHERE 409 A portal event is already bound to another server of the same OAuth app. See Portal event subscriptions

#Billing and tariff

Occur on endpoints that create and wake infrastructure (servers, agents, managed bots). The response includes userMessage in Russian for display in the client interface.

Code HTTP When it occurs
BILLING_EXHAUSTED 402 The balance dropped into the red zone, the account is frozen. A top-up is required
ACCOUNT_FROZEN 402 The billing account is frozen for other reasons
COMMERCIAL_PLAN_REQUIRED 402 Free Bitrix24 plan, the trial period is unavailable or already used
TRIAL_EXPIRED 402 The 14-day trial period has ended
TRIAL_PORTAL_LIMIT 402 The overall per-portal server limit was exceeded during the trial period
TRIAL_USER_LIMIT 402 The per-user server limit was exceeded during the trial period
PLAN_NOT_ALLOWED_ON_TRIAL 402 The requested server/agent plan is unavailable during the trial period
SERVER_WAKE_BLOCKED 403 Server wake-up is blocked for a non-billing reason

#Rate limiting

Code HTTP When it occurs
RATE_LIMITED 429 Bitrix24 throttled the request rate or an internal limit was exceeded. The response includes retryAfter (seconds) and the Retry-After header
ERROR_LOOP_DETECTED 429 A VibeCode-side block: the same request repeats with the same error. A signal of a bug in the client code. Every Nth request is forwarded onward to check for recovery

#Backend and third-party services

Code HTTP When it occurs
BITRIX_ERROR 422 Bitrix24 returned a business error that does not fall under narrower categories (ACCESS_DENIED, NOT_FOUND, INVALID_PARAMS)
BITRIX_UNAVAILABLE 502 Bitrix24 returned 5xx or did not respond in time
BIND_FAILED 502 Bitrix24 rejected the event registration (event.bind) — for example, the portal is not on a commercial plan. See Portal event subscriptions
WINDOWED_SEARCH_FAILED 502 All sub-windows of /search failed
QUEUE_TIMEOUT 504 The portal queue is saturated: more than 30 seconds of waiting on the VibeCode side
INTERNAL_ERROR 500 An unexpected VibeCode backend error

#Detailed reference

#`MISSING_API_KEY` (401)

The request does not contain the X-Api-Key header.

JSON
{
  "success": false,
  "error": {
    "code": "MISSING_API_KEY",
    "message": "API key required. Pass via X-Api-Key header."
  }
}

Causes:

  • The X-Api-Key or Authorization header was not passed.
  • The header was passed with an empty value.

Fix:

  • Add the X-Api-Key: vibe_api_... or X-Api-Key: vibe_app_... header.
  • Verify that the environment variable holding the key is set correctly (for CLI tools and SDKs).

#`INVALID_API_KEY` (401)

The passed key does not exist or its format is unrecognized.

JSON
{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid API key"
  }
}

Causes:

  • A typo or extra whitespace in the key.
  • The key was deleted by its owner or an administrator.
  • The key is from a different environment (staging/production).
  • The prefix is not among the supported ones: vibe_api_, vibe_app_, vibe_live_, vibe_mgmt_.

Fix:

  • Check the key in your account on the /keys page.
  • Create a new key if the old one was deleted.

#`SCOPE_DENIED` (403)

The key lacks the required scope for the requested operation.

JSON
{
  "success": false,
  "error": {
    "code": "SCOPE_DENIED",
    "message": "This endpoint requires 'crm' scope"
  }
}

Causes:

  • CRM entities require the crm scope, tasks require task, the bot platform requires imbot, the AI Router requires vibe:ai, infrastructure requires vibe:infra.
  • The key's scope was narrowed at creation time.

Fix:

  • Open the key page in your account and issue a new key with a broader scope set.
  • The full list of scopes and their purpose is on the Keys and authorization page.

#`BITRIX_ACCESS_DENIED` (403)

Bitrix24 returned ACCESS_DENIED: the user or app lacks permission for the entity or operation.

JSON
{
  "success": false,
  "error": {
    "code": "BITRIX_ACCESS_DENIED",
    "message": "ACCESS_DENIED"
  }
}

Causes:

  • The user lacks permission for the entity in CRM (e.g. another user's deal with restricted visibility).
  • The Bitrix24 app does not have the required scope in its portal permissions.
  • The requested module is disabled on the portal (no CRM, no bot platform, and so on).

Fix:

  • Check the user's permissions in the Bitrix24 entity card.
  • Open the app settings on the portal and broaden the permission set.

#`WRITE_BLOCKED_READONLY_KEY` (403)

The key is in read-only mode (accessMode: "READONLY"), but the request performs a write. The full description of the mode, switching, and portal policy is in Access mode.

JSON
{
  "success": false,
  "error": {
    "code": "WRITE_BLOCKED_READONLY_KEY",
    "message": "Key is in read-only mode. Switch to read+write in /keys to enable writes.",
    "details": {
      "method": "crm.item.add",
      "keyName": "MCP key",
      "currentMode": "READONLY",
      "switchUrl": "/keys"
    }
  }
}

details fields:

Field When returned Description
method Only when proxying to Bitrix24 The Bitrix24 method name that would have been called on a successful write (e.g. crm.item.add). Not returned for management keys — the block is based on the request's HTTP method
keyName Always The key name from your account. If the key has no name — "unnamed" is returned
currentMode Always The key's effective mode — always "READONLY" for this error
switchUrl Always The path to the account page where the mode is switched — "/keys"

Causes:

  • An API key or authorization key (vibe_api_, vibe_app_) in READONLY mode made a call that proxies to Bitrix24 as a write operation: create, update, delete, an action on an entity.
  • A management key (vibe_live_) in READONLY mode made a request with the POST, PATCH, PUT, or DELETE HTTP method — e.g. an attempt to create a key via POST /v1/keys or delete a feedback record.

Fix:

  • The key owner — open API keys, in the relevant key's card under the Access mode block select "Read and write" and save. The mode applies to the next request, no reissue needed.
  • If the toggle in the card is unavailable — the portal administrator restricted the mode. Ask the administrator to lift the restriction for this key.
  • When working through an AI agent — the effective mode is returned by GET /v1/me in the data.accessMode field. If writes are needed permanently, issue a separate key in "read and write" mode.

#`VALIDATION_ERROR` (400)

The body or query parameters failed schema validation. Most V1 endpoints validate via Zod, so the message lists the specific problematic fields.

JSON
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "fields.title: Required; fields.stageId: Expected string, received number"
  }
}

Causes:

  • Required fields are missing.
  • A value's type does not match the schema (string instead of number, wrong date format).
  • The request body is invalid JSON or the Content-Type: application/json header is missing.

Fix:

  • Check the body against the endpoint schema in the entity reference or on the specific endpoint page.
  • Pass numbers without quotes, dates in ISO 8601 format (2026-04-29T10:00:00).
  • Add the Content-Type: application/json header.

#`INVALID_PARAMS` (400)

Bitrix24 or the route handler found an invalid parameter value. Often appears as a wrapper for Bitrix24 errors of the form INVALID_PARAMS: ....

JSON
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMS",
    "message": "Path parameter :id must be a positive integer"
  }
}

Causes:

  • An invalid path parameter value (e.g. a non-number where a number is expected).
  • Bitrix24 rejected a request parameter (e.g. an unsupported enum field value).

Fix:

  • Check the endpoint page: which values are valid for each parameter.
  • For filter, use the field list from GET /v1/<entity>/fields.

#`MISSING_REQUIRED_FILTER` (400)

A required filter was not passed on a list endpoint that needs context.

JSON
{
  "success": false,
  "error": {
    "code": "MISSING_REQUIRED_FILTER",
    "message": "filter[entityType] and filter[entityId] are required for /v1/timelines"
  }
}

Causes:

  • The list of timeline records, comments, and other "nested" data requires a pair of parent identifiers.

Fix:

  • Add the required filter parameters listed in message or on the endpoint page.

#`BATCH_LIMIT_EXCEEDED` (400)

The request exceeds the element limit in a bulk operation. At the /v1/batch level the limit is checked via INVALID_REQUEST referencing Array must contain at most 50 element(s). On domain bulk endpoints (e.g. chats, task-comments) — a dedicated BATCH_LIMIT_EXCEEDED code.

JSON
{
  "success": false,
  "error": {
    "code": "BATCH_LIMIT_EXCEEDED",
    "message": "Maximum 50 dialogs per bulk request (Bitrix24 batch limit)."
  }
}

Causes:

  • The array has more than 50 elements.

Fix:

  • Split the operation into several requests of 50 elements each.
  • Use batch requests for sequential calls from a single key.

#`ENTITY_NOT_FOUND` (404)

A CRM entity record with the given id does not exist or was deleted.

JSON
{
  "success": false,
  "error": {
    "code": "ENTITY_NOT_FOUND",
    "message": "Item not found"
  }
}

Causes:

  • A record with this id genuinely does not exist.
  • The record was deleted by a concurrent process.
  • The wrong entity: the request goes to /v1/deals/:id, but the ID is from a lead.

Fix:

  • Check that the record exists via the entity's list endpoint.
  • Restore it from the Bitrix24 recycle bin if it was deleted recently (via the portal interface).

#`RATE_LIMITED` (429)

Bitrix24 throttled the request rate or an internal VibeCode limit was triggered.

JSON
{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "QUERY_LIMIT_EXCEEDED",
    "hint": "Wait 1-2 seconds and retry. Use POST /v1/batch to combine up to 50 calls in 1 request.",
    "retryAfter": 2
  }
}

Response headers:

Retry-After: 2

Causes:

  • Too many concurrent requests from a single key piled up.
  • The requests-per-second limit on the Bitrix24 side was exceeded.

Fix:

  • Wait for the time from error.retryAfter or the Retry-After header.
  • Implement retries with exponential backoff.
  • Combine up to 50 calls via `POST /v1/batch`.
  • Cache rarely changing reference data (fields, statuses, currencies).

#`ERROR_LOOP_DETECTED` (429)

VibeCode detects a series of identical errors on a single key for a single Bitrix24 method — and temporarily blocks the request on the VibeCode side to avoid inflating Bitrix24 counters. Every Nth request is forwarded onward: if the backend recovered, the block is lifted automatically.

JSON
{
  "success": false,
  "error": {
    "code": "ERROR_LOOP_DETECTED",
    "message": "Vibe-side block (not a Bitrix24 limit). 12 failures on crm.deal.list in the last hour.",
    "hint": "Every 50th request will probe for recovery — keep retrying with backoff. If you suspect a platform-side issue (e.g. 5xx during an outage), platform admin can clear the block via POST /api/platform/analytics/circuit-breaker/<apiKeyId>/clear.",
    "retryAfter": 60
  }
}

Causes:

  • A bug in the client code: a request with identical parameters repeats and consistently fails.
  • A platform outage on the Bitrix24 side during the burst of requests.

Fix:

  • Read message — it names the specific method with the error series.
  • Find the request's source in the code, fix the parameters or logic.
  • During a platform outage — the platform administrator can clear the block via POST /api/platform/analytics/circuit-breaker/<apiKeyId>/clear.

#`BILLING_EXHAUSTED` (402)

The billing account dropped into the red zone: the balance is negative, the grace period is exhausted. Requests to create and wake infrastructure are blocked until a top-up.

JSON
{
  "success": false,
  "error": {
    "code": "BILLING_EXHAUSTED",
    "message": "Account is frozen due to negative balance.",
    "userMessage": "The billing account is frozen due to a negative balance. Top up the account to continue working with servers and agents.",
    "hint": "Top up the account at /billing/topup, then call POST /v1/portals/:id/refresh-tariff."
  }
}

Causes:

  • The balance has no funds to cover the hourly cost of servers and agents.

Fix:

  • Top up the balance on the /billing/topup page.
  • After topping up, call POST /v1/portals/:id/refresh-tariff to update the status.

#`COMMERCIAL_PLAN_REQUIRED` (402)

Creating servers and agents is unavailable on the free Bitrix24 plan after the trial period ends.

JSON
{
  "success": false,
  "error": {
    "code": "COMMERCIAL_PLAN_REQUIRED",
    "message": "Commercial Bitrix24 plan required for infrastructure operations.",
    "userMessage": "Infrastructure creation is available on commercial Bitrix24 plans. The trial period has already been used.",
    "hint": "Upgrade plan at https://www.bitrix24.ru/prices/, then call POST /v1/portals/:id/refresh-tariff."
  }
}

Fix:

  • Upgrade the Bitrix24 plan to a commercial one.
  • After upgrading, call POST /v1/portals/:id/refresh-tariff or GET /v1/me?refresh=tariff.

#`TRIAL_EXPIRED` (402)

The 14-day trial period has ended, the plan remained free.

JSON
{
  "success": false,
  "error": {
    "code": "TRIAL_EXPIRED",
    "message": "Trial period has ended.",
    "userMessage": "The trial period (14 days) has ended. Switch to a commercial Bitrix24 plan to keep using servers and agents."
  }
}

Fix:

  • Switch to a commercial Bitrix24 plan.
  • Update the status via POST /v1/portals/:id/refresh-tariff.

#`BITRIX_ERROR` (422)

Bitrix24 returned a business error that does not fall under narrower categories (ACCESS_DENIED, NOT_FOUND, INVALID_PARAMS, RATE_LIMITED).

JSON
{
  "success": false,
  "error": {
    "code": "BITRIX_ERROR",
    "message": "Invalid filter: field 'NON_EXISTENT_FIELD' is not allowed in filter",
    "hint": "Known Bitrix24 limitation: …"
  }
}

Causes:

  • Bitrix24 rejected the operation for a business reason: incompatible state, unsupported value, business rule.
  • The request runs on a portal where the corresponding module is disabled.

Fix:

  • Read message — it holds the original error text from Bitrix24.
  • If hint is present — use it as the first diagnostic step.

#`BITRIX_UNAVAILABLE` (502)

Bitrix24 returned 5xx or did not respond within the allotted time.

JSON
{
  "success": false,
  "error": {
    "code": "BITRIX_UNAVAILABLE",
    "message": "Bitrix24 returned 503 Service Unavailable"
  }
}

Causes:

  • Maintenance or overload on the Bitrix24 side.
  • Network problems between VibeCode and the portal.

Fix:

  • Retry the request after a few minutes, implementing retries with exponential backoff.
  • Check the portal status at /bitrix/admin/site_checker.php (for the portal administrator).

#`QUEUE_TIMEOUT` (504)

The request queue to Bitrix24 for a specific portal is saturated: more than 30 seconds of waiting.

JSON
{
  "success": false,
  "error": {
    "code": "QUEUE_TIMEOUT",
    "message": "Portal queue saturated — too many concurrent Bitrix24 calls",
    "userMessage": "Requests to Bitrix24 have been queued for over 30 seconds. The portal likely has many concurrent operations.",
    "hint": "If this is a /search request with a wide date range, try adding \"autoWindow\": false OR narrow the date range to <14 days. See /v1/guide for optimization tips.",
    "retryAfter": 10
  }
}

Fix:

  • Reduce concurrency, return a retry to the client after retryAfter seconds.
  • For /search endpoints — narrow the date range or pass autoWindow: false.
  • Combine calls via POST /v1/batch.

#`INTERNAL_ERROR` (500)

An unexpected error on the Vibe API side.

JSON
{
  "success": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Internal server error"
  }
}

Fix:

  • Retry the request.
  • If the error reproduces consistently — submit a ticket via POST /v1/feedback with the request time. The X-Request-Id header from the response speeds up diagnosis.

#Error handling in code

#JavaScript

javascript
async function vibeRequest(url, options = {}) {
  const response = await fetch(url, {
    ...options,
    headers: {
      'X-Api-Key': process.env.VIBE_API_KEY,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  const data = await response.json();

  if (!data.success) {
    const { code, message, retryAfter } = data.error;

    switch (code) {
      case 'RATE_LIMITED':
      case 'QUEUE_TIMEOUT': {
        const wait = retryAfter ?? Number(response.headers.get('Retry-After')) ?? 1;
        await new Promise(r => setTimeout(r, wait * 1000));
        return vibeRequest(url, options);
      }

      case 'BITRIX_UNAVAILABLE':
        await new Promise(r => setTimeout(r, 5000));
        return vibeRequest(url, options);

      case 'MISSING_API_KEY':
      case 'INVALID_API_KEY':
        throw new Error('Check the API key');

      default:
        throw new Error(`${code}: ${message}`);
    }
  }

  return data;
}

#Python

Python
import os
import time
import requests

def vibe_request(url, method="GET", json_data=None):
    headers = {
        "X-Api-Key": os.environ["VIBE_API_KEY"],
        "Content-Type": "application/json",
    }

    response = requests.request(method, url, headers=headers, json=json_data)
    data = response.json()

    if not data.get("success"):
        err = data.get("error", {})
        code = err.get("code")
        message = err.get("message")
        retry_after = err.get("retryAfter") or int(response.headers.get("Retry-After", 1))

        if code in ("RATE_LIMITED", "QUEUE_TIMEOUT"):
            time.sleep(retry_after)
            return vibe_request(url, method, json_data)

        if code == "BITRIX_UNAVAILABLE":
            time.sleep(5)
            return vibe_request(url, method, json_data)

        raise Exception(f"{code}: {message}")

    return data

#PHP

php
function vibeRequest(string $url, string $method = 'GET', ?array $data = null): array {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_HTTPHEADER => [
            'X-Api-Key: ' . getenv('VIBE_API_KEY'),
            'Content-Type: application/json',
        ],
    ]);
    if ($data !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    }
    $body = json_decode(curl_exec($ch), true);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($body === null) {
        throw new Exception("HTTP error: $httpCode");
    }

    if (empty($body['success'])) {
        $errCode = $body['error']['code'] ?? 'UNKNOWN';
        $errMsg = $body['error']['message'] ?? 'Unknown error';
        $retryAfter = $body['error']['retryAfter'] ?? 1;

        if (in_array($errCode, ['RATE_LIMITED', 'QUEUE_TIMEOUT'], true)) {
            sleep((int) $retryAfter);
            return vibeRequest($url, $method, $data);
        }

        if ($errCode === 'BITRIX_UNAVAILABLE') {
            sleep(5);
            return vibeRequest($url, $method, $data);
        }

        throw new Exception("$errCode: $errMsg");
    }

    return $body;
}

#See also