
## Repair tunnel

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

Starts a background repair of the Black Hole tunnel through the cloud provider's serial console. Use it when the virtual machine is in the `running` status, but `blackholeStatus` is stuck in `DISCONNECTED` or `NONE` — that is, the tunnel agent is not connecting. The procedure goes through the serial console (bypassing the firewall) and includes: injecting an SSH key → connecting to the machine → fully reinstalling the agent at the latest version → waiting for it to reconnect. It takes up to 2 minutes (up to 4 minutes if the fallback install path is needed — see "Known specifics"); the call itself returns immediately and does not wait for completion. Track the progress via [`GET /repair-status`](./repair-status.md).

> **Read the operation before repairing a busy channel.** `/repair` reinstalls the tunnel agent and can interrupt live synchronous [`/exec`](../deploy/exec.md) commands and their side effects, including external database transactions. Standalone durable jobs execute independently: repair or a tunnel bounce does not guarantee their termination. For `EXEC_BUSY`, first read the [server operations](../deploy/operations.md) with `kind=exec` and their [outcomes](../deploy/operation-status.md). Use guarded [`/unstick`](../servers/unstick.md) only for a confirmed leaked synchronous mutex after independently confirming idle state. [`DELETE /lock`](../deploy/lock.md) remains the final lock-recovery step after that check. Use `/repair` when the tunnel genuinely fails to connect; it does not serve as cancellation or permission to resend a command. On `STANDALONE`, a running durable job keeps the execution channel busy after `202` releases the platform lock. Poll its operation; an unknown outcome needs support and independently confirmed full termination. Missing `autoExpiresInSeconds` does not prove a leaked mutex. Tunnel recovery and lock deletion do not clear durable admission. Galaxy commands run synchronously with a 600-second limit and share the host channel across all applications and deploys; your own operation list may not show another application’s job, so an empty list does not prove the host is idle.

## Parameters

| Parameter | In | Type | Required | Description |
|----------|---|-----|:-----:|----------|
| `id` | path | string (UUID) | yes | Server ID |

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/repair
```

### 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/repair
```

### JavaScript — personal key

```javascript
// Start the repair and poll the status periodically
await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/repair`,
  { method: 'POST', headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
)

while (true) {
  await new Promise(r => setTimeout(r, 10000))
  const res = await fetch(
    `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/repair-status`,
    { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
  )
  const { data } = await res.json()
  console.log(`Step: ${data.step ?? data.status}`)
  if (data.status === 'idle' || data.status === 'completed' || data.status === 'failed') break
}
```

### JavaScript — OAuth application

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

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | `true` — the repair was successfully started in the background |
| `data.status` | string | Always `"started"` on success |

## Response example

```json
{
  "success": true,
  "data": { "status": "started" }
}
```

## Error response example

409 — repair blocked (for example, the server is marked `preventWake=true`):

```json
{
  "success": false,
  "error": {
    "code": "REPAIR_BLOCKED",
    "message": "Repair blocked: server prevents wake"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `INVALID_INPUT` | The record has no cloud virtual machine: provisioning never finished, or the machine was deleted on the provider's side — in that case the server has to be replaced: it can be deleted by the key that owns it, and a new one created by a key with the `vibe:infra` scope in `READWRITE` mode. The same code is returned for a Galaxy app: the container has no machine of its own, so repair does not apply to it — see [Galaxy app](/docs/infra/galaxy) |
| 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 |
| 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 repair 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 | `NOT_FOUND` | The server does not exist, was deleted, or belongs to another API key while you are not on its development team |
| 409 | `REPAIR_BLOCKED` | Repair blocked: the server is asleep or in error and has a wake block `preventWake=true` (for example, a frozen balance, expired platform access, an administrative block) |
| 422 | `GUEST_NOT_BOOTING` | The machine's guest operating system does not boot. Repair cannot fix that, so the call is refused before the machine is touched and does not wake it — the server has to be recreated |
| 422 | `REPAIR_NO_TRANSPORT` | The platform has no channel to the server: it cannot be reached over SSH, it has no console access, and the record has no key to sign in with. The call is refused before the machine is touched and does not wake it — recreate the server or restore it from a backup if one exists |
| 429 | `RATE_LIMITED` | The platform's overall request limit was exceeded |

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

## Known specifics

- **Why the serial console, not SSH.** When the agent is not connected, a BLACKHOLE server's iptables rules block SSH. The Bitrix24 Cloud provider's serial console bypasses the firewall at the hypervisor level and makes it possible to inject an SSH key into an already-running virtual machine without rebooting it.
- **Two agent-install paths.** By default the agent is reinstalled over inbound SSH — that is the fast path. When inbound SSH is unavailable (port 22 does not answer, the stored IP is stale, cloud firewall rules block it, sshd is down), the procedure installs the agent out-of-band over the same serial console: the agent only needs an OUTBOUND connection to the gateway, so inbound access is not required for a reinstall. A machine without a public IP uses the serial console directly; where there is none, the repair ends with the `REPAIR_NO_TRANSPORT` code. The fallback attempt adds up to two minutes to an already-failing call; if both paths fail, the `error` field of [`GET /repair-status`](./repair-status.md) carries both reasons joined by `; serial fallback:`.
- **Idempotent.** The procedure runs a full cycle (stop the agent → download a fresh binary → clean configuration → start) and is safe for a healthy server — it cannot do harm. A repeated call during an ongoing repair simply returns 409 / the same `started`, breaking nothing.
- **The server mode is preserved.** An OPEN server stays OPEN (iptables is not touched), and a BLACKHOLE server stays BLACKHOLE.
- **How to unblock `REPAIR_BLOCKED`.** If the server is marked `preventWake` (for example, a frozen balance, expired platform access, an administrative block), first resolve the cause (top up the balance or restore platform access), then retry `/repair`.
- **`GUEST_NOT_BOOTING` cannot be fixed by a repair.** The marker is visible in advance in the `provisionErrorCode` field of the server record — [`GET /v1/infra/servers/:id`](/docs/infra/servers/get). Read it before the call: for such a machine, repair is refused, and the only action left is to delete the server and create a new one.
- **A full disk stops the repair.** [`GET /repair-status`](./repair-status.md) returns `status: "failed"`, and the `error` text starts with `DISK_FULL:`. The same code appears in the `provisionErrorCode` field of the server record — [`GET /v1/infra/servers/:id`](/docs/infra/servers/get). Repeating the repair helps only once disk space has been freed. A successful repair clears the code.
- **`REPAIR_NO_TRANSPORT` — the platform has no channel to the server.** When the refusal is known up front (the record has no key to sign in with), `POST /repair` answers `422`: recreate such a server or restore it from a backup if one exists. When the platform could not get onto the server during the repair itself, [`GET /repair-status`](./repair-status.md) returns `status: "failed"`, and the `error` text starts with `REPAIR_NO_TRANSPORT:` followed by the sentence: `The platform could not reach this server to reinstall the agent: it has no working SSH path and no console access. Retry the repair later; if it keeps failing, contact support.` Retry the repair later, and contact support if the refusal persists. After such a refusal the repair does not put a server it woke back to sleep: the server stays on until its idle-sleep setting stops it or you put it to sleep yourself. Other repair failures carry their own error text. While the server is still starting up, the platform retries the sign-in; an authentication failure refuses the repair at once.

## See also

- [Repair status](./repair-status.md)
- [Refresh status](./refresh.md)
- [Get server](/docs/infra/servers/get)
- [Reboot server](./reboot.md)
