
## Reboot server

`POST /v1/infra/servers/:id/reboot`

Reboots a running server at the cloud provider. During the reboot the server moves to `provisioning`, the tunnel is briefly dropped — `blackholeStatus` becomes `DISCONNECTED` — and reconnects after the virtual machine starts. The operation is atomic: an internal database-level guard prevents races if several calls arrive at once. If the provider does not support reboot, the endpoint returns 501 and the status is rolled back to `running`.

## Parameters

| Parameter | In | Type | Required | Description |
|----------|---|-----|:-----:|----------|
| `id` | path | string (UUID) | yes | ID of a server in `running` status |

The request body is empty.

## Examples

### curl — personal key

```bash
curl -X POST -H "X-Api-Key: YOUR_API_KEY" \
  https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/reboot
```

### curl — OAuth application

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

### JavaScript — personal key

```javascript
await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/reboot`,
  { method: 'POST', headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
)

// Poll readiness after the reboot
while (true) {
  await new Promise(r => setTimeout(r, 5000))
  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.status === 'running' && data.blackholeStatus === 'CONNECTED') break
}
```

### JavaScript — OAuth application

```javascript
await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/reboot`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | `true`. The reboot command was sent to the provider |

## Response example

```json
{ "success": true }
```

## Error response example

422 — the server exists but is not in `running` status. The response carries the current state and the actions available now:

```json
{
  "success": false,
  "error": {
    "code": "SERVER_WRONG_STATE",
    "message": "Server is SLEEPING; /reboot requires RUNNING.",
    "userMessage": "Server is currently SLEEPING. Reboot only applies to a RUNNING server.",
    "currentState": { "status": "sleeping", "blackholeStatus": "DISCONNECTED", "hasExternalId": true },
    "availableActions": ["wake", "start", "repair", "delete"]
  }
}
```

409 — the server status changed during the operation (race):

```json
{
  "success": false,
  "error": {
    "code": "CONFLICT",
    "message": "Server state changed during operation"
  }
}
```

## 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 Vibecode balance is frozen. Top up and retry |
| 403 | `VIBE_DEMO_EXPIRED` | The server is a Vibe+ trial machine and the trial has ended: the platform froze it at the scheduled access check and refuses to reboot it. The server and its data are kept; a paid Vibe+ plan on the Bitrix24 account restores access; the server is not started automatically — start it with a new call |
| 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) |
| 403 | `SERVER_ROLE_FORBIDDEN` | You are on this server's development team with the Developer role, and this operation is open to the Administrator role. `error.hint` carries your role, the required threshold and the list of calls that are open to you. Role breakdown — [List servers](/docs/infra/servers/list) |
| 404 | `SERVER_NOT_FOUND` | No server with this `id` — deleted, or belonging to another API key while you are not on its development team |
| 409 | `CONFLICT` | State race — the server status changed during the operation. Retry the request after checking [`GET /v1/infra/servers/:id`](/docs/infra/servers/get) |
| 422 | `SERVER_WRONG_STATE` | The server exists but is not in `running` status. `error.currentState` carries the current state; `error.availableActions` lists what you can do now |
| 422 | `VM_MISSING` | The server record has no cloud VM (provisioning never finished or the VM was removed manually). Replace the server: delete it and create a new one. `error.hint` names the endpoint of each step only if that step is available to this request, and for an unavailable step it gives the reason for the refusal and what to change |
| 422 | `VM_POINTER_LOST` | The record has no `externalId`, but the cloud review does see a machine under this server's cloud name: the link is lost while the machine itself is most likely running and billed. Do NOT delete the server and create a new one — that would leave the machine running with no record at all. The server is not moved to `error`; contact support to have the link restored |
| 429 | `RATE_LIMITED` | The platform's overall request limit was exceeded |
| 501 | `NOT_SUPPORTED` | The cloud provider does not support reboot. Use the sequence [`/stop`](./stop.md) → [`/start`](./start.md) |
| 502 | `PROVIDER_ERROR` | The cloud provider returned an error. The server status is automatically rolled back to `running` |

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

## Known specifics

- **Atomic transition `running → provisioning`.** The database is updated with a single `updateMany` conditioned on `status = 'RUNNING'`. If another `/reboot` or `/stop` arrives at the same moment, the second call gets 409 `CONFLICT`.
- **Automatic rollback on a provider error.** If `adapter.rebootServer()` throws an exception, the record is reverted to `running` status — so the client is not left with a "stuck" `provisioning`.
- **To restart only the application** (without rebooting the virtual machine), use [`POST /exec`](/docs/infra/deploy/exec) with the command `systemctl restart app` — it is dozens of times faster and does not break the tunnel. This process restart does not itself delete server or application files. If a different service name (`serviceName`) was configured during deployment, replace `app` with that name. The application itself may modify its data on startup.
- **After a reboot, wait for both fields.** For the server to be ready, you need both `status: "running"` and `blackholeStatus: "CONNECTED"` at the same time — the tunnel reconnects after the virtual machine starts.

## Galaxy apps (kind=GALAXY_APP)

For a Galaxy app container (`kind=GALAXY_APP` — it has no virtual machine of its own), `/reboot` **restarts the container** on the host rather than rebooting a VM. It is a self-recovery kick for a stuck or crash-looping app: it does **not** wipe the persistent `/data` volume (unlike deletion).

The restart is **advisory** — it does not change the record's status and does not clear the crash marker. After the restart the container is probed, and the response carries a `healthy` verdict: **whether the container came up and stopped restarting**. This inspects the container state (`docker inspect`), not the app's HTTP response. A crash-looping app is authoritatively fixed by **redeploying** its source via [`POST /v1/infra/servers/:id/deploy`](/docs/infra/deploy/deploy) — redeploy is what clears the error state.

The app accepts `/reboot` in the `running` **or** `error` status (a regular server accepts only `running`).

### Response fields

The verdict is nested under `data` (unlike a regular server reboot, whose body is the flat `{ "success": true }`).

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | `true` — the command ran |
| `data.restarted` | boolean | `true` — the container was restarted; `false` — it could not be restarted (container missing / image deleted) — redeploy needed |
| `data.healthy` | boolean | `true` — the container came up and stopped restarting; `false` — it is crashing again (redeploy needed). This inspects the container, not the app's HTTP response |
| `data.hint` | string | present only when `data.healthy: false` — how to deploy a fixed version |

```json
{ "success": true, "data": { "restarted": true, "healthy": false, "hint": "Redeploy the fixed source via POST /v1/infra/servers/:id/deploy" } }
```

### Errors

| HTTP | Code | Description |
|------|------|-------------|
| 404 | `GALAXY_APP_NOT_FOUND` | No app with this `id` on the host — the container is already gone, or the record does not match the host |
| 409 | `CONFLICT` | The app status changed during the operation. Re-read the state and retry |
| 409 | `GALAXY_APP_REBOOT_USE_AGENT_CONTROLS` | The app was created by an agent or a bot — manage it from that agent's or bot's controls, not via `/reboot` |
| 409 | `GALAXY_APP_BUSY` | Busy — either another command is running on the host (a neighbouring app's build) or another operation already holds the lock for this same app (a repeat click / a deploy). Retryable: the response carries a `Retry-After` header plus `error.retryable: true` and `error.retryAfter` in the body |
| 422 | `SERVER_WRONG_STATE` | The app is not in `running`/`error` status (e.g. `sleeping`). `error.availableActions` lists what you can do now. In any status outside `running`/`error` this refusal additionally starts a host repair — see below |
| 502 | `GALAXY_HOST_UNREACHABLE` | The galaxy host is unreachable — its tunnel has dropped. `error.hint` says what to do. Retry after the host recovers |

### An app refusal also repairs the host

A Galaxy app whose host has lost its connection to the platform hits the same barrier in every operation: both waking and deploying go through the host, and the host is unreachable. So before the `422 SERVER_WRONG_STATE` refusal, the platform starts restoring the host connection in the background — the refusal itself still stands, but the next attempt has a chance to go through.

If the repair did start, the error body carries an optional `hint` object:

| Field | Type | Description |
|-------|------|-------------|
| `error.hint.reason` | string | Why the request was refused and what was started |
| `error.hint.recovery` | string | Which operation to retry — [`POST /deploy`](/docs/infra/deploy/deploy) |
| `error.hint.retryAfterSeconds` | number | Lower bound of the wait, in seconds. A guideline, not a promise of readiness |

The `hint` object may be absent, and that is not an error. It is missing when no repair was started: the host connection is in fact alive, a repair is already under way or finished recently, the host itself is off or asleep, it was woken recently, the guest system on the machine does not boot, the machine is blocked from waking, or automatic recovery is switched off on the platform. Read `hint` as an optional cue — if it is there, wait the stated time and retry the deploy; if it is not, act on the error code.

**Repeat calls do not start a new repair.** While a repair is running, and for 10 minutes after it ends — successfully or not — the platform starts no new one, so a repeat refusal arrives without `hint`. This is the same ten-minute window as the lifetime of a completed record in [Repair status](./repair-status.md). A repair that broke off and stopped reporting is treated as dead after 20 minutes and may be started again.

The `retryAfterSeconds` value is 300 and stays a lower bound on the wait rather than a promise of readiness: retrying sooner buys nothing, retrying later is fine.

**The `502 GALAXY_HOST_UNREACHABLE` refusal carries a different `hint`** — four fields instead of three, with their own set of values. They are described under "The `error.hint` field of `GALAXY_HOST_UNREACHABLE`" on the [Galaxy app](/docs/infra/galaxy) page. The shape of the cue is set by the refusal code, so parse it by the code rather than by the page it appeared on.

## See also

- [Start the server](./start.md)
- [Stop the server](./stop.md)
- [Repair tunnel](./repair.md)
- [Run a command](/docs/infra/deploy/exec)
