
## Get a server

`GET /v1/infra/servers/:id`

Returns details of a single server by ID. For a full owner card, the endpoint automatically polls the cloud provider when the stored status is `provisioning` or the IP is absent, provided the server has an external ID. A deleted server (`status: "deleted"`) is not accessible: a 404 is returned.

The card comes in three shapes. The managing key gets the full object. An application key gets the same shape, without the `access` block, for the server bound to it — except for a `GALAXY` host — even when the server record is assigned to another application key. A member of the server's development team gets `200` with a shortened card. If the `galaxyId` of a live Galaxy application managed by the current key points to a live shared host in the same Bitrix24 account, that host arrives as a safe read-only projection with `access.via: "galaxy-reference"`. The shapes and fields are broken down in [List servers](./list.md).

## Parameters

| Parameter | In | Type | Required | Description |
|----------|---|-----|:-----:|----------|
| `id` | path | string (UUID) | yes | Server ID or a Galaxy application's `galaxyId` from [`POST /v1/infra/servers`](./create.md) or [`GET /v1/infra/servers`](./list.md) |

## Examples

### curl — personal key

```bash
curl -H "X-Api-Key: YOUR_API_KEY" \
  https://vibecode.bitrix24.com/v1/infra/servers/e765edfc-ba0a-43de-b8ea-838dd872c522
```

### curl — OAuth application

```bash
curl -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID
```

### JavaScript — personal key

```javascript
// Poll the server until it is ready
async function waitReady(serverId) {
  while (true) {
    const res = await fetch(
      `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}`,
      { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
    )
    const { data } = await res.json()
    if (data.access?.via === 'galaxy-reference') {
      throw new Error('Use the Galaxy application ID, not the host ID, to wait for readiness')
    }
    if (data.status === 'running' && data.blackholeStatus === 'CONNECTED') {
      return data
    }
    if (data.status === 'error') {
      throw new Error(`Server in error state: ${data.id}`)
    }
    await new Promise(r => setTimeout(r, 10000))
  }
}
```

