Для 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.
Что включить на стороне приложения
Канал работает при трёх условиях.
- Переключатель «Внешний API» включён в карточке приложения. Он стоит в карточке приложения на платформе и принимает значения «Включён» и «Выключен». Управлять им может тот, кто управляет приложением.
- У приложения есть сервер. Без сервера включить переключатель нельзя, а вызов возвращает
409 APP_API_NO_SERVER. - Сервер не засыпает. Автоматический сон отключён, тариф сервера не вытесняемый, окна пробуждения не заданы. Иначе переключатель не включится, а вызов вернёт
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:
{
"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
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
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 — и читайте оттуда:
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 |
Долгую работу выносите за пределы вызова: принимайте задачу, отвечайте роботу сразу и досылайте результат отдельным способом. Двадцать пять секунд — это предел канала, а не рекомендация.
Пример ответа
Ответ приложения передаётся как есть, поэтому его форму задаёт само приложение. Ответ приложения, написанного по конвенции для роботов:
{
"score": 82,
"verdict": "approve",
"checkedAt": "2026-09-12T08:41:03Z"
}
Признак успеха — статус ответа и отсутствие заголовка X-Vibecode-Proxy-Error, а не поле success: обёртки платформы на успешном ответе нет.
Пример ответа при ошибке
409 — переключатель внешнего API выключен:
{
"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получает и тот, кто своей доли не выбрал. - Платит владелец вызываемого приложения. Вызовы расходуют его квоту, а всегда включённый сервер стоит денег независимо от числа вызовов. Вызывающая сторона за канал не платит.
- Путь приложения в журнал платформы не попадает. Платформа пишет его длину, но не значение — секрет в пути не станет достоянием журнала. Это не разрешение класть туда секреты: путь видят и вызывающий, и промежуточные узлы.