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 bot ownership to another API key of the same Bitrix24 account and the same user. A Bitrix24 account administrator can also perform the transfer. Resolves the case where the owning key is revoked and the bot stops working. The transfer changes only the key binding — the botId, chat history, subscriptions, and disabled state are preserved. No call to Bitrix24 is made during the transfer. For an active bot, verify the new key's access with a separate POST /v1/bots/:botId/reauth call. For a disabled bot, call it only when 410 BOT_DISABLED includes error.details.reauthAllowed=true. Other disabled states are protected: /reauth returns 409 BOT_REAUTH_NOT_ALLOWED, and transfer does not clear them.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
ParameterbotId (path) |
Typenumber | Requiredyes | DescriptionBot ID. List: GET /v1/bots. If the bot is not there, its ID is returned in the data.botId field of the 409 BOT_ALREADY_EXISTS response — see Bot access recovery |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
FieldtargetApiKeyId |
Typestring | Requiredyes | DescriptionIdentifier of the target key record, the id field in the GET /v1/keys response. Not the key string |
The target key must be an active general-purpose key of the same Bitrix24 account, have the imbot scope, not be expired, and belong to the same Vibecode user who performs the transfer. A Bitrix24 account administrator can transfer a bot to a key of any user in the same account.
Examples
curl — personal key
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": "3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33" }'
curl — OAuth application
curl -X POST https://vibecode.bitrix24.com/v1/bots/42/transfer \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "targetApiKeyId": "3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33" }'
JavaScript — personal key
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: '3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33' }),
})
const { data } = await res.json()
console.log(data) // { transferred: true, botId: 42, fromApiKeyId: '...', toApiKeyId: '...' }
JavaScript — OAuth application
const res = await fetch('https://vibecode.bitrix24.com/v1/bots/42/transfer', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({ targetApiKeyId: '3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33' }),
})
const { data } = await res.json()
Response fields
| Field | Type | Description |
|---|---|---|
Fielddata.transferred |
Typeboolean | Descriptiontrue if the binding changed. false if the key already owned the bot and the repeat call changed nothing |
Fielddata.botId |
Typenumber | DescriptionBot ID |
Fielddata.fromApiKeyId |
Typestring | DescriptionID of the previous owner key. When transferred is false, it equals toApiKeyId |
Fielddata.toApiKeyId |
Typestring | DescriptionID of the new owner key |
Response example
{
"success": true,
"data": {
"transferred": true,
"botId": 42,
"fromApiKeyId": "8c41d5e7-2b90-4a63-b1f5-6d7e9a0c4b12",
"toApiKeyId": "3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33"
}
}
Error response example
400 — the target key is not eligible (the specific cause is in the 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 |
|---|---|---|
| HTTP400 | CodeINVALID_BOT_ID |
DescriptionbotId is not a number |
| HTTP400 | CodeTARGET_KEY_INVALID |
Descriptionerror.reason=recovery_required: the source or target key is part of an unfinished rotation. The binding stays unchanged. Complete recovery through support instead of retrying the transfer |
| HTTP400 | CodeINVALID_PARAMS |
DescriptiontargetApiKeyId is missing, not a string, or an empty string |
| HTTP400 | CodeTARGET_KEY_INVALID |
DescriptionThe target key is not eligible. reason: not_active · wrong_portal · wrong_user · missing_scope · expired · system_key (platform-managed service key) |
| HTTP403 | CodeNOT_BOT_OWNER |
DescriptionThe caller does not own the bot (must be the same user as the owner key, or a portal admin) |
| HTTP403 | CodeSCOPE_DENIED |
DescriptionThe API key does not have the imbot scope |
| HTTP403 | CodeWRITE_BLOCKED_READONLY_KEY |
DescriptionThe key is in read-only mode |
| HTTP404 | CodeBOT_NOT_FOUND |
DescriptionThe bot was not found on the portal |
| HTTP404 | CodeTARGET_KEY_NOT_FOUND |
DescriptionThe target key was not found |
| HTTP409 | CodeBOT_TRANSFER_NOT_ALLOWED |
DescriptionThe bot is managed by an agent or a managed bot — transfer it via that resource, not directly |
| HTTP409 | CodeBOT_TRANSFER_CONFLICT |
DescriptionOwnership changed concurrently or the protective transfer lock on the portal is busy or unavailable — re-read the current owner and retry if still needed. When the lock refuses, no transfer is performed |
Full list of common API errors — Errors.
Known specifics
A bot's operation does not depend on the app that registered it. Transferring to an active personal vibe_api_* key of the same account restores the bot regardless of which app registered it. The full procedure, including how to find the botId and the target key identifier, is in Bot access recovery.
A ready-made call in the 403 BOT_ACCESS_DENIED response. When a bot method responds with 403 BOT_ACCESS_DENIED and error.fix.action is transfer_bot, the response already contains this transfer: error.fix.path is a string like POST /v1/bots/42/transfer with the bot ID, and error.fix.body is the body { "targetApiKeyId": "<calling key id>" }. Call fix.path with the same key, sending fix.body as the request body. The response fields and the cases with error.fix.action: "none" are covered in BOT_ACCESS_DENIED.
Authorization is by user, not by key. The transfer is allowed for the bot owner, that is, the Vibecode user who owns the current owner key, even when that key is already revoked. That is why the transfer works after the original key is revoked.
Idempotency. Transferring to the current owner key, including a revoked one, is safe: the binding stays unchanged and no audit entry is written.