Для AI-агентов: markdown этой страницы — /docs-content/cowork/app-login.md индекс документации — /llms.txt
Вход в приложение из десктопа
POST /v1/cowork/app-login
Выдаёт десктопу Cowork/Code адрес задеплоенного приложения со ссылкой для входа, чтобы встроенный браузер открыл приложение сразу от имени владельца ключа, без формы авторизации.
Платформа сама решает, пускать ли человека: приложение должно принадлежать аккаунту Битрикс24, к которому привязан ключ, а его политика доступа должна пускать владельца ключа. Шлюз меняет токен из адреса на свою куку поддомена приложения и перенаправляет на адрес без токена. Приложение получает номер пользователя в Битрикс24, как при обычном входе. Токен открывает только это приложение — ни кабинет платформы, ни соседние приложения по нему не открываются.
Вызвать эндпоинт может только ключ десктопа Cowork/Code со скоупом vibe:cowork. Ключ агентского места и ключ стороннего агента несут тот же скоуп, но получают 403 COWORK_DESKTOP_KEY_REQUIRED.
Скоуп: vibe:cowork. Ключ должен относиться к классу десктопа Cowork/Code.
Поля запроса (body)
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
appUrl |
string | да | Адрес приложения, которое нужно открыть, с любым путём, параметрами запроса и якорем. Схема, хост и порт должны точно совпадать с адресом приложения. Данные пользователя в адресе и параметр __gw_token не допускаются |
Примеры
curl
curl -X POST https://vibecode.bitrix24.tech/v1/cowork/app-login \
-H "X-Api-Key: YOUR_COWORK_KEY" \
-H "Content-Type: application/json" \
-d '{"appUrl": "https://app-3f9c2a1b7e.vibecode.bitrix24.tech/orders?status=new"}'
JavaScript — получить адрес входа и проверить его
// Адрес приложения: https, поддомен app-* домена приложений, без порта, данных пользователя и токена
function isAppAddress(appUrl) {
try {
const u = new URL(appUrl)
return u.protocol === 'https:' && !u.port && !u.username && !u.password
&& !u.searchParams.has('__gw_token')
&& /^app-[a-z0-9-]+\.vibecode\.bitrix24\.tech$/.test(u.hostname)
} catch {
return false
}
}
async function getAppLoginUrl(appUrl) {
// Не адрес приложения — ручку не зовём и адрес не открываем
if (!isAppAddress(appUrl)) return null
const res = await fetch('https://vibecode.bitrix24.tech/v1/cowork/app-login', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_COWORK_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ appUrl }),
})
if (!res.ok) {
const { error } = await res.json().catch(() => ({}))
// Платформа не признала адрес приложением — не открываем его совсем
if (error?.code === 'INVALID_APP_URL') return null
return appUrl // прочие отказы — адрес уже проверен выше, открываем его с обычным входом
}
const { url } = await res.json() // { url, expiresIn } — без обёртки success/data
// Ответ отличается от запроса ровно одним параметром токена
const stripped = url.replace(/[?&]__gw_token=[^&#]*/, '')
return stripped === appUrl ? url : null // расхождение — не открываем ничего
}
Поля ответа
| Поле | Тип | Описание |
|---|---|---|
url |
string | Исходный appUrl с одним добавленным параметром __gw_token. Параметр стоит последним в строке запроса, перед якорем. Токен не разбирайте и не пишите в журналы |
expiresIn |
number | Срок жизни токена в секундах. Платформа берёт его из самого подписанного токена |
Пример ответа
{
"url": "https://app-3f9c2a1b7e.vibecode.bitrix24.tech/orders?status=new&__gw_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXAiOiJndyJ9.c2lnbmF0dXJl",
"expiresIn": 600
}
Пример ответа при ошибке
{
"success": false,
"error": {
"code": "APP_NOT_AVAILABLE",
"message": "The app is not available."
}
}
Ошибки
| HTTP | Код | Описание |
|---|---|---|
| 400 | INVALID_APP_URL |
Поле appUrl не передано, не строка или не является точным адресом приложения: другая схема или порт, лишний уровень поддомена, данные пользователя, управляющие символы, уже присутствующий параметр __gw_token. Повтор с тем же телом не поможет |
| 401 | MISSING_API_KEY |
Не передан заголовок X-Api-Key |
| 401 | INVALID_API_KEY |
Ключ не опознан: такой строки на платформе нет |
| 401 | KEY_INACTIVE |
Ключ отозван — например, устройство отключили через DELETE /v1/cowork/key |
| 401 | KEY_EXPIRED |
Срок действия ключа истёк |
| 402 | ACCOUNT_FROZEN |
Баланс аккаунта исчерпан — пополните счёт |
| 403 | INSUFFICIENT_SCOPE |
У ключа нет скоупа vibe:cowork |
| 403 | COWORK_DESKTOP_KEY_REQUIRED |
Ключ не относится к классу десктопа Cowork/Code — так отвечает и ключ агентского места, и ключ стороннего агента |
| 403 | PORTAL_REQUIRED |
Ключ не привязан к человеку на аккаунте Битрикс24 |
| 403 | WRITE_BLOCKED_READONLY_KEY |
Ключ выпущен в режиме «только чтение» |
| 404 | APP_NOT_AVAILABLE |
Один ответ на все исходы проверки доступа: приложения нет, оно относится к другому аккаунту, его политика доступа не пускает владельца ключа, либо платформа не смогла принять решение |
| 409 | B24_USER_UNKNOWN |
Не удалось определить номер владельца ключа в Битрикс24, поэтому подтвердить его личность нечем |
| 413 | PAYLOAD_TOO_LARGE |
Тело больше 8 КБ |
| 415 | FST_ERR_CTP_INVALID_MEDIA_TYPE |
Тело прислано с типом содержимого, который этот маршрут не разбирает. Отправляйте тело с заголовком Content-Type: application/json |
| 429 | RATE_LIMITED |
Превышен предел частоты на связку аккаунт и человек — несколько ключей одного человека делят общий предел. Суммарный лимит платформы — 30 запросов в минуту. Действующее для вашего ключа значение приходит в заголовке x-ratelimit-limit — оно ниже суммарного, поскольку лимит делится между репликами |
| 503 | user_self_deletion_pending |
Владелец ключа ожидает удаления учётной записи на этом аккаунте. Запрос отклоняется до выдачи токена; повторяйте после задержки из Retry-After |
Полный список общих ошибок API — Ошибки.
Известные особенности
Успешный ответ (200) — это сам объект, без обёртки success. Ошибки приходят в конверте { success: false, error: { code, message } }. Определяйте успех по HTTP-статусу (res.ok).
Адрес ответа совпадает с запросом побайтно, кроме параметра токена. Платформа не пересобирает адрес: кодировка параметров, пустые значения и якорь остаются как в appUrl. Сравнивайте ответ с запросом после удаления параметра __gw_token и не открывайте адрес, если осталось расхождение. После обмена токена на куку шлюз перенаправляет на адрес без токена и собирает строку запроса заново: порядок параметров и запись пустых значений у приложения могут отличаться от appUrl.
Токен действует до конца срока, а не один раз. Повторный переход по тому же адресу в пределах expiresIn снова пускает в приложение. После первого перехода шлюз ставит куку и перенаправляет на адрес без токена, поэтому следующие открытия того же приложения идут по куке, без нового вызова.
Отзыв ключа останавливает выдачу новых входов. Кука, которую шлюз уже поставил, живёт свой срок. Политику доступа приложения шлюз перепроверяет сам, поэтому закрытие доступа к приложению действует и для такой куки.
Как реагировать на отказы. После 400 INVALID_APP_URL адрес не открывайте совсем: платформа не признала его адресом приложения, покажите пользователю ошибку. Любой другой отказ означает, что приложение нужно открыть по исходному адресу с обычным входом — но только если адрес прошёл вашу собственную проверку до запроса: https, поддомен вида app-* домена приложений, без порта, данных пользователя и параметра __gw_token. Отказ 401 или 403 приходит до разбора адреса и не подтверждает, что это адрес приложения. 401 — попросите пользователя войти в десктоп заново. 400 и 403 — ошибка клиента, повторять запрос бессмысленно. 429 — повторяйте с растущей паузой.