For AI agents: markdown of this page — /docs-content-en/bots/management/transfer.md documentation index — /llms.txt
Transfer bot ownership
POST /v1/bots/:botId/transfer
Moves a bot's ownership to another API key of the same Bitrix24 account and the same user (or an account admin). Resolves the case where a bot is orphaned after the app is recreated: the owning key is revoked and the bot's runtime stops working. The transfer changes only the key binding — the botId, chat history, and subscriptions are preserved. No B24 call is made; after the transfer, verify the new key's B24 binding via POST /v1/bots/:botId/reauth.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
botId |
number | yes | Bot ID (path parameter) |
targetApiKeyId |
string | yes | ID of the target API key (request body) |
The target key must be active (ACTIVE), in the same account as the bot, belong to the same user (or the caller is an account admin), carry the imbot scope, and not be a platform-managed service key (cowork, agent-seat, agent-maintenance) — those have a platform-driven lifecycle, so the bot would be orphaned again when they rotate.
Examples
curl
curl -X POST https://vibecode.bitrix24.com/v1/bots/42/transfer \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "targetApiKeyId": "ck_target_key_id" }'
JavaScript
const res = await fetch('https://vibecode.bitrix24.com/v1/bots/42/transfer', {
method: 'POST',
headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({ targetApiKeyId: 'ck_target_key_id' }),
})
const { data } = await res.json()
console.log(data) // { transferred: true, botId: 42, fromApiKeyId: '...', toApiKeyId: '...' }
Response fields
| Field | Type | Description |
|---|---|---|
data.transferred |
boolean | true if the binding changed; false if the key already owned the bot (idempotent no-op) |
data.botId |
number | Bot ID |
data.fromApiKeyId |
string | ID of the previous owner key (only when transferred: true) |
data.toApiKeyId |
string | ID of the new owner key |
Response example
{
"success": true,
"data": {
"transferred": true,
"botId": 42,
"fromApiKeyId": "ck_old_owner",
"toApiKeyId": "ck_target_key_id"
}
}
If the target key already owns the bot, no transfer happens and transferred: false is returned (with fromApiKeyId equal to toApiKeyId — the current owner).
Error response example
400 — the target key is not eligible (with a reason field):
{
"success": false,
"error": {
"code": "TARGET_KEY_INVALID",
"message": "Target API key is not eligible to own this bot.",
"reason": "wrong_user"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_BOT_ID |
botId is not a number |
| 400 | INVALID_PARAMS |
targetApiKeyId is missing or not a string |
| 400 | TARGET_KEY_INVALID |
The target key is not eligible. reason: not_active · wrong_portal · wrong_user · missing_scope · expired · system_key (platform-managed service key) |
| 403 | NOT_BOT_OWNER |
The caller does not own the bot (must be the same user as the owner key, or an account admin) |
| 403 | SCOPE_DENIED |
The API key does not have the imbot scope |
| 403 | WRITE_BLOCKED_READONLY_KEY |
The key is in read-only mode |
| 404 | BOT_NOT_FOUND |
The bot was not found in the account |
| 404 | TARGET_KEY_NOT_FOUND |
The target key was not found |
| 409 | BOT_TRANSFER_NOT_ALLOWED |
The bot is managed by an agent or a managed bot — transfer it via that resource, not directly |
| 409 | BOT_TRANSFER_CONFLICT |
Ownership changed concurrently — re-read the current owner and retry if still needed |
Full list of common API errors — Errors.
Known specifics
When to use. The bot stopped working after the OAuth app was recreated: the owning key is revoked and the bot's runtime no longer starts. Transferring to an active personal key (vibe_api_*) of the same account restores the bot — the runtime does not depend on which app registered the bot.
Authorization is by user, not by key. The transfer is allowed for the bot owner (the same Vibe user as the current owner key — even if that key is already revoked) or an account admin. Holding the current owner key is not required — that is exactly why the transfer works when the original key is revoked.
Idempotency. Transferring to the current owner key (including a revoked one) is a safe no-op: transferred: false is returned, the binding is unchanged, and no audit entry is written.
Verify after the transfer. No B24 call is made during the transfer. To confirm the new key can drive the bot, call POST /v1/bots/:botId/reauth — it validates the new key's B24 binding.