For AI agents: markdown of this page — /docs-content-en/management-keys/rotate.md documentation index — /llms.txt
Rotate an API key
POST /v1/keys/:id/rotate
Issues a new API key to replace the previous one with the same settings and moves the previous key's bindings to it.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
Parameterid (path) |
Typestring | Requiredyes | DescriptionRecord ID of the previous key. List: GET /v1/keys |
The request body may be empty or {}: body fields are not read, and the new key's settings are taken from the previous one.
Examples
The request accepts only a management key — see Limitations.
curl — management key
curl -X POST "https://vibecode.bitrix24.com/v1/keys/6dbad0ce-79a9-43f5-8ca4-3e1af7ef63bc/rotate" \
-H "X-Api-Key: YOUR_MANAGEMENT_KEY"
JavaScript — management key
const res = await fetch('https://vibecode.bitrix24.com/v1/keys/6dbad0ce-79a9-43f5-8ca4-3e1af7ef63bc/rotate', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_MANAGEMENT_KEY',
},
})
const { success, data, error } = await res.json()
if (success) {
// The new key's value arrives only in this response
console.log('New key:', data.rawKey)
} else if (error.details?.orphanKeyId) {
// The replacement was rolled back, the steps are in "Known specifics"
console.error(error.code, error.details)
}
Response fields
| Field | Type | Description |
|---|---|---|
Fieldsuccess |
Typeboolean | DescriptionAlways true on success |
Fielddata.id |
Typestring | DescriptionRecord ID of the new key |
Fielddata.name |
Typestring | DescriptionName carried over from the previous key |
Fielddata.type |
Typestring | DescriptionKey record type, always APP |
Fielddata.userId |
Typestring | DescriptionKey owner ID |
Fielddata.portalId |
Typestring | DescriptionBitrix24 account ID |
Fielddata.clientId |
Typestring | null | DescriptionOAuth client ID, if recorded on the key. Otherwise null |
Fielddata.scopes |
Typearray | DescriptionScopes carried over from the previous key |
Fielddata.ipWhitelist |
Typearray | DescriptionAllowed IP addresses. An empty array means no restriction |
Fielddata.expiresAt |
Typenull | DescriptionThe new key has no expiry |
Fielddata.rateLimit |
Typenumber | null | DescriptionIndividual request limit for AI endpoints, null — the platform-wide limit |
Fielddata.status |
Typestring | DescriptionStatus of the new key, ACTIVE |
Fielddata.lastUsedAt |
Typenull | DescriptionThe new key has not been used yet |
Fielddata.createdAt |
Typestring | DescriptionCreation date (ISO 8601) |
Fielddata.updatedAt |
Typestring | DescriptionLast update date (ISO 8601) |
Fielddata.accessMode |
Typestring | DescriptionREADWRITE or READONLY |
Fielddata.totalDefault |
Typeboolean | null | DescriptionWhether meta.total arrives in lists requested with this key. null — the platform setting applies |
Fielddata.issuedVia |
Typestring | null | DescriptionChannel through which the new key's webhook was issued: per-portal and cloud-shared — the connector module on the Bitrix24 account, dev_key — the owner's developer key, legacy — registration through Bitrix24 Network, none — no webhook was created |
Fielddata.rawKey |
Typestring | DescriptionFull value of the new key. Not returned again |
The prefix, suffix and b24Ready fields are not in the response — they arrive in GET /v1/keys/:id.
Response example
201 — the new key is issued:
{
"success": true,
"data": {
"id": "9b1f2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"name": "CRM integration",
"type": "APP",
"userId": "fafa9780-eed7-4d29-b040-2448d924f452",
"portalId": "0d9f7744-f853-47c7-b06e-6f61f9dd1b7f",
"clientId": null,
"scopes": ["crm"],
"ipWhitelist": [],
"expiresAt": null,
"rateLimit": null,
"status": "ACTIVE",
"lastUsedAt": null,
"createdAt": "2026-10-29T12:22:53.937Z",
"updatedAt": "2026-10-29T12:22:53.937Z",
"accessMode": "READONLY",
"totalDefault": null,
"issuedVia": "cloud-shared",
"rawKey": "vibe_api_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
}
Error response example
409 — the previous key is already revoked:
{
"success": false,
"error": {
"code": "KEY_NOT_ACTIVE",
"message": "Key is no longer active"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodePERSONAL_KEY_WEBHOOK_SCOPES_INVALID |
DescriptionThe previous key is left with only application-context scopes — placement, entity, userfieldtype. The scope set is checked before the Bitrix24 account is contacted, so every account receives the same refusal. First add a data scope to the previous key through PATCH /v1/keys/:id |
| HTTP400 | CodeBOX_NO_DEVELOPER_KEY |
DescriptionOn-premise Bitrix24: the key owner has no developer key, and the previous key carries Bitrix24 scopes |
| HTTP400 | CodeBOX_NO_DEVELOPER_KEY |
DescriptionOn-premise Bitrix24: the previous key has a webhook, and it cannot be removed without the owner's developer key |
| HTTP402 | CodeACCOUNT_FROZEN |
DescriptionThe account balance is frozen |
| HTTP403 | CodeSYSTEM_KEY_ROTATE_FORBIDDEN |
DescriptionThe key being rotated is one the platform issues on its own endpoint: a Cowork/Code desktop key, an agent key, a project deploy key. The response text says where this key is issued again |
| HTTP403 | CodeCOWORK_PLATFORM_DISABLED |
DescriptionA key billed by the Cowork/Code subscription: Cowork/Code is disabled at the platform level |
| HTTP403 | CodeCOWORK_ACCESS_REQUIRED |
DescriptionA Cowork/Code subscription key: the key owner has not been granted Cowork/Code access |
| HTTP403 | CodeCOWORK_EXTERNAL_CLIENTS_DISABLED |
DescriptionA Cowork/Code subscription key: the Bitrix24 account administrator has disallowed third-party clients |
| HTTP403 | CodeCOWORK_SUBSCRIPTION_INACTIVE |
DescriptionA Cowork/Code subscription key: the subscription is paused or cancelled |
| HTTP403 | CodeINT_TARIFF_REQUIRED |
DescriptionThe account's Bitrix24 plan does not grant access, so the webhook is not issued. Rotation resumes once the account moves to a commercial Bitrix24 plan |
| HTTP403 | CodeWRITE_BLOCKED_READONLY_KEY |
DescriptionThe management key that sent the request is in READONLY mode |
| HTTP403 | CodeMANAGEMENT_SCOPE_REQUIRED |
DescriptionThe management key lacks the vibe:mgmt:keys scope |
| HTTP404 | CodeKEY_NOT_FOUND |
DescriptionNo key with this id exists, it belongs to another user, it is a management key, or it is a key without a Bitrix24 account |
| HTTP404 | CodeKEY_NOT_FOUND |
DescriptionThe key was deleted while issuance was running. No new key is created |
| HTTP409 | CodeKEY_NOT_ACTIVE |
DescriptionThe previous key is no longer active: it was revoked or its status is EXPIRED. No new key is created. Re-read the key with GET /v1/keys/:id |
| HTTP409 | CodeKEY_NOT_ACTIVE |
DescriptionThe authorization key (vibe_app_) is not active, was deleted, its application was deleted, or the key was changed after the request started. No new key is created |
| HTTP409 | CodeREISSUE_IN_PROGRESS |
DescriptionAuthorization keys (vibe_app_) only: another operation on this application's key is already running — a rotation, an update or a deletion. Retry the request once it finishes |
| HTTP409 | CodeKEY_ROTATION_RECOVERY_REQUIRED |
DescriptionThe replacement of this key is unfinished: an earlier rotation ended with a 500 response, or another rotation of the same key has not finished yet. The refusal applies to both the old and the new key of that replacement. No new key is created. If there was no 500 response and the refusal still arrives after a few minutes, contact support |
| HTTP409 | CodeKEY_ROTATION_SOURCE_CHANGED |
DescriptionThe source key's binding changed during issuance. No new key is created. Re-read the key record and state before starting a new operation |
| HTTP409 | CodePORTAL_ORIGIN_CUTOVER_BUSY |
DescriptionThe address of the key's Bitrix24 account is changing. Retry the rotation later |
| HTTP500 | CodeKEY_ROTATE_REVOKE_FAILED |
DescriptionThe replacement was rolled back: for a blocked key, it was not confirmed that its access tokens were revoked. The new key was created, and its value is not in the response. The steps are in "Known specifics" |
| HTTP500 | CodeKEY_ROTATION_REPOINT_FAILED |
DescriptionThe replacement was rolled back: the move of bindings to the new key was not confirmed. The steps are in the same place |
| HTTP500 | CodeKEY_ROTATION_FINALIZE_FAILED |
DescriptionThe replacement was rolled back: the completion of the replacement was not confirmed. The steps are in the same place |
| HTTP503 | CodePORTAL_ORIGIN_CUTOVER_UNAVAILABLE |
DescriptionCould not check whether the address of the key's Bitrix24 account is changing. Retry the rotation later |
| HTTP401 | CodeWRONG_KEY_TYPE |
DescriptionThe request was sent with a Bitrix24 account key — a personal vibe_api_ key or an authorization key vibe_app_. The endpoint accepts only a management key |
| HTTP401 | CodeKEY_INACTIVE |
DescriptionThe management key is revoked or blocked |
| HTTP401 | CodeKEY_EXPIRED |
DescriptionThe management key has expired |
| HTTP401 | CodeINVALID_API_KEY |
DescriptionNo key with the given value was found |
| HTTP401 | CodeMISSING_API_KEY |
DescriptionThe X-Api-Key header is missing |
Rotation shares its other issuance refusals with creation: both methods issue the key the same way. These are the issuance refusals with status 409, 410 and 502, with status 503 — NETWORK_DEVKEY_REQUIRED and CONNECTOR_KEY_ISSUE_TIMEOUT, and with status 403 — B24_INSUFFICIENT_SCOPE, B24_ACCESS_DENIED and CONNECTOR_KEY_ISSUE_FORBIDDEN. Their descriptions and the error.reason field are in Create an API key. Status 503 arrives both from issuance and from the account address check, so branch on the code, not on the status: PORTAL_ORIGIN_CUTOVER_UNAVAILABLE from the table above may be retried later, while CONNECTOR_KEY_ISSUE_TIMEOUT must not be retried right away — the webhook may already exist on the Bitrix24 account. The check of the Bitrix24 account's access to the platform does not apply to rotation, so 402 INT_TARIFF_REQUIRED and 403 PORTAL_TARIFF_UNREADABLE do not arrive here. With status 402, rotation receives only Bitrix24's own refusal to issue the key due to the plan — INT_VIBE_PLUS_REQUIRED.
The OWNER_DELETED, OWNER_BLOCKED, ACCOUNT_PENDING_ERASURE and B24_USER_DELETED refusals depend on the key owner's account — see Owner account state.
Full list of common API errors — Errors.
Known specifics
What is carried over to the new key. The name, scopes, allowed IP addresses, access mode, individual limit and the totalDefault setting are carried over from the previous key as they are. The previous key's servers, applications, access tokens, deploy operations and bots move to the new one. The expiry is not carried over: the new key does not expire.
The previous key's transition period is up to 24 hours. The previous key keeps working for 24 hours after rotation, then becomes invalid. If its own expiry comes earlier, that expiry is kept: rotation does not extend the key and does not revive an expired one.
A blocked key can be rotated too. Rotation is the only way to replace a key in BLOCKED status; there is no unblocking. The previous key stays blocked and gets no transition period, and its access tokens are revoked rather than moved to the new key.
A 500 response means the new key was created, but the replacement was rolled back. The new key's value is not in the response. Its ID arrives in error.details.orphanKeyId. The error.details.strandedSlots field appears if the rollback could not return something to the previous key, and lists it: applications, servers, accessTokens, deployOperations, bots, previousKeyGrace. If the field is absent or has no previousKeyGrace, the previous key keeps its previous expiry. Next steps:
- The
strandedSlotsfield is present — contact support. Do not delete the new key while anything still points at it. - The
strandedSlotsfield is absent — delete the new key withDELETE /v1/keys/:idusing the ID fromorphanKeyId. After the204response, rotate the previous key once more. If the deletion returns409 KEY_ROTATION_RECOVERY_REQUIRED, contact support.
Until the replacement is finished, rotating or deleting either the previous or the new key returns 409 KEY_ROTATION_RECOVERY_REQUIRED, so retrying the rotation without these steps fails. The exception is deleting the new key as described in the second item.
A refusal for a platform-issued key leaves the previous key untouched. On a SYSTEM_KEY_ROTATE_FORBIDDEN refusal or a Cowork/Code subscription refusal, the previous key keeps working. Ordinary dashboard keys are unaffected by these checks. What a subscription key can do — Your own agent on the subscription.