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

Terminal
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

Terminal
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

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: '3f9a1c20-5e6b-4d18-9a77-0c2b8e4f1d33' }),
})
const { data } = await res.json()
console.log(data) // { transferred: true, botId: 42, fromApiKeyId: '...', toApiKeyId: '...' }

JavaScript — OAuth application

javascript
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

JSON
{
  "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):

JSON
{
  "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.

See also