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

Внешний API приложения

Внешний API открывает HTTP-контур приложения тем, у кого есть ключ: внешняя система или другое приложение отправляет запрос платформе, платформа передаёт его приложению и возвращает ответ как есть.

Базовый URL: https://vibecode.bitrix24.tech/v1 | Авторизация: X-Api-Key | Ключ: ключ внешнего API приложения

Когда это нужно | Что включить | Ключ | Адрес и методы | Что получает приложение | Что возвращается вызывающему | Примеры | Приложение, которое зовут автоматизации | Ограничения | Ошибки

Когда это нужно

Канал решает две задачи.

Внешняя система вызывает приложение. Интегратор или сервис заказчика получает ключ внешнего API и обращается к маршрутам приложения напрямую: платформа доставляет запрос приложению и возвращает ответ вызывающему как есть.

Приложение вызывает другое приложение. Двум приложениям одного портала не нужно договариваться о сетевом доступе и придумывать собственную аутентификацию: вызывающее приложение отправляет запрос по адресу платформы с ключом вызываемого.

Во внутренний контур портала канал не ходит: это ровно проброс до вашего кода. Запросы к данным Битрикс24 по-прежнему идут через Entity API.

Что включить на стороне приложения

Канал работает при трёх условиях.

  1. Переключатель «Внешний API» включён в карточке приложения. Он стоит в карточке приложения на платформе и принимает значения «Включён» и «Выключен». Управлять им может тот, кто управляет приложением.
  2. У приложения есть сервер. Без сервера включить переключатель нельзя, а вызов возвращает 409 APP_API_NO_SERVER.
  3. Сервер не засыпает. Автоматический сон отключён, тариф сервера не вытесняемый, окна пробуждения не заданы. Иначе переключатель не включится, а вызов вернёт 409 APP_API_NOT_ALWAYS_ON. Как отключить авто-сон — Настроить авто-сон. У galaxy-приложения этот метод отвечает 400 GALAXY_APP_USE_GALAXY_ROUTE — таймер сна ему через API не задаётся, отключите авто-сон в кабинете, в карточке сервера приложения.

Важно: включённый переключатель публикует весь HTTP-контур приложения держателям ключа. Отбора маршрутов на стороне платформы нет — держатель ключа может обратиться к любому пути приложения. Аутентификацию и разграничение доступа своих маршрутов приложение делает само: платформа оставляет для этого свободным заголовок Authorization и передаёт его приложению без изменений.

Выключить переключатель можно всегда и без условий. Выключение сразу закрывает канал для всех выданных ключей.

Ключ

Внешний API принимает только ключ, выпущенный для этого приложения. Личный API-ключ и ключ авторизации приложения сюда не подходят: они получают 403 APP_API_NOT_GRANTED. Обратное тоже верно — ключ внешнего API не открывает остальные адреса платформы, попытка вызвать ими что-то ещё возвращает 403 APP_API_KEY_OUT_OF_SCOPE.

Где взять. Карточка приложения на платформе, блок «Внешний API», кнопка «Получить ключ внешнего API». Секрет показывается один раз — платформа не отдаёт его повторно.

Кто может выпустить. Владелец приложения либо администратор портала. Владельцем выпущенного ключа в обоих случаях становится владелец приложения — он же платит за вызовы.

Сколько ключей. До 10 действующих ключей на приложение. Одиннадцатый выпуск возвращает 409 APP_API_KEY_LIMIT с полями limit и used. Отзыв ключа освобождает место сразу. Срока жизни у ключа нет — он действует до отзыва, до выключения переключателя или до смены владельца приложения.

Смена владельца приложения отзывает все ключи внешнего API. Передали приложение другому человеку — выпустите ключи заново и обновите их в роботах и вызывающих приложениях.

Ключ передаётся одним из двух заголовков, оба равноправны:

X-Api-Key: YOUR_APP_EXTERNAL_API_KEY
Authorization: Bearer YOUR_APP_EXTERNAL_API_KEY

Вторая форма нужна клиентам, которые умеют только Authorization. Если приложению нужен собственный Authorization, передавайте ключ платформы через X-Api-Key — тогда заголовок Authorization доедет до приложения нетронутым.

Адрес и методы

METHOD https://vibecode.bitrix24.tech/v1/applications/:applicationId/api/<путь в приложении>

Принимаются GET, POST, PUT, PATCH, DELETE и HEAD. Метод доезжает до приложения тем же, каким пришёл.

Параметр Тип Обяз. Описание
applicationId (path) string да Идентификатор приложения. Список: GET /v1/applications
<путь в приложении> (path) string нет Всё, что идёт после /api/, приложение получает как свой путь запроса. Пустой путь означает корень приложения. Предел длины — 2048 символов
строка запроса string нет Передаётся приложению дословно, платформа её не разбирает

