## Create an API key

`POST /v1/keys`

Creates a personal API key on the specified Bitrix24 account on behalf of the management key owner. Fields are passed flat at the root of the JSON body.

## Request fields (body)

| Field | Type | Required | Description |
|------|-----|:-----:|----------|
| `portalId` | string | yes | Bitrix24 account ID. List: [`GET /v1/portals`](/docs/management-keys/portals). The management key owner must be a member of this Bitrix24 account |
| `name` | string | yes | Key name, up to 255 characters. A whitespace-only string is not accepted |
| `scopes` | array | yes | [Scopes](/docs/scopes) of the new key, at least one. Without `exactScopes: true`, four platform scopes are added to them — see below |
| `exactScopes` | boolean | no | `true` — store exactly the listed scopes, without the four platform ones. Default `false` |
| `mode` | string | no | Key access mode: `READWRITE` or `READONLY`. Without the field, the new-key mode set on the Bitrix24 account applies |
| `ipWhitelist` | array | no | Allowed IP addresses. Without the field or with an empty array — no restriction |
| `expiresAt` | string \| null | no | Expiration date (ISO 8601). Without the field or `null` — never expires |
| `rateLimit` | number \| null | no | Only `null` is accepted. A number from 1 to 100,000 is rejected with `403 RATE_LIMIT_ADMIN_ONLY`, a number outside this range with `400 VALIDATION_ERROR` |
| `billing` | string | no | Only `wallet` is accepted, which is also the default. Keys billed to the Cowork/Code subscription are issued in your Vibecode account — see [Your own agent on the subscription](/docs/cowork/harness) |

Four platform scopes are added to the requested ones: `vibe:infra`, `vibe:ai`, `vibe:search`, `vibe:storage`. The key from the examples below gets `crm`, `task` and these four.

On a Bitrix24 account where the administrator has turned server management off, `vibe:infra` is dropped from this set without an error — the key is issued with the other three. The `403 INFRA_DISABLED_FOR_PORTAL` refusal arrives only for a scope explicitly listed in `scopes`.

To get a key with exactly the listed scopes, pass `exactScopes: true`. The body `{"portalId": "…", "name": "Storage only", "scopes": ["crm", "vibe:storage"], "exactScopes": true}` issues a key with only `crm` and `vibe:storage`, and it answers `POST /v1/infra/servers` and AI calls with `403`. The scopes of such a key are final: the platform does not widen them at request time, so [`GET /v1/me`](/docs/keys-auth/me) returns exactly what was stored at issue time.

The `placement`, `entity` and `userfieldtype` scopes are unavailable to a personal key and are not stored: the `scopes` of the created key in the response do not contain them, even when they were in the request alongside data scopes. The `userfieldconfig` scope is stored.

## Examples

This endpoint accepts only a management key — see [Limitations](/docs/management-keys#limitations).

### curl — management key

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/keys" \
  -H "X-Api-Key: YOUR_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "portalId": "0d9f7744-f853-47c7-b06e-6f61f9dd1b7f",
    "name": "Deal export",
    "scopes": ["crm", "task"]
  }'
