
## Create a server

`POST /v1/infra/servers`

Creates an application at a cloud provider, always in Black Hole mode — iptables blocks all inbound ports, so the application is invisible from the internet. On some servers the rules keep one service entry: SSH access from a single platform address, which the platform needs to reinstall the agent.

There are two placement models, and what to expect after creation depends on the one your Bitrix24 account uses:

- **A dedicated virtual machine** (the default model). The platform reserves the server and responds immediately with the `provisioning` status and no IP address, then prepares the machine after the response, which takes 1–3 minutes. Poll [`GET /v1/infra/servers/:id`](./get.md) until the status is `running`, `error` or `sleeping`. A cloud provider refusal arrives later: the server gets `status: "error"` and the reason is in `provisionError`. An account hold set before the machine is ordered ends the same way. A hold set after the machine was ordered leaves the server as `status: "sleeping"` (machine stopped) with an empty `provisionError`. The response contains no SSH credentials: the server runs in Black Hole mode and inbound SSH is closed. A person turns on OPEN mode in the Vibecode dashboard ([`PATCH /v1/infra/servers/:id/mode`](/docs/infra/access/mode) to `OPEN` answers `403 DIRECT_NETWORK_CHANGE_REQUIRES_HUMAN` for any API key), and only then does [`GET /v1/infra/servers/:id/ssh`](/docs/infra/access/ssh) return the password and key. With an API key alone, use the [Deploy API](/docs/infra/deploy) (`exec`, `upload`, `logs`). This page describes that model in the request fields, response fields, and error table below.
- **A Galaxy application** — a container on a shared host. If your Bitrix24 account places apps in galaxies, the same `POST /v1/infra/servers` creates a Galaxy application instead of a virtual machine. In the response, this model is marked by `createdVia: "galaxy"`. The ["Galaxy application"](#galaxy-application) section below describes the two startup scenarios, and the full model is on the [Galaxy app](/docs/infra/galaxy) page.

## Galaxy application

If your Bitrix24 account places apps in galaxies, `POST /v1/infra/servers` creates a **Galaxy application** — a container on a shared host rather than a dedicated virtual machine. The request and the call order are the same, but the lifecycle differs. In the response, this model is marked by `createdVia: "galaxy"`. The full model, cost, and deploy differences are on the [Galaxy app](/docs/infra/galaxy) page.

**Do not wait for `CONNECTED` before uploading the code.** A Galaxy application **never** reaches `blackholeStatus: "CONNECTED"` on its own — its container is built when you upload the code. If you just poll the status, the app stays in `provisioning`, and after about 20 minutes the platform marks it as `error` and records in the `provisionError` field that the code was never uploaded. So upload the code right away — using one of the two scenarios below.

### Scenario 1 — transparent, recommended

A single request. Pass the application code in the `source` field of `POST /v1/infra/servers` together with `runtime` and `start` (and, if needed, `install`, `env`, `port`). The build runs in the background. After the response, poll [`GET /v1/infra/servers/:id`](./get.md) until `status: "running"`. A separate code-upload call is not needed.

```bash
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-crm-app",
    "displayName": "My CRM bot",
    "source": { "content": "<base64-archive>" },
    "runtime": "node24",
    "start": "node index.js",
    "install": "npm ci",
    "port": 3000
  }'
```

Fields `source`, `runtime`, `start`, `install`, `env`, `port`, and `healthPath` have the same meaning as in [`POST /:id/deploy`](/docs/infra/deploy/deploy). They are listed under [Request fields (body)](#request-fields-body).

The one-shot create accepts no other deploy fields: `preStart`, `systemd`, `serviceName`, `cleanDeploy`, `extractTo`, `hardening`, `preserveEnv`, `dataDirs`, `dataDirsRecursive` return `400 UNKNOWN_PARAM`, and the full list of accepted fields arrives in `details.validParams`. These fields belong to a dedicated virtual machine deploy and do not apply to a Galaxy application — put the commands that must run before the application starts into `install`.

With `source`, `runtime` is required, and `start` is required for every runtime except `static`. A missing required field returns `400`. On a Bitrix24 account without Galaxy mode, a fresh request carrying `source` is forbidden and returns `400 SOURCE_AT_CREATE_GALAXY_ONLY`. A retry that safely converges on an in-flight keyless `GALAXY_APP` does so before that fresh-only refusal: it returns the existing id with `sourceIgnored: true` and never starts a second build.

During the trial period, first read the `deployment` and `capabilities.servers.create` blocks from [`GET /v1/me`](/docs/keys-auth/me). When `galaxyApp` is present, a one-shot create is worth trying, but the block reflects stored state and does not guarantee success: only the `POST` checks live capacity. When Galaxy mode is disabled, `placementNote` is absent and a request carrying `source` returns `400 SOURCE_AT_CREATE_GALAXY_ONLY`. With no host or in dedicated-machine mode, the two-step path is available only while `capabilities.servers.create.available` is `true`; select an allowed plan ID from `capabilities.servers.create.limits.allowedPlans`. When an existing host is unsuitable, wait for it to recover, ask an administrator to repair it, or upgrade the plan before retrying one-shot creation. While `available` is `false`, do not switch to a dedicated machine: with trial enforcement active, the occupied slot returns `402 TRIAL_PORTAL_LIMIT`. After removing the host, re-read `GET /v1/me` and use the two-step path advertised there.

### Scenario 2 — two-step

Create the application **without** `source` — the response comes with the `provisioning` status and the hint `next: "deploy"`. Immediately call [`POST /v1/infra/servers/:id/deploy`](/docs/infra/deploy/deploy) with the code in `source`, the runtime, and the start command. Upload the code right away, without waiting for `CONNECTED`.

```bash
# Step 1 — create the application without code
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "provider": "bitrix-cloud", "name": "my-crm-app", "plan": "bc-medium", "region": "bc-eu-central" }'

# The response contains data.id, data.next = "deploy", and data.hint.
# Step 2 — upload the code right away, without waiting for CONNECTED
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/deploy \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "content": "<base64-archive>" },
    "runtime": "node24",
    "start": "node index.js",
    "port": 3000
  }'
```

`provider` and `plan` are required by the schema for a create without `source`. `region` is optional: omit it and the platform resolves the chosen provider's default region. For a Galaxy application the values are informational — the app inherits its host's provider, plan and region.

When a value you sent differs from the one the app actually got, the response carries an entry in `warnings[]` next to `data`: it names the diverging fields, shows both the value you sent and the one in force, and warns that re-sending will change nothing. Fields that matched are not mentioned, so a correct call stays free of warnings. If you need a machine whose provider, plan and region you choose, create it with `placement` set to `dedicated`.

### A dedicated virtual machine instead of a Galaxy app

If your Bitrix24 account places apps in galaxies but this particular app needs its own server, pass the `placement` field set to `dedicated`. The `provider` + `plan` pair is required in that case; `region` stays optional. A `source` field in such a request returns `400 SOURCE_AT_CREATE_GALAXY_ONLY`: a dedicated virtual machine is deployed in two steps, and the code is uploaded via [`POST /:id/deploy`](/docs/infra/deploy/deploy).

```bash
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "plan": "bc-small",
    "region": "bc-eu-central",
    "placement": "dedicated"
  }'
```

The response carries `kind` set to `STANDALONE`. From there the dedicated virtual machine flow applies: wait for `status: "running"` and `blackholeStatus: "CONNECTED"`, then deploy.

### Diagnostics and environment variables

- **The `provisionError` field.** If the build or startup failed, [`GET /v1/infra/servers/:id`](./get.md) returns the failure reason in `data.provisionError` — plus the tail of the Docker build log when the image build failed. Read this field to find the cause. Internal and unknown provisioning errors are returned by GET/list as `provisionErrorCode: "GENERIC"` with a neutral message. Contact support before creating another server: the outcome of the previous order may be unknown. Public categories and hold reasons are listed in the [GET fields](./get.md).
- **`env` variables are injected at start.** Values from `env` are passed into the container at start time via `docker run --env`, not baked into the image. The platform passes `PORT` alongside them (equal to the `port` field, `3000` by default): the `PORT` key is reserved, your own value is overridden, and the response carries a `warnings[]` entry — see [POST /v1/infra/servers/:id/deploy](../deploy/deploy.md). So API keys and secrets from `env` do not end up in the image layers.

## Request fields (body)

| Field | Type | Required | Description |
|------|-----|:-----:|----------|
| `provider` | string | yes | Provider ID from [`GET /v1/infra/providers`](../providers/list.md), for example `bitrix-cloud`. 1–50 characters |
| `name` | string | yes | System name of the server — the technical identifier. 2–63 characters, only lowercase Latin letters, digits, and `-`, with a letter as the first character. Pattern: `^[a-z][a-z0-9-]*$`. Used in the subdomain URL (`app-<hex>` is generated separately), logs, and the audit log. **Immutable after creation.** For a human-readable name, use `displayName` |
| `displayName` | string | no | Human-readable name in any language or script (emoji included). 2–100 characters, no control bytes. Displayed in the UI, server-down notifications, security alerts, billing line items, and the Bitrix24 catalog. If not passed, `name` is used instead. The `name` itself remains the technical identifier, immutable, and is used in the subdomain URL and logs |
| `description` | string | no | Application description shown on the Bitrix24 catalog card, up to 500 characters. Line breaks and tabs are allowed; other control bytes are rejected. The value is trimmed at both ends. If not passed, the description stays empty; you can set it later via [`PATCH /v1/infra/servers/:id`](./update.md) |
| `plan` | string | yes | Plan ID from [`GET /v1/infra/providers/:providerId/plans`](../providers/plans.md), for example `bc-small` |
| `region` | string | no | Region ID from [`GET /v1/infra/providers/:providerId/regions`](../providers/regions.md), for example `bc-eu-central`. Omit it and the platform resolves the provider's default region |
| `image` | string | no | Public OS image identifier from [`GET /v1/infra/providers/:providerId/images`](../providers/images.md). Stable family identifiers such as `ubuntu-2404-lts` are accepted; the platform resolves them to the provider's current concrete image. If omitted, the provider's default image is resolved |
| `sshPublicKey` | string | no | Your SSH public key (`ssh-rsa …`, `ssh-ed25519 …`, `ecdsa-sha2-nistp256/384/521 …`, security keys). Up to 8192 characters. The platform installs it alongside its own key while the machine is being prepared. You can log in over SSH only once a person has switched the server to OPEN mode in the Vibecode dashboard |
| `placement` | string | no | Placement model: `auto` (default) or `dedicated`. On a Bitrix24 account in Galaxy First mode (`galaxies-only`), the value `dedicated` creates a dedicated virtual machine instead of a Galaxy application — "graduating" the application onto its own server. It passes the same checks as a normal server create: the `serverCreation` policy and the per-user server quota. With `auto` the behavior is unchanged (in Galaxy First mode a Galaxy application is created) |
| `graduateFrom` | string | no | The identifier of your Galaxy application (`kind=GALAXY_APP`) that the platform deletes as soon as it has created the dedicated server for it — so the old application does not linger in the galaxy. Owner-scoped: the same key, the same Bitrix24 account, `kind=GALAXY_APP`. An identifier that is not yours, or that does not belong to a Galaxy application, returns `404` and deletes nothing. Meaningful only together with `placement: dedicated`. For a dedicated server, the `Idempotency-Key` header is not compatible with this parameter — see [Idempotency](#idempotency) |
| `runMode` | object | no | The run mode the server is created in. Same shape as [`PATCH /:id/run-mode`](/docs/infra/lifecycle/run-mode): `{"mode": "ALWAYS"}`, `{"mode": "IDLE", "idleMinutes": 15\|30\|60\|240}` or `{"mode": "SCHEDULE", "scheduleId": "…"}`. The block is discriminated on `mode`, so a field that does not belong to the chosen mode is rejected rather than silently dropped. The refusal comes BEFORE the machine is created, so a failed request leaves no server behind. Without the block the server gets the schedule that an administrator of the Bitrix24 account has marked for new machines — [Set the default schedule](/docs/infra/work-schedules/set-default). Without such a mark the default auto-sleep timeout from "Known specifics" applies |
| `source` | object | no | Code for one-step Galaxy application creation. `source.content` is a base64 archive. Request body limit: 96 MB, approximately 72 MB before base64 encoding. Exceeding the limit returns `413 INLINE_SOURCE_TOO_LARGE`. For larger archives use [scenario 2](#scenario-2-two-step). Requires `runtime` and, except for `static`, `start`. A fresh create outside Galaxy mode or with `placement: "dedicated"` returns `400 SOURCE_AT_CREATE_GALAXY_ONLY`. |
| `runtime` | string | no | Runtime ID from [`GET /v1/infra/runtimes`](/docs/infra/deploy/runtimes). Required with `source`. Without `source`, returns `400 RUNTIME_PARAM_REMOVED`. |
| `start` | string | no | Single-line start command, up to 5000 characters. Required with `source`, except for `static`, whose fixed nginx entrypoint ignores this command. A line break returns `400`. |
| `install` | string | no | Single-line dependency installation command, up to 5000 characters. |
| `env` | object | no | Environment variables `{ "KEY": "value" }`, passed to the container at start. See [Diagnostics and environment variables](#diagnostics-and-environment-variables). |
| `port` | integer | no | Application listening port, from 1 to 65535. |
| `healthPath` | string | no | The path the platform uses to check the application's readiness inside its container. Up to 500 characters, must start with `/`. Defaults to `/`. Ignored when creating a dedicated virtual machine |

> **Note:** for the dedicated-virtual-machine model the `runtime` parameter is not accepted in `POST /v1/infra/servers` — passing it will return `400 RUNTIME_PARAM_REMOVED`. The runtime is installed at the deploy stage. See [POST /:id/deploy](/docs/infra/deploy/deploy). **Exception — Galaxy mode:** when `source` is passed, `runtime` is required (see the ["Galaxy application"](#galaxy-application) section), and `400 RUNTIME_PARAM_REMOVED` is not returned in that case.

## Examples

### curl — personal key

```bash
# name — lowercase Latin letters, digits and hyphens only, first character a letter (^[a-z][a-z0-9-]*$)
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "displayName": "My CRM bot",
    "plan": "bc-small",
    "region": "bc-eu-central",
    "image": "ubuntu-2404-lts"
  }'
```

### curl — OAuth application

```bash
# name — lowercase Latin letters, digits and hyphens only, first character a letter (^[a-z][a-z0-9-]*$)
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "displayName": "My CRM bot",
    "plan": "bc-small",
    "region": "bc-eu-central",
    "image": "ubuntu-2404-lts"
  }'
```

### JavaScript — personal key

```javascript
// name — lowercase Latin letters, digits and hyphens only, first character a letter (^[a-z][a-z0-9-]*$)
const res = await fetch('https://vibecode.bitrix24.com/v1/infra/servers', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    provider: 'bitrix-cloud',
    name: 'my-crm-app',
    displayName: 'My CRM bot',
    plan: 'bc-small',
    region: 'bc-eu-central',
    image: 'ubuntu-2404-lts',
  }),
})
const { data } = await res.json()
console.log('Server ID:', data.id, 'Subdomain:', data.subdomain)
```

### JavaScript — OAuth application

```javascript
// name — lowercase Latin letters, digits and hyphens only, first character a letter (^[a-z][a-z0-9-]*$)
const res = await fetch('https://vibecode.bitrix24.com/v1/infra/servers', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    provider: 'bitrix-cloud',
    name: 'my-crm-app',
    displayName: 'My CRM bot',
    plan: 'bc-small',
    region: 'bc-eu-central',
    image: 'ubuntu-2404-lts',
  }),
})
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | Always `true` on success |
| `data.id` | string (UUID) | Unique server identifier |
| `data.status` | string | For a new server — `"provisioning"`, wait for `"running"`, which takes 1–3 minutes. On a retry with the same `Idempotency-Key`, this is the server's status at the time of the retry. **On a reuse response (`reused: true`) this is the existing server's status and can be anything**, including `"running"` immediately — in which case waiting for `"running"` verifies nothing |
| `data.provider` | string | Echo of the passed `provider`. **Exception — a Galaxy application** (`createdVia: "galaxy"`): the host's provider is returned, not the one requested |
| `data.name` | string | The server's system name. Echo of the passed `name` when a new server is created. **On a reuse response it is the existing server's name**, and the one you asked for is in `data.requestedName` |
| `data.kind` | string | Server type: always `STANDALONE` or `GALAXY_APP` |
| `data.galaxyId` | string | Only for the galaxy branch (the galaxy host ID); absent from a standalone response |
| `data.displayName` | string \| null | Human-readable name. If `displayName` was not passed at creation, it equals `name` |
| `data.description` | string \| null | Echo of the passed `description`. The key is present only in the response for a dedicated virtual machine. **For a Galaxy application**, where `createdVia` equals `galaxy`, the key is absent from the response even though the description is stored — read it via [`GET /v1/infra/servers/:id`](./get.md) |
| `data.ip` | string \| null | Public IP. `null` in the first create response; on a retry with the same `Idempotency-Key` it is the server row's current value. The address appears once the machine starts, read it from [`GET /v1/infra/servers/:id`](./get.md) |
| `data.ssh.user` | string | SSH user: `root` for new servers |
| `data.ssh.port` | number | SSH port: `22` |
| `data.ssh.password` | string \| null | Always `null`: the create response does not contain the password. A person turns on OPEN mode in the Vibecode dashboard, then [`GET /v1/infra/servers/:id/ssh`](/docs/infra/access/ssh) returns the root password. With an API key alone, use the [Deploy API](/docs/infra/deploy). On a reuse response and for a Galaxy application the whole `ssh` block is `null` |
| `data.ssh.privateKey` | string \| null | Always `null`: the create response does not contain the private key, even when you passed your own `sshPublicKey`. A person turns on OPEN mode in the Vibecode dashboard, then [`GET /v1/infra/servers/:id/ssh`](/docs/infra/access/ssh) returns the platform's private key. With an API key alone, use the [Deploy API](/docs/infra/deploy). Also `null` on a retry with the same `Idempotency-Key` — see [Idempotency](#idempotency). On a reuse response and for a Galaxy application the whole `ssh` block is `null` |
| `data.plan` | string | Echo of the passed `plan`. **Exception — a Galaxy application** (`createdVia: "galaxy"`): the host's plan is returned, not the one requested |
| `data.region` | string | The region the server actually landed in. May differ from the requested one when a zone fallback triggers (see "Known specifics"). **For a Galaxy application** — the host's region |
| `data.image` | string | Resolved public image identifier. It may be the provider default when `image` was omitted, or a stable family identifier normalized from the provider's concrete image |
| `data.mode` | string | Always `"BLACKHOLE"` right after creation |
| `data.createdVia` | string | `"api"` for calls via the v1 API, `"ui"` for calls from the Vibecode dashboard. **On a reuse response it is the existing server's value**, so a v1 create response may carry `"ui"` |
| `data.subdomain` | string | Subdomain for the application, for example `app-92fb1c34`. Used in `appUrl` |
| `data.blackholeStatus` | string | State of the agent tunnel. `"NONE"` in the first create response; on a retry with the same `Idempotency-Key` it is the server row's current value. `"CONNECTED"` appears in [`GET /v1/infra/servers/:id`](./get.md) once the agent has connected |
| `data.accessPolicy` | string | Access policy for the application. By default `"OWNER_ONLY"` — only the key owner |
| `data.sleepAfterMinutes` | number \| null | Saved auto-sleep timeout in minutes. The field is present in every successful `201` response — for a new dedicated machine and Galaxy application, and on reuse and idempotency replay. `null` is the saved "Never" choice. Effective sleep depends on the plan and schedule, as described below |
| `data.runtimeId` | string \| null | Always `null` at creation. Filled in on deploy with `runtime` |
| `data.runtimeStatus` | string \| null | A deprecated field, kept for compatibility. For servers created after 2026-04-25 it always returns `null`. Runtime readiness after a deploy is determined by the success of the `runtime` step in the [`POST /:id/deploy`](/docs/infra/deploy/deploy) response, not this field |
| `data.appUrl` | string \| null | HTTPS address of the application: `https://{subdomain}.vibecode.bitrix24.com` |
| `data.createdAt` | string (ISO 8601) | Creation timestamp |
| `warnings` | array\<string\> | Next to `data`, not inside it. Returned only when there is something to report: for example, `displayName` or `description` lost their non-ASCII characters on the way in (Windows PowerShell without explicit UTF-8 serialization is the usual case). The server is still created, and both values reach the Bitrix24 catalog card as is |

`data.sleepAfterMinutes` is a response field, not a create parameter. To change the saved policy after creating a dedicated machine, call [`PATCH /v1/infra/servers/:id/sleep`](/docs/infra/lifecycle/sleep). Do not substitute the expected default for this field: on reuse or idempotency replay the response carries the existing record's value, including `null` or a historical numeric value. It is not a computed effective timeout. Without an effective [wake schedule](/docs/infra/wake-schedules), `null` disables idle auto-sleep. On a preemptible plan with an effective schedule, the platform's post-window timeout applies — 15 minutes by default. On a non-preemptible plan, writing `null` is rejected with `ALWAYS_ON_CONFLICT` while enabled windows exist, unless the server is in the `SCHEDULE` run mode. Creating or updating any window while `null` is stored on a non-preemptible plan is also rejected, even with `enabled: false`. The window-write exceptions are an app in a galaxy and a server in the `SCHEDULE` run mode. For an app in a galaxy, `null` with an effective schedule also activates the platform's post-window timeout. A dedicated machine on a non-preemptible plan with `null` outside the `SCHEDULE` run mode does not idle-sleep, even with previously stored windows. An assigned work schedule determines sleep in the `SCHEDULE` run mode. Writing the timeout through `PATCH /sleep` removes that work schedule.

## Response example

A dedicated virtual machine created from scratch:

```json
{
  "success": true,
  "data": {
    "id": "db008c84-91a5-4e15-b9d5-6c6aa2838448",
    "status": "provisioning",
    "provider": "bitrix-cloud",
    "name": "docs-test-temp",
    "kind": "STANDALONE",
    "ip": null,
    "ssh": {
      "user": "root",
      "port": 22,
      "password": null,
      "privateKey": null
    },
    "plan": "bc-small",
    "region": "bc-eu-central",
    "image": "ubuntu-2404-lts",
    "mode": "BLACKHOLE",
    "createdVia": "api",
    "subdomain": "app-92fb1c34",
    "blackholeStatus": "NONE",
    "accessPolicy": "OWNER_ONLY",
    "sleepAfterMinutes": 60,
    "runtimeId": null,
    "runtimeStatus": null,
    "appUrl": "https://app-92fb1c34.vibecode.bitrix24.com",
    "createdAt": "2026-04-22T10:50:11.477Z"
  }
}
```

A Galaxy application via scenario 1 — `source` was passed, the build runs in the background. `ssh` is `null`. There is no `next` field:

```json
{
  "success": true,
  "data": {
    "id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
    "status": "provisioning",
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "kind": "GALAXY_APP",
    "galaxyId": "<galaxy-host-id>",
    "displayName": "My CRM bot",
    "ip": null,
    "ssh": null,
    "plan": "bc-medium",
    "region": "bc-eu-central",
    "image": "ubuntu-2404-lts",
    "mode": "BLACKHOLE",
    "createdVia": "galaxy",
    "subdomain": "app-7c2b1f08",
    "blackholeStatus": "NONE",
    "accessPolicy": "OWNER_ONLY",
    "sleepAfterMinutes": 60,
    "runtimeId": null,
    "runtimeStatus": null,
    "appUrl": "https://app-7c2b1f08.vibecode.bitrix24.com",
    "createdAt": "2026-04-22T10:50:11.477Z"
  }
}
```

A Galaxy application via scenario 2 — `source` was not passed, `next` and `hint` appear:

```json
{
  "success": true,
  "data": {
    "id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
    "status": "provisioning",
    "provider": "bitrix-cloud",
    "name": "my-crm-app",
    "kind": "GALAXY_APP",
    "galaxyId": "<galaxy-host-id>",
    "displayName": "My CRM bot",
    "ip": null,
    "ssh": null,
    "plan": "bc-medium",
    "region": "bc-eu-central",
    "image": "ubuntu-2404-lts",
    "mode": "BLACKHOLE",
    "createdVia": "galaxy",
    "subdomain": "app-7c2b1f08",
    "blackholeStatus": "NONE",
    "accessPolicy": "OWNER_ONLY",
    "sleepAfterMinutes": 60,
    "runtimeId": null,
    "runtimeStatus": null,
    "appUrl": "https://app-7c2b1f08.vibecode.bitrix24.com",
    "createdAt": "2026-04-22T10:50:11.477Z",
    "next": "deploy",
    "hint": "This is a galaxy app — POST /v1/infra/servers/:id/deploy with source.content now; it will not reach \"running\" on its own."
  }
}
```

If a Galaxy application's build failed, [`GET /v1/infra/servers/:id`](./get.md) returns the reason in `data.provisionError`:

```json
{
  "success": true,
  "data": {
    "id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
    "status": "error",
    "createdVia": "galaxy",
    "provisionError": "Docker build failed: npm ci exited with code 1"
  }
}
```

## Error response example

400 — validation failed (the name starts with an uppercase letter):

```json
{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "name: Name must start with a letter and contain only lowercase letters, digits, and hyphens"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `INVALID_REQUEST` | Field validation failed (an invalid name format, a missing required field, an invalid SSH key, etc.). The `message` field contains the specific reason. On a portal that places applications in a galaxy, when the body carries neither `source` nor the required `provider` + `plan` pair, an `error.hint` object is added (see below) |
| 400 | `SOURCE_AT_CREATE_GALAXY_ONLY` | The request carrying `source` could not be placed in a galaxy. This happens when Galaxy mode is disabled, when the request also carries `placement: "dedicated"`, or when the actual check finds no suitable host. Follow `error.hint` and `capabilities.servers.create`: with no galaxy host the two-step path is available only when `available` is `true`; with an existing host and `available: false`, the hint rules that path out because with trial enforcement active a dedicated-machine create returns `402 TRIAL_PORTAL_LIMIT` |
| 400 | `GALAXY_SOURCE_URL_NOT_ALLOWED` | The link in `source.url` did not pass the check that runs before it is sent to the galaxy host. Reasons: the address does not point at the platform [source storage](/docs/source-storage), the value is not an absolute URL, the scheme is neither `http` nor `https`, or the address does not point at the public network — a local, internal or service host. The response text names the rule that fired, and for an outside address it names the replacement — save the archive as a version and deploy it as a second step through [code deploy](/docs/infra/deploy/deploy). The same check runs on that deploy too |
| 400 | `RUNTIME_PARAM_REMOVED` | The `runtime` parameter was passed without `source`. Specify the runtime in [`POST /:id/deploy`](/docs/infra/deploy/deploy), or pass `runtime`, `start` and `source` together in this same request. On a portal that places applications in a galaxy, an `error.hint` object is added (see below) |
| 400 | `UNKNOWN_PARAM` | The request body has an unknown field (e.g. `deployMode` instead of `placement`). `details.unknownFields` lists the extra fields, `details.suggestions` proposes the correct name, and `details.validParams` is the full list of accepted fields |
| 400 | `WORK_SCHEDULE_NOT_FOUND` | The `runMode` block named a schedule ID that does not exist in your account. No machine is created |
| 400 | `WORK_SCHEDULE_EMPTY` | The named schedule has no windows — a server on it would never run. No machine is created |
| 400 | `RUN_MODE_UNAVAILABLE` | Run modes are not enabled for your account yet. No machine is created |
| 400 | `INVALID_PLAN` | The `plan` value is not in the provider catalog. List — [`GET /v1/infra/providers/:providerId/plans`](../providers/plans.md) |
| 400 | `INVALID_REGION` | The `region` you passed is not in the provider catalog. List — [`GET /v1/infra/providers/:providerId/regions`](../providers/regions.md). Second case: `region` was omitted and the provider exposes no regions at all — `message` then names the provider, and the platform has no default to substitute |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |
| 401 | `INVALID_API_KEY` | The key is not recognized — no such key exists on the platform |
| 401 | `UNAUTHENTICATED` | An authorization key (`vibe_app_`) was sent without the `Authorization: Bearer` header — server creation runs a plan check that has to know who is creating the server. The `error.hint` field names the next step: complete the OAuth flow, or use a personal API key (`vibe_api_`), which needs no session. See [Keys and authorization](/docs/keys-auth) |
| 402 | `INT_TARIFF_REQUIRED` | The portal is on a free Bitrix24 plan — creating servers requires a commercial plan (a trial plan grants limited access). [Plan and access](/docs/infra#plan-and-access) explains which access path applies to the portal |
| 402 | `INT_VIBE_PLUS_REQUIRED` | Account access is narrowed to a paid Vibe+ plan, and an ordinary commercial Bitrix24 plan is no longer enough. An account on a trial plan keeps its trial access and does not receive this code |
| 402 | `COMMERCIAL_PLAN_REQUIRED` | The platform could not find the record of the Bitrix24 account the key is bound to, so it cannot check access |
| 402 | `TRIAL_PORTAL_LIMIT` | The portal's server quota for the trial period was exceeded (`error.retryable: false`); do not repeat creation with different names, keys, providers or plans. Check `deployment.standalone.reuseTarget` in [GET /v1/me](/docs/keys-auth/me); otherwise follow `userMessage` and `alternatives`. |
| 402 | `PLAN_NOT_ALLOWED_ON_TRIAL` | The requested plan is not available during the trial period. Read the allowed plans from `error.details.allowedPlans`, which lists the plans the trial gate is configured for — the set varies, so do not hardcode it. `error.details.requestedPlan`, when present, echoes the rejected plan |
| 402 | `GALAXY_NOT_ON_TRIAL` | A Galaxy host cannot be created on trial access. Obtain full access under the regional model; for trial deployment use a standalone server with a plan listed in `capabilities.servers.create.limits.allowedPlans` from `GET /v1/me`, within the remaining quota. This refusal does not forbid a `GALAXY_APP` container on an existing eligible host |
| 402 | `ACCOUNT_FROZEN` | The Vibecode balance is frozen — a top-up is required |
| 403 | `PORTAL_TARIFF_UNREADABLE` | The account plan could not be read, so the response does not name the plan that is missing: `error.details.requiredTariffs` is empty and the button points to support. Buying a plan does not clear this refusal |
| 403 | `INFRA_NOT_PERMITTED` | Infrastructure is disabled on the platform or on the portal |
| 403 | `SERVER_CREATION_DISABLED` | Server creation is forbidden by portal policy |
| 403 | `MAX_SERVERS_REACHED` | The per-API-key server limit was exceeded. Delete unneeded ones via [`DELETE`](./delete.md) or create a new API key |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key is in read-only mode, but creating a server is a write operation. Switch the key to read and write — see [Access mode](/docs/keys-auth/access-mode) |
| 403 | `INFRA_SCOPE_REQUIRED` | The key lacks the `vibe:infra` scope — infrastructure management is unavailable. Add the scope or use a key with infrastructure permissions |
| 403 | `INFRA_FORBIDDEN_FOR_COWORK_KEY` | The call was made with a Cowork key — such a key works with data only and cannot perform write operations. To issue a key that can, see [Project key for deploy](/docs/cowork/deploy-key) |
| 404 | `NO_CREDENTIALS` | The `provider` is not configured on the platform |
| 409 | `GALAXY_FULL` | The portal hosts applications in galaxies and there is no room for another one. Before refusing, the platform tries to place the application on another galaxy, so this response means there really is no room left. Delete an unneeded Galaxy application, or ask for a dedicated machine — the `placement` field with the value `dedicated` |
| 409 | `REISSUE_IN_PROGRESS` | The application key or server binding is busy (key rotation, secret delivery or deletion), or changed during this call. No new machine was created; retry shortly |
| 409 | `APPLICATION_KEY_RECOVERY_REQUIRED` | The replacement of the API key that made the request is unfinished: its rotation ended with a `500` response or has not finished yet. No server was created: it would have landed on a key with an unfinished replacement. The steps are in [Rotate an API key](/docs/management-keys/rotate) |
| 409 | `APPLICATION_REUSE_CONFLICT` | While the platform was picking an existing server to reuse, a parallel request changed the server's binding to the application. `error.retryable: true`; retry the request |
| 413 | `INLINE_SOURCE_TOO_LARGE` | The body carrying inline `source.content` is over 96 MB, which is about 72 MB of the archive itself. The decision is based on the `Content-Length` header before the body is read, so the refusal is deterministic — re-sending the same body fails identically. This saves no traffic: the platform accepts the whole body first, and the refusal arrives once the upload has finished. `error.hint` carries four strings — `reason`, `recovery`, `recoveryAction` and `note`: the recipe here is to create the server without `source`, save the archive as a version on the identifier that comes back, and deploy it with `{"source": {"versionId": "vN"}}`, because every URL on the versioned path is built from the server identifier, which does not exist before creation |
| 429 | `RATE_LIMITED` | The platform's overall request limit was exceeded |
| 429 | `DEPLOY_BACKEND_BUSY` | The request carried an archive in `source.content` while the backend was already running the maximum number of such heavy requests (one shared counter with [`POST /:id/deploy`](/docs/infra/deploy/deploy) and [`POST /:id/upload`](/docs/infra/deploy/upload)). The response carries a `Retry-After: 30` header — retry in half a minute. To avoid the queue entirely, create the server without `source` and upload the code in a separate request with a link, `{source: {url: ...}}` — that path has no concurrency cap |
| 502 | `PROVIDER_ERROR` | The request to the cloud provider failed before the server record was created, for example the zone catalog could not be read. There is no server record, the quota is not consumed, and you can retry the request. A refusal to create the machine itself arrives later: the server gets `status: "error"`, and the reason is in `provisionError` and `provisionErrorCode` of the [`GET /v1/infra/servers/:id`](./get.md) response. The `message` field carries a localized description of the failure plus an incident code — the provider's raw response never reaches the body; it stays in the platform logs. Quote the incident code when contacting support |
| 503 | `POOL_EXHAUSTED` | The create outcome is unknown: the database failure may have occurred after the operation started. Wait out `Retry-After`, check the server list, and retry creation only once you have confirmed that no server appeared |

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

### The `error.hint` object

On a Bitrix24 account that places applications in a galaxy, the `INVALID_REQUEST` and `RUNTIME_PARAM_REMOVED` errors are extended with an `error.hint` object. It names the reason for the refusal and gives a ready-made request body. The condition differs between the two codes. For `INVALID_REQUEST` the hint arrives when the body carries neither `source` nor the required `provider` + `plan` pair. The `region` field is optional and defaults to the chosen provider's region. For `RUNTIME_PARAM_REMOVED` the hint arrives when `runtime` is passed without `source`, even if `provider` and `plan` are filled in. On a Bitrix24 account with dedicated virtual machines there is no hint in either case.

During trial access, these early hints use the same advisory assessment as `GET /v1/me`. A host that is suitable according to stored state produces a recommendation to try the one-shot request. With no host, the two-step path is available only while `capabilities.servers.create.available` is `true`. When an existing host is unsuitable, wait for recovery, repair the host, or upgrade the plan before retrying one-shot creation; after removing the host, re-read `GET /v1/me` and use the two-step path advertised there. The hint is advisory: the `POST` makes the final decision.

When a request carrying `source` has already reached the actual placement check and returns `SOURCE_AT_CREATE_GALAXY_ONLY`, its `error.hint` reflects that result. With an existing host during trial access, the hint rules out the two-step path while `capabilities.servers.create.available` is `false`: with trial enforcement active, that path returns `402 TRIAL_PORTAL_LIMIT`. With no host, it permits creating a dedicated machine without `source` and then deploying, but only after that capability check succeeds.

Do not confuse it with `data.hint` from a successful response: that one is a string about the next step after creating an application via scenario 2; this one is an object inside `error`.

| Field | Type | Description |
|-------|------|-------------|
| `error.hint.reason` | string | Why the request was refused |
| `error.hint.recovery` | string | What to change in the request so it succeeds |
| `error.hint.example` | object | A ready-made request body to start from |

```json
{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "provider: Required; plan: Required",
    "hint": {
      "reason": "This portal places new apps on shared galaxy hosts, and the request lacked `source` and the required provider/plan pair. `region` is optional and defaults to the provider's region.",
      "recovery": "RECOMMENDED: create-and-deploy in ONE call — POST /v1/infra/servers { name, source: { content }, runtime, start }; OMIT provider/plan/region. Two-step also works: pass provider and plan (region is optional and defaults to the provider's region) to create an empty slot, then POST /v1/infra/servers/:id/deploy with the source. See GET /v1/me -> deployment.galaxyApp.checklist. For a deliberate dedicated standalone VM pass placement: \"dedicated\" with provider and plan; region is optional.",
      "example": {
        "name": "<slug>",
        "source": { "content": "<base64 gzip-tar of the app>" },
        "runtime": "node24",
        "start": "node server.js",
        "port": 3000
      }
    }
  }
}
```

If the body carries `placement: "dedicated"`, the hint is different: instead of moving the application into a galaxy container, it suggests keeping the dedicated server and adding the required `provider` and `plan`, with `region` omitted to get the provider's default region.

## Reusing a server

`POST /v1/infra/servers` does **not** create a second server and returns an existing one in two cases. First, the calling key belongs to a card in **Applications** that already has a live server bound to it. Second, the same API key is already creating a `GALAXY_APP` in `provisioning`, that server has a live keyless card, the Applications section is visible to the Bitrix24 account, and the parent Galaxy host is still live: it has not been deleted, is still a `GALAXY`, and is not in `error` or `deleted`. The second case applies to every placement except `placement: "dedicated"`, even if placement settings switched to standalone machines after the first request, and it is checked before the fresh-only refusal for a body carrying `source`. Once that application leaves `provisioning`, the next create may create a new server again.

The `name` you sent plays no part in that decision: reuse is determined from the calling key and server state, not the name.

**On a reuse response (`reused: true`) every `data` field describes the existing server, not your request** — `status`, `provider`, `plan`, `region`, `image`, `createdVia`, `createdAt` all belong to it. The status can be anything, so waiting for `"running"` is **not** a check that the server is yours. The check is `reused` plus the `name` ⊕ `requestedName` pair.

Such a response always carries `data.reused: true` and, next to `data`, a `warnings` array with at least one entry. Compare `data.name` with `data.requestedName` — they differ whenever the application's server was created earlier under a different name.

| Field | Type | When |
|-------|------|------|
| `data.reused` | boolean | Always on such a response. No new server was created |
| `data.reusedReason` | string | `APPLICATION_ALREADY_HAS_SERVER` |
| `data.requestedName` | string | Echo of your `name` — always, even when it equals the existing name |
| `data.sourceIgnored` | boolean | The `source` you sent was **not** deployed by this request |
| `data.deploying` | boolean | The `source` you sent is being built into the application's container right now |
| `data.metaIgnored` | boolean | The `displayName`/`description` you sent were **not** applied |
| `warnings` | array | Next to `data`, not inside it. At least one entry |

**What to do before deploying.** Call [`GET /v1/infra/servers/:id`](./get.md) and inspect what is already deployed there. A deploy **replaces** the code currently running — it is not additive. If this is not the server you intended, stop and ask the user which application this key should be working on.

**About the `source` you sent.** Ordinary reuse deploys the source to a Galaxy application only when it has no container yet or its previous build failed — the response then carries `deploying: true`, and you poll `GET /v1/infra/servers/:id` until `running`. When the request converges on a keyless application in `provisioning`, it never starts a second build and returns `sourceIgnored: true`. If the earlier build is already active, `data.next` is absent: do not call deploy in parallel. Wait until the server leaves `provisioning`, then deploy the ignored source through [`POST /v1/infra/servers/:id/deploy`](/docs/infra/deploy/deploy) only if replacement is still needed. A parked application with no active build keeps `next: "deploy"`.

**About the `displayName` and `description` you sent.** A reuse never renames the server it hands back: it keeps its own name and description, and the values you sent are discarded — the response shows this as `data.metaIgnored: true` plus a dedicated warning. Rename it deliberately with [`PATCH /v1/infra/servers/:id`](./update.md) once you have confirmed the server is the right one.

**About `data.next`.** On a reuse response the field arrives **only** for a parked Galaxy application that is attached to a galaxy host and has no container and no active build. During an already active build, `next` is deliberately absent: poll the server and do not start a competing deploy. It is also absent when the application is detached from its host or the server can already run code. Do not read the missing field as an error or permission to overwrite code.

**How to get a different server for the application.** There is no separate call that changes the binding. When you need a dedicated virtual machine and want the binding left alone, create it with `placement: "dedicated"` — reuse does not fire, but the new server is not bound to the application either. When you need to change the bound server itself, delete the current one via [`DELETE /v1/infra/servers/:id`](./delete.md): the next `POST /v1/infra/servers` with the same key then clears the stale binding and binds the newly created server. Deletion is irreversible and destroys everything deployed on the server — confirm via [`GET /v1/infra/servers/:id`](./get.md) that it is not someone else's work first.

A create with `placement: "dedicated"` is not affected by reuse.

## Idempotency

Pass the optional `Idempotency-Key` header to safely retry a registered standalone-server or Galaxy-application create. If the response to the first request is lost (a network drop, a load-balancer timeout), a retry with the same key does not create a second resource — it returns the same server with HTTP `201` and an `Idempotent-Replayed: true` response header, and the fields describe its state as of the retry.

**The retry works for 15 minutes** from key reservation (from server creation for earlier standalone records). Past that window the same key no longer returns the server: the request runs as a new create with every policy check — and gets either an ordinary policy refusal (`402` / `403`) or `409 IDEMPOTENCY_KEY_ALREADY_USED`. **Unless you deleted it, the server is still there** — the response text says so.

**Important: retry with THE SAME key, not a fresh one.** For a standalone create the same `Idempotency-Key` cannot produce a second machine: it returns either the server itself or a `409` that tells you its fate. A fresh `Idempotency-Key` is not itself a replay of the earlier request, but ordinary reuse rules still apply. In particular, a request with the same API key returns a live keyless `GALAXY_APP` in `provisioning` with `reused: true` for every non-dedicated placement. A fresh key therefore does not mean that a new server will necessarily be created: inspect `data.id`, `data.reused`, and [`GET /v1/infra/servers`](./list.md).

- The key is a 1–255 character string from `[A-Za-z0-9_.:-]`. It is scoped to your API key.
- **A standalone replay does not return SSH credentials either.** As in the first response, `ssh.privateKey` and `ssh.password` are `null`, and the `note` field explains how to get access: a person turns on OPEN mode in the Vibecode dashboard, then [`GET /v1/infra/servers/:id/ssh`](/docs/infra/access/ssh) returns the credentials; with an API key alone, use the [Deploy API](/docs/infra/deploy). A `GALAXY_APP` has no SSH credentials of its own: `ssh` is `null` and no note is required.
- **Galaxy create and reuse are protected once the new protection is enabled.** The key is reserved independently of the resource row: different keys can return one reused slot. New fresh, revived, and reused results record the exact `applicationId`; if the fresh/revived server's background attachment finds the rightful card already created for this key, the record atomically switches from its preallocated id to that card's id and is fixed thereafter. A retry within 15 minutes accepts only that exact card, not any later same-owner card attached to the server. The platform also rechecks the owner and Bitrix24 account of both rows and the live Galaxy host. If the recorded card was deleted, detached, or replaced, ownership changed, or the host was deleted or entered `error`/`deleted`, the key remains spent but the response is `409 IDEMPOTENCY_KEY_ALREADY_USED` without `data` or the previous id; the request never falls through to a fresh create. UUID-only records from before activation remain legacy. Past the window ordinary checks and the spent-key refusal apply. Until activation the first galaxy create still does not reserve a key; recorded keys are read even after protection is switched off. Earlier galaxy creates without a recorded key cannot be recovered.
- The header is not supported together with `graduateFrom` for a dedicated server — it returns `400 IDEMPOTENCY_UNSUPPORTED_WITH_GRADUATION`.
- **A retry of a recorded key does not restart the build.** After a build failure, run an explicit deploy. A fresh/revived Galaxy result replays through the ordinary create response; semantic reuse again carries `reused`, `reusedReason`, `requestedName`, `sourceIgnored`/`metaIgnored`, `warnings`, `hint`, and an applicable `next`. These fields use the replay's current validated body, but a new reuse record separately remembers whether the original request accepted its `source`: while the build it started remains active, the replay returns `deploying: true` without `sourceIgnored` or `next`; after it finishes, the source is still not called ignored and is not deployed again automatically. A legacy v1 record has no such provenance, so its replay does not invent an outcome: it returns neither `sourceIgnored` nor `next`, and tells the caller to inspect the server and logs before any deploy. A request with a fresh key follows ordinary reuse rules: an empty or failed slot may start a build, while convergence on a keyless application with an active build never starts another one and may return `sourceIgnored: true`.

| Status | `code` | When |
|--------|--------|------|
| 400 | `INVALID_IDEMPOTENCY_KEY` | The key fails validation (length or disallowed characters) |
| 400 | `IDEMPOTENCY_UNSUPPORTED_WITH_GRADUATION` | The key together with `graduateFrom` for a dedicated server |
| 409 | `IDEMPOTENCY_KEY_ALREADY_USED` | The key is spent. The response text distinguishes a deleted server, a retry **past the 15-minute window**, and an unavailable previous result — including deletion, detachment, or replacement of the exact recorded Galaxy card. In the last case the response carries no `data` or previous identifier and never starts a fresh create. The same code is returned on the galaxy path when the key is already spent |
| 409 | `IDEMPOTENCY_CONCURRENT_RETRY` | A concurrent request with the same key is still in progress — retry shortly |

## Known specifics

- **Examples of valid and invalid names** for the pattern `^[a-z][a-z0-9-]*$`:
  - Valid: `my-app`, `bot-1`, `crm-dashboard`.
  - Invalid: `My-App` (uppercase), `bot_1` (underscore), `my.app` (dot), `café` (non-ASCII letters).
- **Your own `sshPublicKey` is installed while the machine is being prepared.** The create response arrives before the key is installed. You can log in over SSH with your own key only once a person has switched the server to OPEN mode in the Vibecode dashboard.
- **A cloud provider refusal arrives after the response.** Create answers `201` with the `provisioning` status, and if the cloud refuses to create the machine, the server gets `status: "error"` and the reason is in `provisionError` and `provisionErrorCode`. A hold set on the account before the machine is ordered gives `status: "error"` with the reason in `provisionError`. A hold set after the machine was ordered leaves the server as `status: "sleeping"` (machine stopped) with an empty `provisionError`. An `error` record counts toward the per-user server limit (`MAX_SERVERS_REACHED`) until you delete it. Read `provisionErrorCode` before deleting or recreating the server; for `GENERIC`, contact support first. With `graduateFrom`, the custom domain and the application addresses move to the new server right away and are released when you delete the failed record.
- **When `region` in the response differs from the requested one.** If the requested zone has run out of IP addresses (`Address space exhausted`), the platform automatically tries the next zones in the order from [`GET /v1/infra/providers/:providerId/regions`](../providers/regions.md), and records this in the audit log with the `SERVER_ZONE_FALLBACK` event. In Bitrix24 Cloud, a capacity fallback may select another availability zone inside the same public region; in that case `data.region` does not change.
- **Provisioning timeout is 15 minutes.** If `status` stays in `provisioning` longer, the platform moves the server to `error` and fills in `provisionError` and `provisionErrorCode`. Read `provisionErrorCode`: for `GENERIC`, contact support first; after a confirmed creation failure, delete the record through [`DELETE`](./delete.md) before creating another server.
- **A new regular machine gets a 60-minute auto-sleep timeout.** The actual saved value is returned immediately in `data.sleepAfterMinutes` by the create response and later by [`GET /v1/infra/servers/:id`](./get.md). On reuse and idempotency replay this is the existing record's value, so it can be `null` or differ from 60. A server created with the schedule marked for new machines keeps this value, but it does not apply while the schedule is assigned — the mode is in the `runMode` and `workSchedule` fields of the same `GET`. Inbound HTTP requests and SSH activity reset the timer; cron, background processes, and the application's own outbound requests do not. For the allowed values and deliberate disabling of auto-sleep through `PATCH` with `null`, see [Configure auto-sleep](/docs/infra/lifecycle/sleep).
- **The runtime is installed at the deploy stage.** The `runtime` parameter without `source` is not accepted in `POST /v1/infra/servers` (it will return `400 RUNTIME_PARAM_REMOVED`). Specify `runtime` in the body of [`POST /:id/deploy`](/docs/infra/deploy/deploy) — for a dedicated virtual machine, the pipeline begins in the order `runtime → stop_existing → clean → download → install`. Together with `source` the runtime is accepted and required: that is the one-shot Galaxy application create, where `runtime` and `start` go in the same request.
- **Non-ASCII characters in `displayName` and `description` from Windows PowerShell.** Sent without explicit UTF-8 serialization, they are stored as question marks (`?`): the bytes are lost on the client side, before the request is sent. A ready-to-run call with `UTF8.GetBytes` — [Windows / PowerShell and UTF-8](/docs/infra#windows-powershell-and-utf-8).

## See also

- [Providers and catalogs](/docs/infra/providers)
- [Get a server](./get.md)
- [List servers](./list.md)
- [Update name and description](./update.md)
- [Delete a server](./delete.md)
- [Configure auto-sleep](/docs/infra/lifecycle/sleep)
- [Deploy API](/docs/infra/deploy)
- [Full application deploy](/docs/infra/deploy/deploy)
- [List of runtimes](/docs/infra/deploy/runtimes)
- [Plan and access](/docs/infra#plan-and-access)
- [Access mode](/docs/keys-auth/access-mode)