Что отбивается в пути. Путь проверяется и в исходном виде, и в однократно раскодированном, поэтому обход через процентное кодирование не проходит. Ответ 400 APP_API_BAD_PATH дают:

  • сегмент . или .. в любом месте, в том числе с параметром сегмента после точки с запятой
  • путь, начинающийся с // или с /@
  • обратный слэш
  • абсолютный адрес вместо пути
  • управляющие символы
  • битое процентное кодирование
  • символы вне набора, пригодного для пути, — их надо кодировать

Точки внутри сегмента законны: /files/report..v2 проезжает.

Что отбивается в строке запроса. Действует один набор допустимых символов по RFC 3986. Внутри значения параметра законны .., // и ;, а обратный слэш, пробел и всё за пределами набора обязаны приезжать в процентном кодировании. Иначе — тот же 400 APP_API_BAD_PATH.

Что получает приложение

Приложение получает исходный метод, путь после /api/, строку запроса и тело запроса без изменений.

К ним платформа добавляет заголовки, которые приложение не может получить ни от кого другого — входящие заголовки с префиксом X-Vibe- срезаются, поэтому подделать их вызывающий не может.

Заголовок Что несёт
X-Vibe-Request-Id Идентификатор вызова. Тот же идентификатор платформа пишет в свой журнал — называйте его в обращении в поддержку
X-Vibe-Caller-Kind Вид вызывающего: external-api — вызов через этот канал, contract — вызов метода по контракту решения, у долгого метода он описан на странице Отправить результат долгого метода. Это и есть признак, по которому приложение отличает внешний вызов от открытия в интерфейсе Битрикс24
X-Vibe-Caller-Portal-Id Портал, которому принадлежит ключ
X-Vibe-Caller-Key-Id Идентификатор ключа, которым сделан вызов. Годится для журналирования и для разграничения доступа внутри приложения

Важно: заголовков X-Vibe-User-Id, X-Vibe-User-Name, X-Vibe-User-Role и X-Vibe-Authorization на этом пути нет. Они появляются только тогда, когда приложение открывает человек через интерфейс Битрикс24 и у него есть сессия — Что приходит в приложение. Внешний вызов сессии не несёт, поэтому пользовательская личность приложению недоступна: известен портал и ключ, но не сотрудник. Код, написанный по странице «Что приходит в приложение», на внешнем вызове увидит анонимного посетителя — читайте X-Vibe-Caller-Kind и расходитесь по ветвям.

Что срезается из запроса. Ключ платформы (X-Api-Key), Cookie, служебные заголовки соединения, Host, Content-Length и семейства заголовков доверия к прокси целиком: X-Forwarded, X-Original, X-Rewrite, CF, Fastly. Точечно срезаются имена, из которых стандартные библиотеки читают адрес клиента: Forwarded, X-Real-Ip, Client-Ip, X-Client-Ip, True-Client-Ip, X-Cluster-Client-Ip, Forwarded-For, Appengine-User-Ip, X-Appengine-User-Ip. Значение любого из них у внешнего вызова задаёт сам вызывающий, поэтому приложение его не получает и доверять адресу клиента на этом пути не может.

Authorization срезается по значению, а не по имени: платформа убирает его только тогда, когда там лежит её собственный ключ. Собственная схема приложения — Authorization: Bearer eyJ…, Basic, любая другая — доезжает без изменений.

Что возвращается вызывающему

Статус и тело ответа приложения передаются вызывающему как есть. Заголовки ответа тоже, кроме Set-Cookie, Content-Length, служебных заголовков соединения и заголовков с префиксом X-Vibe- — этот префикс на обоих направлениях принадлежит платформе.

Ответы 3xx передаются как есть — платформа по ним не переходит, решение принимает вызывающий.

Отказ платформы отличается от ответа приложения по заголовку X-Vibecode-Proxy-Error. Он стоит на каждом ответе, который построила платформа, и снимается ровно тогда, когда ответ пришёл от приложения. Приложение выставить его не может — из ответа приложения этот заголовок срезается.

Тело отказа платформы — конверт V1:

JSON
{
  "success": false,
  "error": {
    "code": "APP_API_NOT_ALWAYS_ON",
    "message": "Application API is not reachable"
  }
}

Поэтому разбирать ответ надо в таком порядке: сначала X-Vibecode-Proxy-Error, потом статус. Статус 503 с этим заголовком — отказ платформы, статус 503 без него — ответ самого приложения.

Ответ без этого заголовка и без конверта V1 — например HTML-страница с кодом 400 на запрос с двумя заголовками Content-Length — пришёл от промежуточного узла на пути к платформе, а не от неё и не от приложения.

