## Project deployment key

`POST /v1/cowork/deploy-key`

A Cowork key with the `vibe:cowork` scope works with data only — chat and the Bitrix24 REST proxy — and has no access to the infrastructure control plane. Requests to create a server, deploy, execute commands, or manage the server lifecycle all return `403 INFRA_FORBIDDEN_FOR_COWORK_KEY`. This endpoint issues a separate **project key with deployment rights**: call it with the same Cowork key, take the `key` field from the response — the body is flat, without a `data` wrapper — and use it as the `X-Api-Key` header for operations under `/v1/infra/*` within the key's access mode — see "Known specifics". A subscription key issued for a third-party agent gets no project key from this endpoint, which returns `403 COWORK_HARNESS_KEY_FORBIDDEN`: with such a key, deploy from the Cowork desktop app or with an ordinary key that has the `vibe:infra` scope, see [Your own agent on the subscription](/docs/cowork/harness).

The returned key has the `vibe:infra` and `vibe:storage` scopes (without `vibe:cowork`), is valid for 7 days, and is bound to the same owner and Bitrix24 account as the Cowork key. Each call issues a new key and revokes the previous project key — only one is active at a time. The issued key cannot grant broader access than either the calling Cowork key or the subscription's access mode. The subscription's mode applies to all of the employee's devices on this Bitrix24 account: a read-only subscription yields a read-only deployment key.

**Scope:** `vibe:cowork` (the Cowork key you call the endpoint with). The returned key is separate and has the `vibe:infra` + `vibe:storage` scopes.

## Examples

Only Cowork keys carry the `vibe:cowork` scope. Personal keys and OAuth application keys do not have this scope; requests using them return `403 INSUFFICIENT_SCOPE`.

### curl — Cowork key

```bash
curl -X POST https://vibecode.bitrix24.com/v1/cowork/deploy-key \
  -H "X-Api-Key: YOUR_COWORK_KEY"
```

### JavaScript — Cowork key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/cowork/deploy-key', {
  method: 'POST',
  headers: { 'X-Api-Key': 'YOUR_COWORK_KEY' },
})
const deployKey = await res.json()
// deployKey.key — the project key for calls under /v1/infra/*
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `key` | string | The raw key. It is returned ONCE and cannot be recovered — save it. |
| `apiKeyId` | string | Identifier of the created key |
| `prefix` | string | Key prefix for display |
| `suffix` | string | Last characters of the key for identification |
| `scopes` | array | `["vibe:infra", "vibe:storage"]` |
| `expiresAt` | string | Expiration (ISO 8601), 7 days from issuance |
| `accessMode` | string | Access mode of the issued key — `READONLY` or `READWRITE`. Cannot grant broader access than either the calling key or the Cowork subscription |
| `repointTruncated` | boolean | `true` — there were more source keys than a single issuance transfers; the rest are transferred on the next call |
| `applicationSlotBlocked` | boolean | `true` — an application card stayed on its previous key because the new key's slot is already taken by another card |
| `howToUse` | string | A hint for the agent: how to use the key |

## Response example

```json
{
  "key": "vibe_api_…",
  "apiKeyId": "796877da-0c3b-409b-900f-b5e0d34a5527",
  "prefix": "vibe_api_…",
  "suffix": "d29c",
  "scopes": ["vibe:infra", "vibe:storage"],
  "expiresAt": "2026-09-29T22:31:18.701Z",
  "accessMode": "READWRITE",
  "repointTruncated": false,
  "applicationSlotBlocked": false,
  "howToUse": "Use this key as the X-Api-Key header for all deploy / provision / exec / server-lifecycle calls under /v1/infra/*. Do NOT use the Co-work key for those. This key expires in 7 days; call POST /v1/cowork/deploy-key again to obtain a fresh one. Keep using the Co-work key for chat and Bitrix24 data calls."
}
```

## Error response example

403 — the request used a third-party agent key issued for the subscription:

```json
{
  "success": false,
  "error": {
    "code": "COWORK_HARNESS_KEY_FORBIDDEN",
    "message": "A subscription key issued for an external agent cannot mint a project deploy key. Use the Cowork/Code desktop app."
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not provided |
| 401 | `INVALID_API_KEY` | The key is not recognized — no such key exists on the platform |
| 402 | `ACCOUNT_FROZEN` | The portal balance is exhausted — top it up |
| 402 | `INT_TARIFF_REQUIRED` | The portal is on a free Bitrix24 plan — issuing a project key requires a commercial plan (a demo Bitrix24 plan grants a limited trial) |
| 402 | `INT_VIBE_PLUS_REQUIRED` | Account access is narrowed to a paid Vibe+ plan, so an ordinary commercial Bitrix24 plan is no longer enough. An account on a demo Bitrix24 plan keeps its trial access and does not receive this code |
| 403 | `INSUFFICIENT_SCOPE` | The key lacks the `vibe:cowork` scope |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The request used a Cowork key in read-only mode, and issuing a project key is a write. No new key is issued, and the one already issued keeps working |
| 403 | `COWORK_NOT_ACTIVATED` | No active Cowork subscription for the user–portal pair |
| 403 | `COWORK_HARNESS_KEY_FORBIDDEN` | The request used a third-party agent key issued for the subscription. Such a key cannot obtain a project deployment key — see [Your own agent on the subscription](/docs/cowork/harness) |
| 403 | `PORTAL_TARIFF_UNREADABLE` | The account plan could not be read, so the response does not name which plan is required: `error.details.requiredTariffs` is empty and the button points to support. Buying a plan does not resolve this error |
| 403 | `INFRA_NOT_PERMITTED` | Infrastructure is disabled for this portal |
| 403 | `SERVER_CREATION_DISABLED` | The portal forbids server creation for all employees |
| 403 | `SERVER_CREATION_ADMINS_ONLY` | The portal restricts server creation to administrators or to a named list of employees, and the key owner is not among them |
| 409 | `B24_USER_DELETED` | The Bitrix24 employee who owns the calling Cowork key is no longer active on the Bitrix24 account — no key can be issued for them. This state is permanent: retrying will not help; the employee must be reactivated on the Bitrix24 account |
| 415 | `FST_ERR_CTP_INVALID_MEDIA_TYPE` | The request body has a content type that this route does not parse. Send the request with no body, or with an empty body and the `Content-Type: application/json` header |
| 429 | `RATE_LIMITED` | The rate limit for the portal and key-owner pair has been exceeded. The platform-wide limit is 3 requests every 5 minutes. The effective limit for your key is returned in the `x-ratelimit-limit` header. |
| 503 | `COWORK_FEATURE_DISABLED` | Cowork is disabled at the platform level |
| 503 | `DEPLOY_KEY_DISABLED` | Self-service issuance of project keys is disabled at the platform level |
| 503 | `INFRA_DISABLED` | Infrastructure is disabled at the platform level — the key would serve no purpose |

See [Errors](/docs/errors) for the full list of common API errors.

## Known specifics

**The raw key (`key`) is returned only once**, when it is issued. It cannot be recovered afterward. Each call issues a new key and revokes the previous project key, so call the endpoint once at the start of the deployment session and use the key you receive for the rest of the session.

**Issuance moves everything that was bound to the previous keys onto the new key.** Server ownership, the application card, live access tokens, and the visibility scope of started deployment operations are transferred in the same transaction that issues the key. Therefore, the [server list](/docs/infra/servers/list), retrieving a specific server, publishing, and [reading an operation's outcome](/docs/infra/deploy/operation-status) all work under the new key immediately after the response, including container applications in Galaxy mode.

**Bindings are transferred from more than just the currently active key.** Issuance also transfers bindings from previously revoked project keys for the same owner and Bitrix24 account: up to five keys per call, starting with the most recent. If more keys than that hold bindings, subsequent issuances transfer the rest; no separate action is required. Servers that disappeared from the list after earlier reissues therefore reappear after another key request, without manual action in your Vibecode account.

**Exactly one application card moves per call.** A key holds a single card slot, so if the owner–account pair has several cards, the first one takes the slot on the new key and the rest stay on their previous keys. The `applicationSlotBlocked` field in the response tells you that a card stayed on its previous key; the response does not name which card it is, so check which card is visible under the new key.

**A project key does not bypass the read-only mode.** For a Cowork key in read-only mode, the endpoint answers `403 WRITE_BLOCKED_READONLY_KEY`: issuing a key is a write. No new project key is issued, and the one already issued keeps working. A read-and-write key associated with a read-only Cowork subscription gets a project key in `READONLY` mode — the `accessMode` field shows it. Such a key can deliver to a server it already owns — these operations are listed on the [Access mode](/docs/keys-auth/access-mode) page. Requests to create a server, manage its lifecycle, write to source storage, or publish an application return `403 WRITE_BLOCKED_READONLY_KEY`. Requesting a new key does not grant broader access and revokes the previous project key. To perform these operations, enable read-and-write access for the subscription by changing the mode of the Cowork desktop key in the [API Keys](/keys) section — or use an ordinary read-and-write key with the `vibe:infra` scope.

**Use the top-level `key` field from the response for deployment, NOT the Cowork key.** Continue using the Cowork key for chat and Bitrix24 REST calls; use the project key for `/v1/infra/*`.

**`403 INFRA_FORBIDDEN_FOR_COWORK_KEY` is returned for every write operation, not only for deployment.** A Cowork key cannot call any `/v1/infra/*` method except reads (`GET`). Outside infrastructure, it also cannot publish or unpublish an application or write to source storage. The restriction does not apply to reads, but a Cowork key sees only resources it owns: issuance transfers server ownership to the project key, so the server list is empty and reading a specific server returns `404 NOT_FOUND`. Source version history remains available to a Cowork key because access there is determined per employee rather than per key. A demo Bitrix24 plan grants trial access. The key restrictions also distinguish between `POST /v1/cowork/activate-market-trial`, which requires a Cowork desktop key and does not return this code, and [`POST /v1/portals/:id/activate-market-trial`](/docs/activate-market-trial): an untagged `vibe:cowork` agent-seat key (`purpose = null`) reaches this guard and receives `INFRA_FORBIDDEN_FOR_COWORK_KEY`; a third-party agent subscription key has a non-null `purpose` and is rejected earlier with `PURPOSE_KEY_FORBIDDEN`. These endpoints do not activate trial access for an international Bitrix24 account.

**The project key never appears in `error.details.deployableKeys`.** The `403 INFRA_FORBIDDEN_FOR_COWORK_KEY` error response includes `error.details.requiredAction` with the steps to take, while `deployableKeys` lists the owner's ordinary keys that can deploy — up to the five most recent, never the secret itself. For a third-party agent key, `requiredAction` names the Cowork desktop app and an ordinary key with the `vibe:infra` scope instead of this endpoint. The key issued by this endpoint has a special-purpose tag, and the filter selects only untagged keys, so an empty list does NOT mean you have nothing to deploy with. Keep using the project key you already have: another request issues a new one and revokes the previous one. The `deployableKeys` field is absent when the platform cannot resolve the key owner; this differs from an empty list.

**A successful response (200) is the object itself, without a `success` wrapper.** Errors arrive in the envelope `{ success: false, error: { code, message } }`. Determine success by the HTTP status (`res.ok`).

## See also

- [Cowork](/docs/cowork)
- [Infrastructure](/docs/infra)
- [Errors](/docs/errors)
