
## Перенос владения ботом

`POST /v1/bots/:botId/transfer`

Переносит владение ботом на другой API-ключ того же портала Битрикс24 и того же пользователя, либо перенос выполняет администратор портала. Решает ситуацию, когда ключ-владелец отозван и бот перестал работать. Перенос меняет только привязку ключа — `botId`, история чатов и подписки сохраняются. Обращения к Битрикс24 при переносе не происходит, поэтому доступ нового ключа проверяется отдельным вызовом [`POST /v1/bots/:botId/reauth`](/docs/bots/management/reauth).

## Параметры

| Параметр | Тип | Обяз. | Описание |
|----------|-----|:-----:|---------|
| `botId` (path) | number | да | ID бота. Список: [`GET /v1/bots`](/docs/bots/management/list). Если бота там нет, его ID приходит в поле `data.botId` ответа `409 BOT_ALREADY_EXISTS` — см. [Восстановление доступа к боту](/docs/bots/ownership-recovery) |

## Поля запроса (body)

| Поле | Тип | Обяз. | Описание |
|------|-----|:-----:|---------|
| `targetApiKeyId` | string | да | Идентификатор записи целевого ключа, поле `id` в ответе [`GET /v1/keys`](/docs/management-keys). Не строка ключа |

Целевой ключ должен быть активным ключом общего назначения того же портала, со скоупом `imbot` и незакончившимся сроком действия, и принадлежать тому же пользователю Вайбкод, который выполняет перенос. Администратор портала переносит бота на ключ любого пользователя того же портала. Ключ, не прошедший проверку, отклоняется с `400 TARGET_KEY_INVALID`, конкретная причина приходит в поле `reason` — расшифровка причин в таблице [Ошибки](#ошибки).

## Примеры

### curl — личный ключ

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/bots/42/transfer \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "targetApiKeyId": "3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33" }'
```

### JavaScript — личный ключ

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/bots/42/transfer', {
  method: 'POST',
  headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
  body: JSON.stringify({ targetApiKeyId: '3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33' }),
})
const { data } = await res.json()
console.log(data) // { transferred: true, botId: 42, fromApiKeyId: '...', toApiKeyId: '...' }
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|---------|
| `data.transferred` | boolean | `true`, если привязка изменилась. `false` — если ключ уже был владельцем и повторный вызов ничего не изменил |
| `data.botId` | number | ID бота |
| `data.fromApiKeyId` | string | ID прежнего ключа-владельца. При `transferred: false` равен `toApiKeyId` |
| `data.toApiKeyId` | string | ID нового ключа-владельца |

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

```json
{
  "success": true,
  "data": {
    "transferred": true,
    "botId": 42,
    "fromApiKeyId": "8c41d5e7-2b90-4a63-b1f5-6d7e9a0c4b12",
    "toApiKeyId": "3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33"
  }
}
```

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

400 — целевой ключ не подходит (с уточнением в поле `reason`):

```json
{
  "success": false,
  "error": {
    "code": "TARGET_KEY_INVALID",
    "message": "Target API key is not eligible to own this bot.",
    "reason": "wrong_user"
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|---------|
| 400 | `INVALID_BOT_ID` | `botId` не является числом |
| 400 | `INVALID_PARAMS` | `targetApiKeyId` не передан, не строка или пустая строка |
| 400 | `TARGET_KEY_INVALID` | Целевой ключ не подходит. `reason`: `not_active` · `wrong_portal` · `wrong_user` · `missing_scope` · `expired` · `system_key` (служебный платформенный ключ) |
| 403 | `NOT_BOT_OWNER` | Вызывающий не владеет ботом (нужен тот же пользователь, что у ключа-владельца, или администратор портала) |
| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `imbot` |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | Ключ в режиме «только чтение» |
| 404 | `BOT_NOT_FOUND` | Бот не найден на портале |
| 404 | `TARGET_KEY_NOT_FOUND` | Целевой ключ не найден |
| 409 | `BOT_TRANSFER_NOT_ALLOWED` | Бот управляется агентом или управляемым ботом — переносите через соответствующий ресурс, а не напрямую |
| 409 | `BOT_TRANSFER_CONFLICT` | Владение изменилось параллельным запросом — перечитайте текущего владельца и повторите при необходимости |

Полный список общих ошибок API — [Ошибки](/docs/errors).

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

**Работа бота не зависит от приложения-регистратора.** Перенос на активный личный ключ `vibe_api_*` того же портала восстанавливает работу бота независимо от того, каким приложением он был зарегистрирован. Полный порядок с поиском `botId` и идентификатора целевого ключа — [Восстановление доступа к боту](/docs/bots/ownership-recovery).

**Авторизация по пользователю, а не по ключу.** Перенос разрешён владельцу бота, то есть пользователю Вайбкод, которому принадлежит текущий ключ-владелец, даже если этот ключ уже отозван. Выполнить перенос может и администратор портала. Держать ключ-владелец не требуется, именно поэтому перенос работает после отзыва исходного ключа.

**Идемпотентность.** Перенос на текущий ключ-владелец, в том числе отозванный, безопасен: привязка не меняется, запись в журнал не создаётся.

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

- [Восстановление доступа к боту](/docs/bots/ownership-recovery)
- [Повторная авторизация бота](/docs/bots/management/reauth)
- [Диагностика проблем](/docs/bots/troubleshooting)
- [Бот-платформа](/docs/bots)