Заголовок X-Vibe-Request-Id на ответе вызывающему стоит, когда вызов дошёл до туннеля приложения: на ответе приложения и на отказах, которые вернул сам туннель. На отказах до туннеля — ключ, переключатель, сервер, путь, размер тела, — на 503 APP_API_TIMEOUT и при насыщении канала идентификатора нет: в обращении в поддержку называйте applicationId и время вызова.

Поле Тип Описание
success boolean Всегда false — конверт приходит только на отказе платформы
error.code string Код отказа из таблицы Ошибки
error.message string Английское пояснение. Разбирайте error.code, не текст

Примеры

Оба примера показывают ключ внешнего API. Второй оси авторизации у этого адреса нет: личный ключ и ключ авторизации приложения возвращают 403 APP_API_NOT_GRANTED, потому что не привязаны к приложению.

curl — ключ внешнего API

Terminal
curl -X POST "https://vibecode.bitrix24.tech/v1/applications/7f3a1c40-0a2e-4b5d-9c11-2f8e6d3b0a55/api/score" \
  -H "X-Api-Key: YOUR_APP_EXTERNAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dealId": 741, "amount": 125000}'

JavaScript — ключ внешнего API

javascript
const res = await fetch(
  'https://vibecode.bitrix24.tech/v1/applications/7f3a1c40-0a2e-4b5d-9c11-2f8e6d3b0a55/api/score',
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_EXTERNAL_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ dealId: 741, amount: 125000 }),
  },
);

if (res.headers.get('x-vibecode-proxy-error') === '1') {
  const { error } = await res.json();
  throw new Error(`платформа отказала: ${error.code}`);
}

const data = await res.json();

Приложение, которое зовут автоматизации

Автоматизации — частый вызывающий, и у них свои привычки. Приложение, рассчитанное на автоматизации, придерживается трёх правил.

Принимайте и application/x-www-form-urlencoded, и JSON. Автоматизация, отправляющая HTTP-запрос, складывает значения полями формы и JSON не собирает. Приложение, которое читает только JSON, на таком вызове получит пустое тело. Разбирайте оба типа содержимого и сводите их к одной структуре.

Отвечайте плоским JSON. Автоматизация кладёт в переменные бизнес-процесса значения верхнего уровня, вложенные объекты и массивы ей недоступны. Ответ вида {"score": 82, "verdict": "approve"} разбирается автоматизацией целиком, а {"result": {"score": 82}} — нет.

Отдавайте GET /openapi.json со схемой своих маршрутов. Схема, выложенная приложением, доступна снаружи тем же каналом — по адресу …/api/openapi.json. Так вызывающая сторона — человек, другое приложение или AI-агент — узнаёт набор маршрутов и формы тел, не читая ваш код.

Вызов из другого приложения

Вызывающему приложению нужен ключ вызываемого. Положите его в переменные окружения при развёртывании — POST /v1/infra/servers/:id/deploy — и читайте оттуда:

javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/applications/${process.env.PARTNER_APP_ID}/api/score`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': process.env.PARTNER_APP_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ dealId: 741 }),
  },
);

На стороне вызываемого приложения такой запрос отличается от робота значением X-Vibe-Caller-Key-Id — заведите отдельный ключ под каждого вызывающего, и приложение сможет их различать.

Ограничения

Ограничение Значение
Тело запроса 4 МиБ. Больше — 413 APP_API_PAYLOAD_TOO_LARGE
Тело ответа приложения 4 МиБ. Больше — 502 APP_API_RESPONSE_TOO_LARGE без Retry-After: повтор такой ответ не исправит
Время ответа приложения 25 секунд на ответ приложения. Дольше — 503 APP_API_UNAVAILABLE с Retry-After. Общий предел вызова — 30 секунд, за ним 503 APP_API_TIMEOUT
Частота вызовов 120 запросов в минуту на ключ. Точное значение — в заголовке x-ratelimit-limit (потолок делится на реплики). Больше — 429 APP_API_RATE_LIMITED
Одновременные вызовы Канал ограничивает число вызовов, идущих разом. При насыщении — 503 APP_API_UNAVAILABLE с Retry-After
Длина пути 2048 символов
Действующих ключей на приложение 10

Долгую работу выносите за пределы вызова: принимайте задачу, отвечайте роботу сразу и досылайте результат отдельным способом. Двадцать пять секунд — это предел канала, а не рекомендация.

Пример ответа

Ответ приложения передаётся как есть, поэтому его форму задаёт само приложение. Ответ приложения, написанного по конвенции для роботов:

JSON
{
  "score": 82,
  "verdict": "approve",
  "checkedAt": "2026-09-12T08:41:03Z"
}

Признак успеха — статус ответа и отсутствие заголовка X-Vibecode-Proxy-Error, а не поле success: обёртки платформы на успешном ответе нет.

Пример ответа при ошибке

409 — переключатель внешнего API выключен:

JSON
{
  "success": false,
  "error": {
    "code": "APP_API_NOT_ENABLED",
    "message": "Application API is not reachable"
  }
}

Текст message у отказов допуска один и тот же — Application API is not reachable — для APP_API_NOT_GRANTED, APP_API_NOT_ENABLED, APP_API_NO_SERVER, APP_API_NOT_ALWAYS_ON и APP_API_UNAVAILABLE по серверу. Различайте их по error.code.

Ошибки

Каждый ответ из этой таблицы несёт заголовок X-Vibecode-Proxy-Error: 1.

HTTP Код Описание
400 APP_API_BAD_PATH Путь или строка запроса не прошли проверку
401 MISSING_API_KEY Заголовок с ключом не передан
401 INVALID_API_KEY Ключ не существует или отозван
402 ACCOUNT_FROZEN Счёт владельца приложения заморожен
403 APP_API_NOT_GRANTED Ключ выпущен для другого приложения или не является ключом внешнего API
403 APP_API_KEY_OUT_OF_SCOPE Ключом внешнего API вызван другой адрес платформы
403 WRITE_BLOCKED_READONLY_KEY Ключ переведён в режим чтения, а вызов изменяет данные
404 APP_API_APP_NOT_FOUND Приложения с таким applicationId нет или оно удалено
409 APP_API_NOT_ENABLED Внешний API приложения выключен
409 APP_API_NO_SERVER У приложения нет сервера
409 APP_API_NOT_ALWAYS_ON Сервер приложения засыпает — автосон, вытесняемый тариф или заданные окна пробуждения
413 APP_API_PAYLOAD_TOO_LARGE Тело запроса больше 4 МиБ
429 APP_API_RATE_LIMITED Превышена частота вызовов на ключ
429 QUOTA_EXCEEDED Исчерпана квота вызовов владельца приложения
502 APP_API_BAD_ENVELOPE Приложение ответило вне контракта — например статусом из диапазона 1xx
502 APP_API_RESPONSE_TOO_LARGE Ответ приложения больше 4 МиБ. Retry-After не приходит
503 APP_API_UNAVAILABLE Сервер приложения не запущен, приложение не отвечает, или канал насыщен. Приходит с Retry-After в секундах
503 APP_API_TIMEOUT Ответ не пришёл за 30 секунд — общий предел вызова. Приходит с Retry-After в секундах

Полный список общих ошибок API — Ошибки.

Ошибки выдачи ключа приходят на карточке приложения, а не на этом адресе: 409 APP_API_KEY_LIMIT — достигнут предел действующих ключей, 409 APP_API_KEY_ISSUE_CONFLICT — владелец приложения сменился, пока ключ выпускался.

Известные особенности

  • Повторять имеет смысл только 503 и 429. У них приходит Retry-After со сроком в секундах. 502 повтором не лечится: он означает, что ответ приложения не укладывается в контракт канала. Статуса 504 этот адрес не отдаёт вовсе: медленное приложение приходит вызывающему как 503 — и когда платформа перестаёт ждать ответ на двадцать пятой секунде (APP_API_UNAVAILABLE), и когда вызов упирается в общий предел в тридцать секунд (APP_API_TIMEOUT). Увидели 504 на этом адресе — он пришёл не от канала, а от промежуточного узла на пути к платформе. Таймаут не означает, что работа не выполнена: приложение могло довести её до конца после того, как канал перестал ждать, поэтому слепой повтор неидемпотентного вызова задваивает её.
  • Причину 503 различает код, а не статус. APP_API_TIMEOUT означает ровно одно — приложение не ответило за общий предел вызова, и лечится это выносом долгой работы за пределы вызова. APP_API_UNAVAILABLE собирает под собой молчащий туннель, недоступный сервер и насыщенный канал: их вызывающему не различить, и остаётся повтор через Retry-After. Разобрать причину можно по обращению в поддержку: с идентификатором из X-Vibe-Request-Id, если он на ответе есть, иначе — по applicationId и времени вызова.
  • Один шумный вызывающий отбирает канал у остальных. Доля одного портала ограничена, но эта доля — предел, а не резерв. Когда канал занят соседями, отказ 503 получает и тот, кто своей доли не выбрал.
  • Платит владелец вызываемого приложения. Вызовы расходуют его квоту, а всегда включённый сервер стоит денег независимо от числа вызовов. Вызывающая сторона за канал не платит.
  • Путь приложения в журнал платформы не попадает. Платформа пишет его длину, но не значение — секрет в пути не станет достоянием журнала. Это не разрешение класть туда секреты: путь видят и вызывающий, и промежуточные узлы.

Смотрите также