### JavaScript — OAuth application

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}`,
  {
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)
const { data } = await res.json()
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | Always `true` on success |
| `data` | object | Full owner card, shortened `collaborator` card or `galaxy-reference` projection. A referenced host projection contains only `id`, `status`, `kind`, `galaxyId`, `name`, `displayName`, `createdAt` and `access`. The other fields in this table are absent from it |
| `data.id` | string (UUID) | Server ID |
| `data.status` | string | Last stored status: `provisioning` \| `running` \| `stopped` \| `sleeping` \| `error`. A deleted server returns 404 |
| `data.provider` | string | Provider ID |
| `data.kind` | string | Resource type: `STANDALONE` (a dedicated virtual machine), `GALAXY` (a host carrying Galaxy applications) or `GALAXY_APP` (a Galaxy application — a container on a shared host). Determines the deploy contract — check it before uploading code |
| `data.galaxyId` | string (UUID) \| null | For `GALAXY_APP` — the ID of the host the application runs on. `null` for other types |
| `data.appCount` | number \| null | For a `GALAXY` host — the number of non-deleted applications on it. `null` for other types |
| `data.diskTotalMb` | number \| null | Disk size of the galaxy host machine, in mebibytes. `null` for `STANDALONE` and `GALAXY_APP` |
| `data.diskFreeMb` | number \| null | Free disk space on the galaxy host machine, in mebibytes. `null` for `STANDALONE` and `GALAXY_APP` |
| `data.diskState` | string \| null | Disk usage verdict: `ok`, `warning`, `critical`, or `unknown` when nothing has been measured yet. Thresholds are described in [Galaxy](/docs/infra/galaxy). `null` for `STANDALONE` and `GALAXY_APP` |
| `data.diskProbedAt` | string (ISO 8601) \| null | Time of the disk measurement. A sleeping machine reports the last known value together with the time of that measurement |
| `data.reachability` | object \| null | `GALAXY_APP` only — whether the application can respond right now: `effectiveStatus`, the state of the carrying galaxy in `hostStatus` and `hostTunnel`, the live readings `container` and `forwarder`, the state of the public address `publicEntry`, the poll outcome `probe` and its timestamp `probedAt`. The `status` field above is the state of the application record, and during a wake it lags behind the machine. The values are explained in [Galaxy app sleep and wake](/docs/infra/galaxy-sleep). `null` for other types |
| `data.name` | string | System name of the server — the technical identifier |
| `data.displayName` | string \| null | Display name for the UI. Equals `name` if it was not provided at creation |
| `data.description` | string \| null | Application description shown on the Bitrix24 catalog card. `null` if no description was set. Changed via [`PATCH /v1/infra/servers/:id`](./update.md) |
| `data.ip` | string \| null | Public IP. `null` until the virtual machine receives an address |
| `data.ssh` | object \| null | For an OPEN server: `{ user, port, hasPassword }`. For BLACKHOLE it may be `null` or the same block without actual access. **The password and private key are not returned here** — they are returned by [`GET /v1/infra/servers/:id/ssh`](/docs/infra/access/ssh) for a server in OPEN mode, which a person switches on in the cabinet, and the [`POST /v1/infra/servers`](./create.md) response does not contain them |
| `data.plan` | string | Plan. **For a Galaxy application** (`kind: "GALAXY_APP"`) the host's plan is returned, not the one requested at creation — applications share the host's resources |
| `data.region` | string | The region the server actually landed in. **For a Galaxy application** the host's region is returned (the platform picks placement itself), not the one requested |
| `data.image` | string | OS image |
| `data.monthlyCost` | string | Catalog monthly cost in Vibe credits (Ꝟ) as a string (for example `"24"`). The amount actually billed may differ. Compare via `Number(data.monthlyCost)` |
| `data.mode` | string | `BLACKHOLE` or `OPEN` |
| `data.createdVia` | string | `api` — a call through the Vibecode API, `ui` — an action in the Vibecode dashboard, `galaxy` — a Galaxy application. Agent and bot servers carry their own values |
| `data.subdomain` | string | Subdomain for the application |
| `data.blackholeStatus` | string | `NONE` \| `CONNECTED` \| `DISCONNECTED`. For a running BLACKHOLE server, a stored `CONNECTED` value is checked against the current gateway snapshot for this response only; this GET changes neither the database row nor the tunnel |
| `data.accessPolicy` | string | `OWNER_ONLY` \| `NAMED_USERS` \| `DEPARTMENT` \| `PORTAL` \| `AUTHENTICATED` \| `PUBLIC` |
| `data.runtimeId` | string \| null | ID of the runtime installed via POST /:id/deploy (server creation always returns `null`). |
| `data.runtimeStatus` | string \| null | A deprecated field, kept for compatibility. For servers created after 2026-04-25 it always returns `null`. The runtime is installed at the [`POST /:id/deploy`](/docs/infra/deploy/deploy) stage, and its readiness signal is the success of the `runtime` step in the deploy response, not this field's value |
| `data.startCommand` | string \| null | Start command of the last SUCCESSFUL deploy (`start` in [`POST /:id/deploy`](/docs/infra/deploy/deploy)). `null` means either no command was needed or no deploy has completed yet; distinguish those cases with `deployPort` |
| `data.installCommand` | string \| null | Install command of the same deploy. `null` — the deploy ran without `install`, including when an empty value was passed |
| `data.deployPort` | number \| null | Port declared in the same deploy (`port` in `POST /:id/deploy`). Distinct from `localPort`: [`PATCH /v1/infra/servers/:id/port`](/docs/infra/deploy/port) changes `localPort` independently of any deploy. `null` — no deploy yet |
| `data.appUrl` | string \| null | HTTPS address of the application |
| `data.localPort` | number | Configured port (default `3000`). In auto-detect mode it is the preferred fallback; `detectedPort` shows the last actual target. Changed via [`PATCH /v1/infra/servers/:id/port`](/docs/infra/deploy/port) |
| `data.detectedPort` | number \| null | Last port reported by the connected agent's scanner. `null` means no reliable observation has been stored yet; a reply from an agent without a scanner neither creates nor erases an observation |
| `data.detectedPortObservedAt` | string (ISO 8601) \| null | When the platform received `detectedPort`. It is updated asynchronously after the tunnel connects; this GET does not probe the agent |
| `data.portPinned` | boolean | The port is pinned in the agent settings on the machine, so it survives sleep, wake, and tunnel repair. `false` — the agent detects the port itself on every start. Details — [Change the port](/docs/infra/deploy/port) |
| `data.sleepAfterMinutes` | number \| null | Number of idle minutes after which the server is automatically put to sleep. `null` — never auto-sleep |
| `data.idleSleepProtected` | boolean | `true` when idle sleep is unavailable because the server must stay reachable. The field exposes only the decision and no bot or API-key metadata |
| `data.runMode` | string | Run mode: `IDLE` — sleeps when idle, `SCHEDULE` — runs on a schedule, `ALWAYS` — around the clock. Derived from the assigned schedule, `idleSleepProtected`, `createdVia`, and `sleepAfterMinutes`. A protected server without a schedule is `ALWAYS` even when it stores a numeric threshold |
| `data.workSchedule` | object \| null | The assigned schedule, or `null`. Fields: `id`, `name` (empty for a platform preset — identify it by `presetKey`), `presetKey`, `timezone`, `windows`. Every window is `{{ isoDay, start, end }}`, where `isoDay` is 1…7 counted from Monday and `start`/`end` are local HH:MM in the timezone of that schedule. Windows arrive as a LIST: one day may hold two of them, a shift with a break for instance |
| `data.provisionError` | string \| null | A short reason for the last provisioning/build failure. `null` if there were no errors |
| `data.provisionErrorCode` | string \| null | Public failure category: `PROVISION_TIMEOUT` / `NO_CAPACITY` / `NETWORK_CAPACITY` / `OVERLOADED` / `TRANSPORT_UNAVAILABLE` / `CONFIG` / `INVALID_INPUT` / `GENERIC` / `BOX_UNREACHABLE` / `HOST_UNREACHABLE` / `WAKE_NO_CAPACITY` / `PREEMPTIBLE_EVICTION` / `AGENT_SERVER_GONE` / `AGENT_NEVER_CONNECTED` / `KEY_ISSUE_REFUSED` / `KEY_ISSUE_OUTCOME_UNKNOWN` / `KEY_ISSUE_RESPONSE_LOST` / `DISK_FULL` / `CREATION_HELD` / `KEY_ISSUE_MODULE_MISSING` / `GUEST_NOT_BOOTING`. A creation hold may instead return the stored hold reason, for example `BILLING_FROZEN` or `TRIAL_EXPIRED`. Internal and unknown codes are returned as `GENERIC` with a neutral `provisionError`: contact support before creating another server. `null` when no code is stored. `GUEST_NOT_BOOTING` is terminal; it and `DISK_FULL` are explained in "Known specifics" |
| `data.provisionReason` | string \| null | Structured cause of a Galaxy application failure: `oom` — the container ran out of memory, or `crash`. `null` for regular servers and when there was no failure. `oom` is the signal to move the application to a dedicated server; the procedure is described in [Create a server](./create.md) |
| `data.buildLog` | string \| null | Tail of the Docker build log (≤8 KB) for a failed Galaxy application build. `null` for regular servers and on a successful build |
| `data.nextScheduledWakeAt` | string (ISO 8601) \| null | The next scheduled wake time (lead offset included). `null` if the server has no enabled wake windows. Configured via [Scheduled wake](/docs/infra/wake-schedules) |
| `data.wakeScheduleCapable` | boolean | Whether scheduled wake windows can be configured for the server. Depends on the server kind and whether the feature is enabled for the Bitrix24 account |
| `data.buildHint` | string \| null | A localized recommendation for a Galaxy app build failure or a busy upload path. Always present. Set for a recognized cause in `error`, and for `provisioning` with a busy upload path (`GALAXY_UPLOAD_BUSY`), where it advises retrying the same deploy. A stuck background attempt changes to `error` after the timeout, preserving the busy-path reason and hint. Otherwise `null`, including for regular servers |
| `data.b24CatalogSync` | object | State of the app card in the Vibecode apps catalog in the Bitrix24 account: `{ status, itemId, attempts, pendingOp, eligible }`. Field breakdown — [Publish to the catalog](./b24-catalog-publish.md) |
| `data.createdAt` | string (ISO 8601) | Creation timestamp |
| `data.access` | object | For a team member, contains `via: "collaborator"`, `role`, `allowedActions`, `allowedEndpoints` and `_note`. For a referenced host, contains `via: "galaxy-reference"`, `allowedActions: ["read"]`, only `GET /v1/infra/servers` and `GET /v1/infra/servers/:id` in `allowedEndpoints`, and `_note`, with no `role` field. The managing key gets no such field in the detail card — [List servers](./list.md) returns the `via: "owner"` marker |

## Response example

Full owner card:

```json
{
  "success": true,
  "data": {
    "id": "e765edfc-ba0a-43de-b8ea-838dd872c522",
    "status": "running",
    "provider": "bitrix-cloud",
    "kind": "STANDALONE",
    "galaxyId": null,
    "appCount": null,
    "diskTotalMb": null,
    "diskFreeMb": null,
    "diskState": null,
    "diskProbedAt": null,
    "name": "vibe-server-pd9l",
    "displayName": "vibe-server-pd9l",
    "description": null,
    "ip": "111.88.251.211",
    "ssh": {
      "user": "ubuntu",
      "port": 22,
      "hasPassword": true
    },
    "plan": "bc-small",
    "region": "bc-eu-central",
    "image": "ubuntu-2404-lts",
    "monthlyCost": "24",
    "mode": "OPEN",
    "createdVia": "ui",
    "subdomain": "app-05b67cf7",
    "blackholeStatus": "CONNECTED",
    "accessPolicy": "OWNER_ONLY",
    "runtimeId": null,
    "runtimeStatus": null,
    "startCommand": null,
    "installCommand": null,
    "deployPort": null,
    "appUrl": "https://app-05b67cf7.vibecode.bitrix24.com",
    "localPort": 3000,
    "detectedPort": 3000,
    "detectedPortObservedAt": "2026-09-15T08:15:30.000Z",
    "portPinned": false,
    "sleepAfterMinutes": null,
    "idleSleepProtected": false,
    "runMode": "IDLE",
    "workSchedule": null,
    "provisionError": null,
    "provisionErrorCode": null,
    "provisionReason": null,
    "b24CatalogSync": {
      "status": "IDLE",
      "itemId": null,
      "attempts": 0,
      "pendingOp": null,
      "eligible": true
    },
    "buildLog": null,
    "nextScheduledWakeAt": null,
    "wakeScheduleCapable": true,
    "buildHint": null,
    "createdAt": "2026-04-03T13:30:25.819Z"
  }
}
```

The shared host referenced by the `galaxyId` of the current key's Galaxy application, as a read-only projection with `access.via: "galaxy-reference"`:

```json
{
  "success": true,
  "data": {
    "id": "3f7a91c4-6e0b-42d8-8b55-19ad7c204e6f",
    "status": "running",
    "kind": "GALAXY",
    "galaxyId": null,
    "name": "vibe-galaxy-host-q7x2",
    "displayName": "Shared Galaxy host",
    "createdAt": "2026-06-18T08:15:30.000Z",
    "access": {
      "via": "galaxy-reference",
      "allowedActions": ["read"],
      "allowedEndpoints": [
        "GET /v1/infra/servers",
        "GET /v1/infra/servers/:id"
      ],
      "_note": "This host is readable only because one of your galaxy applications references it. Host management and application operations must target resources owned by your key, not this shared host."
    }
  }
}
```

## Error response example

404 — the server does not exist, is deleted, or the current key has no managing, team or reference access:

```json
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Server not found"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 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 |
| 403 | `EXTERNAL_COLLABORATOR_KEY_SERVER_MISMATCH` | The call was made with an external-collaborator key, and the address names a server other than the one the key was issued for. Such a key is valid for exactly one server, and the response does not reveal whether the requested server exists. Working with another server requires the key issued for that external membership |
| 403 | `EXTERNAL_COLLABORATOR_NOT_A_MEMBER` | An external-collaborator key addresses its own server, but access is not granted right now: the membership was removed, it expired, or external development collaborators are switched off for the Bitrix24 account that owns the server. The code does not tell these reasons apart and reveals nothing about the server. Membership is re-read on every request, so revoked access stops working immediately |
| 404 | `NOT_FOUND` | Server not found, deleted or inaccessible to the current key through management, application binding or development-team membership. A foreign host stays hidden unless a live Galaxy application managed by the key references it in the same portal |
| 429 | `RATE_LIMITED` | The platform's overall request limit was exceeded |

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

