## Вход в Коворк по коду

Способ только для официального клиента `cowork-desktop` — десктопного приложения Коворк. Пользователь нажимает кнопку на странице Коворка в Вайбкод, платформа выпускает одноразовую ссылку `openwork://vibecode-auth?grant=...`, и приложение обменивает код из ссылки на ключ без повторного входа. Любой другой `client_id` получает `401 invalid_client`.

Код живёт 120 секунд и обменивается один раз. Новый код, выпущенный повторным нажатием кнопки, гасит прежний.

**Шаг 1. Просмотр кода — необязательно.** Показывает, кому принадлежит код, чтобы приложение спросило подтверждение до входа. Код при этом не тратится.

`POST /v1/connect/desktop-handoff/preview`

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/connect/desktop-handoff/preview \
  -H "Content-Type: application/json" \
  -d '{"client_id":"cowork-desktop","grant":"HANDOFF_CODE"}'
```

```json
{
  "user": { "id": 42, "name": "Иван Петров", "email": "user@example.com" },
  "portal": { "id": "550e8400-e29b-41d4-a716-446655440000", "domain": "example.bitrix24.ru" },
  "expiresAt": "2026-09-28T12:02:00.000Z"
}
```

`user.id` — идентификатор сотрудника в Битрикс24, `null`, если привязки нет. Суммарный лимит просмотра на платформе — 60 запросов в минуту с одного адреса. Он делится между репликами, поэтому действующее значение ниже; оно приходит в заголовке `x-ratelimit-limit`. При превышении — `429` с заголовком `Retry-After`.

**Шаг 2. Обмен кода на ключ.**

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/connect/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "urn:vibecode:params:oauth:grant-type:desktop-handoff",
    "client_id": "cowork-desktop",
    "grant": "HANDOFF_CODE",
    "device_id": "install.550e8400-e29b-41d4-a716-446655440000"
  }'
```

`device_id` — идентификатор установки приложения: латинские буквы, цифры и символы `_ . : -`, до 128 знаков. Значение другого формата — `400 invalid_request`, код при этом не тратится. Поле можно опустить, но тогда платформа не знает, какие ключи принадлежат установке, и шаг 3 прежних ключей не гасит, поэтому передавайте его всегда. Сам обмен прежние ключи установки не гасит — это делает шаг 3; единственное исключение — лимит в 10 живых ключей: при обмене без `device_id` платформа освобождает место, отзывая ключ без `device_id`, которым давно не пользовались.

```json
{ "api_key": "vibe_api_...", "scopes": [], "granted_scopes": ["vibe:cowork"] }
```

Поля те же, что у [входа с устройства](/docs/partner-connect): `scopes` — запрошенные права, у `cowork-desktop` их нет, поэтому список пуст; `granted_scopes` — права, которые ключ получил на самом деле, набор зависит от портала. Если данные доступны, в ответе есть ещё `b24_credentials`. Ключ отдаётся **один раз**: повторный обмен того же кода вернёт `invalid_grant`. Если ответ потерялся, выпустите новый код на странице Коворка — прежний ключ установки при этом продолжает работать (кроме случая лимита ключей без `device_id`, описанного выше).

После обмена платформа отправляет владельцу уведомление в чат Битрикс24 с кнопкой «Это не я — отключить»: она отзывает выданный ключ. Доставка не гарантируется, например, на портале может не оказаться бота.

**Шаг 3. Подтверждение доставки.** Когда приложение надёжно сохранило новый ключ, оно сообщает об этом, и платформа гасит прежние ключи той же установки, выпущенные раньше начала этой попытки входа. Доказательство доставки — сам новый ключ. Гашение работает только для обмена, в котором передан `device_id`: без него подтверждение отвечает `200 {}`, но ничего не гасит.

`POST /v1/connect/desktop-handoff/confirm`

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/connect/desktop-handoff/confirm \
  -H "Content-Type: application/json" \
  -d '{"client_id":"cowork-desktop","api_key":"NEW_API_KEY"}'
```

Ответ — `200 {}`. Повторный вызов тоже отвечает `200 {}` и ничего не гасит повторно, поэтому при сбое сети его можно повторить. Если подтверждения не было, прежние ключи установки остаются рабочими до следующего входа на ней. Подтвердить можно в течение суток после истечения кода, позже — `400 invalid_grant`. Лимит — 60 запросов в минуту с одного адреса, как у просмотра.

### Ошибки

| Статус | `error` | `error_description` | Когда |
|--------|---------|---------------------|-------|
| 401 | `invalid_client` | `COWORK_DESKTOP_HANDOFF_INVALID` | `client_id` не `cowork-desktop` или клиент отключён |
| 400 | `invalid_grant` | `COWORK_DESKTOP_HANDOFF_INVALID` | Код неверный, истёк, сгорел или уже обменян |
| 400 | `unsupported_grant_type` | `COWORK_DESKTOP_HANDOFF_UNAVAILABLE` | Вход по коду выключен (на просмотре — `404 not_found`) |
| 400 | `invalid_request` | `Invalid device_id` | Формат `device_id` не подходит; только обмен, код не тратится |
| 403 | `access_denied` | код причины | Пользователь, портал или доступ к Коворку больше не позволяют получить ключ |
| 409 | `access_denied` | `B24_USER_DELETED` | Сотрудник удалён в Битрикс24 |
| 409 | `access_denied` | `COWORK_DEVICE_LIMIT_REACHED` | У пары «сотрудник — портал» уже 10 живых ключей Коворка, а `device_id` новый. Код при этом потрачен: отзовите ненужное устройство в кабинете и запросите новую ссылку |
| 423 | `access_denied` | `ACCOUNT_PENDING_ERASURE`, `USER_SELF_DELETION_PENDING` | Аккаунт готовится к удалению |
| 503 | `temporarily_unavailable` | `COWORK_DISABLED` | Коворк временно выключен на платформе; это ответ сервера, а не сбой сети |

Подтверждение доставки отвечает `401 invalid_client` на чужой или отключённый клиент и `400 invalid_grant` / `COWORK_DESKTOP_HANDOFF_INVALID`, если ключ неизвестен, отозван, выдан не обменом кода или строка кода уже удалена. В этом случае ничего не гасится. Если вход по коду выключили уже после обмена, подтверждение отвечает `404 not_found` / `COWORK_DESKTOP_HANDOFF_UNAVAILABLE` и тоже ничего не гасит: повторите его, когда вход вернут, пока прежние ключи нужны.