```

### JavaScript — management key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/keys', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_MANAGEMENT_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    portalId: '0d9f7744-f853-47c7-b06e-6f61f9dd1b7f',
    name: 'Deal export',
    scopes: ['crm', 'task'],
  }),
})

const { success, data } = await res.json()
// The key value arrives only once — save it to a secrets store
const apiKey = data.rawKey
console.log('Key created:', data.id, data.scopes)
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | Always `true` on success |
| `data.id` | string | Key record ID. It is accepted by [`GET /v1/keys/:id`](/docs/management-keys/get), [`PATCH /v1/keys/:id`](/docs/management-keys/update), [`DELETE /v1/keys/:id`](/docs/management-keys/delete) and [`POST /v1/keys/:id/rotate`](/docs/management-keys/rotate) |
| `data.name` | string | Key name |
| `data.type` | string | Key record type, always `APP` |
| `data.userId` | string | Key owner ID — the management key owner |
| `data.portalId` | string | Bitrix24 account ID |
| `data.clientId` | string \| null | OAuth client ID, if one is recorded on the key. Otherwise `null` |
| `data.scopes` | array | [Scopes](/docs/scopes) stored on the key: the requested ones plus the added platform ones, without those unavailable to a personal key |
| `data.ipWhitelist` | array | Allowed IP addresses. An empty array — no restriction |
| `data.expiresAt` | string \| null | Expiration date (ISO 8601), `null` — never expires |
| `data.rateLimit` | number \| null | Individual request limit for AI endpoints, `null` — the platform-wide limit |
| `data.status` | string | Key status, `ACTIVE` for a new key |
| `data.lastUsedAt` | string \| null | Date of the last request made with this key (ISO 8601), `null` for a new key |
| `data.createdAt` | string | Creation date (ISO 8601) |
| `data.updatedAt` | string | Last modification date (ISO 8601) |
| `data.accessMode` | string | `READWRITE` or `READONLY` |
| `data.totalDefault` | boolean \| null | Whether `meta.total` is returned in lists requested with this key. `null` — the platform setting applies |
| `data.issuedVia` | string \| null | Channel through which the 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 via Bitrix24 Network, `none` — no webhook was created, the key carries only `vibe:*` scopes |
| `data.rawKey` | string | Full key value. Arrives only in this response — save it immediately |

## Response example

```json
{
  "success": true,
  "data": {
    "id": "3c1f6a52-8e4b-4d07-9a1e-5b2d7f0c6e91",
    "name": "Deal export",
    "type": "APP",
    "userId": "fafa9780-eed7-4d29-b040-2448d924f452",
    "portalId": "0d9f7744-f853-47c7-b06e-6f61f9dd1b7f",
    "clientId": null,
    "scopes": ["crm", "task", "vibe:infra", "vibe:ai", "vibe:search", "vibe:storage"],
    "ipWhitelist": [],
    "expiresAt": null,
    "rateLimit": null,
    "status": "ACTIVE",
    "lastUsedAt": null,
    "createdAt": "2026-10-29T12:23:14.701Z",
    "updatedAt": "2026-10-29T12:23:14.701Z",
    "accessMode": "READWRITE",
    "totalDefault": null,
    "issuedVia": "cloud-shared",
    "rawKey": "vibe_api_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  }
}
```

## Error response example

400 — `portalId` not provided:

```json
{
  "success": false,
  "error": {
    "code": "MISSING_PORTAL_ID",
    "message": "Field portalId is required"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `MISSING_PORTAL_ID` | `portalId` is not specified in the request body |
| 400 | `VALIDATION_ERROR` | The body failed validation: no `name` or `scopes`, empty `scopes`, an unknown scope, an invalid IP address or date. The field and the reason are in `error.message` |
| 400 | `INVALID_JSON_BODY` | The request body is invalid JSON |
| 400 | `BILLING_MODE_NOT_SUPPORTED` | The body carries `billing: "subscription"` |
| 400 | `PERSONAL_KEY_WEBHOOK_SCOPES_INVALID` | `scopes` contains no data scope. A personal key is issued as a Bitrix24 incoming webhook, while the `placement`, `entity` and `userfieldtype` scopes require an application context and are unavailable to a personal key. Add at least one data scope, for example `crm` or `user_brief`, or register an OAuth application for application features |
| 400 | `INVALID_SCOPES` | Some of the requested scopes are unavailable on the Bitrix24 account |
| 400 | `PORTAL_NOT_LINKED` | The Bitrix24 account is not connected to Bitrix24 Network — the key cannot be created |
| 400 | `BOX_NO_DEVELOPER_KEY` | The Bitrix24 account is self-hosted, the owner has no developer key configured, and `scopes` contains Bitrix24 scopes |
| 402 | `INT_TARIFF_REQUIRED` | The Bitrix24 account is on a free Bitrix24 plan |
| 402 | `SELFHOSTED_NOT_AVAILABLE` | The Bitrix24 account is self-hosted, and self-hosted Bitrix24 is not yet served on this installation. Buying a plan does not clear the refusal — contact support |
| 402 | `ACCOUNT_FROZEN` | The account's billing account is frozen due to a negative balance |
| 403 | `NOT_PORTAL_MEMBER` | The management key owner is not a member of the Bitrix24 account with the specified `portalId` |
| 403 | `PORTAL_ACCESS_BLOCKED` | The owner's access to the Bitrix24 account is disabled: their membership is inactive or forbidden by the account's access policy |
| 403 | `PORTAL_TARIFF_UNREADABLE` | The account plan could not be read, and the response does not name the missing plan: the `error.details.requiredTariffs` field is empty and the button leads to support. Buying a plan does not clear the refusal |
| 403 | `KEY_CREATION_RESTRICTED` | The Bitrix24 account policy does not allow the owner to create keys. The right is granted by the account administrator — see [Creation rights](/docs/access-rights) |
| 403 | `KEY_POLICY_READONLY_REQUIRED` | The Bitrix24 account policy allows creating only `READONLY` keys, and `mode` requests `READWRITE` |
| 403 | `RATE_LIMIT_ADMIN_ONLY` | `rateLimit` carries a number. An individual limit cannot be set through this endpoint |
| 403 | `INFRA_DISABLED_FOR_PORTAL` | `scopes` explicitly requests `vibe:infra`, and the Bitrix24 account administrator has turned server management off. The key is not created. A request without this scope goes through on such an account as well |
| 403 | `SCOPE_NOT_AVAILABLE_ON_PORTAL` | `scopes` contains a scope that is not enabled on this Bitrix24 account. The list of such scopes is in `error.message` |
| 403 | `KEY_STORAGE_SCOPE_REQUIRES_PERSONAL_KEY` | `scopes` explicitly requests `vibe:storage`, and the management key is not confirmed as the owner's personal key. The refusal arrives with `exactScopes: true` as well. A management key created in your account does not get this refusal |
| 403 | `INT_TARIFF_REQUIRED` | The account's Bitrix24 plan does not grant access, so the webhook is not issued. The response carries no upgrade link |
| 403 | `B24_INSUFFICIENT_SCOPE` | The owner's developer key lacks rights on the account. Reconnect the account so the developer key is issued again |
| 403 | `B24_ACCESS_DENIED` | Bitrix24 did not issue the webhook because of the employee's rights |
| 403 | `CONNECTOR_KEY_ISSUE_FORBIDDEN` | The account forbids this employee from issuing keys. The right is granted by the account administrator — see [Creation rights](/docs/access-rights) |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The management key is in read-only mode |
| 403 | `MANAGEMENT_SCOPE_REQUIRED` | The management key does not carry the `vibe:mgmt:keys` scope |
| 409 | `CONNECTOR_MODULE_NOT_INSTALLED` | The connector module is not installed on the account, so a key cannot be issued through it. Retrying without installing the module will not help |
| 410 | `STALE_DEVELOPER_KEY` | The owner's developer key is invalid and is not re-issued automatically. Reconnect the account |
| 502 | `CONNECTOR_PLAN_REQUIRED` | The Bitrix24 account plan does not include Vibecode, and there is nothing to offer — a self-hosted account, an account already on a paid plan, an unrecognized region. The human-readable reason is in `error.userMessage`. Retrying will not help until the plan changes |
| 502 | `CONNECTOR_REST_UNAVAILABLE` | The account's plan or trial period is active, but Bitrix24 refused the issuance. The original reason is in `error.details.reason`, the state is transient |
| 502 | `CONNECTOR_KEY_ISSUE_FAILED` | Any other refusal from the connector module, or its transport is unavailable. The original reason is in `error.details.reason` |
| 502 | `CONNECTOR_KEY_ISSUE_RESPONSE_LOST` | The connector module's response was lost, and the webhook may already exist on the Bitrix24 account. Do not retry the issuance right away |
| 502 | `RECOVERY_FAILED` | Re-issuing the developer key failed, the state is transient — retry the request |
| 502 | `DEVKEY_MINT_FAILED` | Any other issuance failure through the developer key. Diagnostics are in `error.details.b24Code` and `error.details.b24Status` |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 did not register the incoming webhook: it did not respond or refused without a stated reason. In the second case the response carries `error.details.incidentCode` — a six-character support reference that support uses to find the log entry |
| 503 | `NETWORK_DEVKEY_REQUIRED` | The owner's developer key has not been issued yet, and on this Bitrix24 account issuance goes only through it. The human-readable text is in `error.userMessage` |
| 503 | `CONNECTOR_KEY_ISSUE_TIMEOUT` | The connector module did not respond in time, and the webhook may already exist on the Bitrix24 account. Do not retry the issuance right away |
| 401 | `WRONG_KEY_TYPE` | The 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 |
| 401 | `KEY_INACTIVE` | The management key is revoked or blocked |
| 401 | `KEY_EXPIRED` | The management key has expired |
| 401 | `INVALID_API_KEY` | No key with the provided value was found |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |

The `OWNER_DELETED`, `OWNER_BLOCKED`, `ACCOUNT_PENDING_ERASURE` and `B24_USER_DELETED` refusals depend on the key owner's account — see [Owner account state](/docs/management-keys#owner-account-state).

**Two platform access checks.** The first is the Bitrix24 account access check. A commercial Bitrix24 plan grants full access, a demo plan grants a trial, and an account on a free plan gets `402 INT_TARIFF_REQUIRED`. A self-hosted Bitrix24 account where self-hosted Bitrix24 is not yet served gets `402 SELFHOSTED_NOT_AVAILABLE`. One more outcome of the same check comes with status `403` and means the platform has nothing to name: `PORTAL_TARIFF_UNREADABLE`. This check applies only to issuing a new key: [rotation](/docs/management-keys/rotate), automatic key recovery and ownership handover are not subject to it, and already issued keys keep working.

The second check runs later, and its reach is wider: Bitrix24 itself refuses the issuance because the account plan does not include Vibecode. It fires on everything that issues a key — creation, [rotation](/docs/management-keys/rotate), automatic key recovery and ownership handover. Where access can be purchased, the response is `402 INT_VIBE_PLUS_REQUIRED`. Where there is nothing to offer — `502 CONNECTOR_PLAN_REQUIRED`. Retrying a request with either code will not help until the Bitrix24 plan changes.

**Refusals shared with rotation.** The issuance refusals themselves also arrive on rotation — which ones exactly is listed in [Rotate an API key](/docs/management-keys/rotate#errors). The body of an issuance refusal may carry an `error.reason` field with the exact cause in the platform's internal classification, while `error.code` stays the same. Branch on `error.code`, not on the `message` text.

Full list of common API errors — [Errors](/docs/errors).

## See also

- [List Bitrix24 account API keys](/docs/management-keys/list)
- [Get an API key](/docs/management-keys/get)
- [Update an API key](/docs/management-keys/update)
- [Delete an API key](/docs/management-keys/delete)
- [Rotate an API key](/docs/management-keys/rotate)
- [List Bitrix24 accounts](/docs/management-keys/portals)
- [Scopes](/docs/scopes)
- [Management keys](/docs/management-keys)