## Known specifics

- **The card uses an effective tunnel status without side effects.** If a running BLACKHOLE server is stored as `CONNECTED` but its subdomain is absent from a successful non-empty gateway snapshot, the response shows `DISCONNECTED`. An unavailable or empty snapshot preserves the stored value. The request does not run `repair` or write the check result to the database. This does not apply to the `galaxy-reference` projection, which always remains a stored snapshot.
- **A development-team member's card is shorter and does not poll the provider.** With `access.via: "collaborator"` the fields `ip`, `ssh`, `localPort`, `detectedPort`, `detectedPortObservedAt`, `portPinned`, `deletedAt`, `appCount`, the disk fields, `wakeScheduleCapable` and `b24CatalogSync` do not arrive, while `monthlyCost`, `sleepAfterMinutes` and `nextScheduledWakeAt` are added for the `ADMIN` role only. Application diagnostics and the last deploy parameters stay in place: `reachability`, `buildLog`, `buildHint`, `startCommand`, `installCommand` and `deployPort` arrive exactly as they do for the owner. In this card, the status comes from the platform's last measurement rather than being reconciled with the provider on the spot, so on a freshly created server it can lag by one polling tick — for a readiness-waiting loop use the key the server was created with.
- **The `galaxy-reference` projection is shorter still and always uses stored state.** It does not contact the provider or gateway even with `status: "provisioning"`. The projection exists only while a live Galaxy application managed by the current key references a live `GALAXY` host in the same Bitrix24 account. It grants access only to this GET and the server list. `/deploy`, `/exec`, `/upload`, `/logs`, `/sources`, `/ssh`, `/refresh`, lifecycle operations, `PATCH` and `DELETE` remain unavailable for the host.
- **For a full owner card, a provider request is made when the status is `provisioning` or the IP is missing.** In either case, the server must have an external ID. When an IP is already stored, no request is made for `running`/`stopped`/`sleeping`/`error`. To force reconciliation, use [`POST /v1/infra/servers/:id/refresh`](/docs/infra/lifecycle/refresh).
- **The readiness criterion is two fields.** To continue working with the [Deploy API](/docs/infra/deploy) you need both `status: "running"` **and** `blackholeStatus: "CONNECTED"` at the same time. If only `running` is set, the virtual machine is already up, but the tunnel agent has not connected yet. You do not have to wait forever: on a newly created standalone server (`kind: "STANDALONE"`) that holds `running` with `blackholeStatus: "NONE"` for more than ten minutes, the agent will not come up — the platform moves such a server to `error` with the code `AGENT_NEVER_CONNECTED` (see below). Runtime readiness after a deploy is determined by the success of the `runtime` step in the [`POST /:id/deploy`](/docs/infra/deploy/deploy) response — the `runtimeStatus` field is not used for this: it is deprecated and stays `null`.
- **`blackholeStatus: "DISCONNECTED"` with `running`** — the tunnel lost its connection on a live server. Try [`POST /v1/infra/servers/:id/repair`](/docs/infra/lifecycle/repair).
- **`AGENT_NEVER_CONNECTED` — the agent never came up on a new machine.** The virtual machine started normally, but the on-boot agent install did not finish, so there is no tunnel and there will not be one: the first-boot script runs once. For a standalone server (`kind: "STANDALONE"`) the platform issues this verdict only while it is newly created and holds `status: "running"` with `blackholeStatus: "NONE"` for more than ten minutes — the server moves to `error` and billing for the machine is closed out. Try [`POST /v1/infra/servers/:id/repair`](/docs/infra/lifecycle/repair) first: it reinstalls the agent over SSH. If that does not help, recreate the server. [`POST /start`](/docs/infra/lifecycle/start) on such a server answers `422` with the same code: starting it fixes nothing and erases the explanation. The verdict applies to Black Hole servers only — on an open-mode server `running` with `blackholeStatus: "NONE"` is a normal state. A galaxy host (`kind: "GALAXY"`) gets the same code on its own threshold — fifteen minutes in `running` with `blackholeStatus: "NONE"` — and not only right after creation: the verdict is issued whenever the host stays in that state past the threshold again. Existing galaxy hosts that already had the legacy `provisionError` text before this change keep `provisionErrorCode: null` in GET responses, but `/start` and `availableActions` recognize that exact legacy reason and expose the same repair-only behavior. The rest is the same: billing is closed out, `POST /start` answers `422` with this code, and repair fixes it. Should the agent connect on its own, the server returns to `running` and both `provisionError` and `provisionErrorCode` are cleared.
- **`DISK_FULL` — the repair could not install the agent: the server's disk is full.** The code is recorded by a [repair](/docs/infra/lifecycle/repair) that failed. Repeating the repair helps only once disk space has been freed. A successful repair clears the code. The server does not move to `error`. If the repair woke a sleeping server, the machine stays running after the failure until auto-sleep, or until someone stops it if auto-sleep is off.
- **`monthlyCost` pitfall:** the field is returned as a string. Two such strings are compared character by character: `"24" > "1000"` returns `true`, and `.sort()` orders the values lexicographically, not numerically. Convert to a number via `Number(data.monthlyCost)`.
- **The password and key of an OPEN server can always be read again.** The create response does not contain SSH credentials. A person switches OPEN mode on in the cabinet; an API key cannot do it. While the server is in OPEN mode, they are returned by [`GET /v1/infra/servers/:id/ssh`](/docs/infra/access/ssh). With an API key alone, work through the [Deploy API](/docs/infra/deploy). For BLACKHOLE, recreate the SSH key manually via [`POST /v1/infra/servers/:id/exec`](/docs/infra/deploy/exec) or recreate the server.
- **`GUEST_NOT_BOOTING` is terminal — retrying does not help.** The guest operating system cannot boot. [`POST /repair`](/docs/infra/lifecycle/repair) and [`POST /start`](/docs/infra/lifecycle/start) return `422` with this code, [`availableActions`](/docs/infra/lifecycle/refresh) keeps only `delete`, and billing has stopped. Application deletion still requires confirmed container removal: [`DELETE /v1/infra/servers/:id`](./delete.md) returns `502 GALAXY_HOST_UNREACHABLE` if teardown cannot be confirmed and preserves the application and its binding. Restore host access and retry, or contact platform support to confirm cloud VM destruction and finish cleanup. Scheduled clients must recreate this machine instead of repeatedly starting or repairing it.

## See also

- [Create a server](./create.md)
- [List servers](./list.md)
- [Update name and description](./update.md)
- [Delete a server](./delete.md)
- [Refresh status](/docs/infra/lifecycle/refresh)
- [Tunnel metrics](/docs/infra/deploy/metrics)
