Для 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

Terminal
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 — получить адрес входа и проверить его

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 Срок жизни токена в секундах. Платформа берёт его из самого подписанного токена

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

JSON
{
  "url": "https://app-3f9c2a1b7e.vibecode.bitrix24.tech/orders?status=new&__gw_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXAiOiJndyJ9.c2lnbmF0dXJl",
  "expiresIn": 600
}

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

JSON
{
  "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 — повторяйте с растущей паузой.

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