## Set the server run mode

`PATCH /v1/infra/servers/:id/run-mode`

Switches a BLACKHOLE server to one of three run modes: it sleeps when there are no requests, runs on a schedule, or runs around the clock. The mode determines what the server costs, so the request is either applied in full or refused with nothing changed.

If run modes are not enabled for the Bitrix24 account, the request is refused with `400 RUN_MODE_UNAVAILABLE`. The current mode is always readable — the `runMode` and `workSchedule` fields in [`GET /v1/infra/servers/:id`](/docs/infra/servers/get).

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|----------|
| `id` (path) | string | yes | ID of a server in BLACKHOLE mode. Source — the [server list](/docs/infra/servers/list) |

## Request fields (body)

The body is one of three shapes, chosen by the `mode` field. If the body contains a field that the selected shape does not accept, the request is rejected with `VALIDATION_ERROR` rather than the field being silently ignored.

| Field | Type | Required | Description |
|------|-----|:-----:|----------|
| `mode` | string | yes | `IDLE` — sleeps when there are no requests. `SCHEDULE` — runs during the windows of a schedule and sleeps outside them. `ALWAYS` — runs around the clock |
| `idleMinutes` | number | with `IDLE` | Minutes without requests before the server falls asleep: `15`, `30`, `60` or `240` |
| `scheduleId` | string | with `SCHEDULE` | ID of a schedule in the same Bitrix24 account. Source — the [schedule library](/docs/infra/work-schedules/list) |

## Examples

### curl — personal key

```bash
curl -X PATCH https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/run-mode \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "SCHEDULE", "scheduleId": "WORK_SCHEDULE_ID"}'
```

### curl — OAuth application

```bash
curl -X PATCH https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/run-mode \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode": "SCHEDULE", "scheduleId": "WORK_SCHEDULE_ID"}'
```

### JavaScript — personal key

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/run-mode`,
  {
    method: 'PATCH',
    headers: {
      'X-Api-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ mode: 'SCHEDULE', scheduleId: workScheduleId }),
  }
)
const { data } = await res.json()
console.log(`Mode: ${data.runMode}, next wake-up: ${data.nextScheduledWakeAt}`)
```

### JavaScript — OAuth application

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/run-mode`,
  {
    method: 'PATCH',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ mode: 'SCHEDULE', scheduleId: workScheduleId }),
  }
)
const { data } = await res.json()
```

## Response fields

The response carries the run-mode fields read after the write. The same shape comes in [`GET /v1/infra/servers/:id`](/docs/infra/servers/get).

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | Always `true` on success |
| `data.runMode` | string | Mode after the write: `IDLE`, `SCHEDULE` or `ALWAYS` |
| `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.sleepAfterMinutes` | number \| null | The stored idle threshold in minutes. In `SCHEDULE` mode it is the remembered value: it does not apply while a schedule is assigned, and the schedule does not erase it |
| `data.workSchedule` | object \| null | The assigned schedule, or `null` |
| `data.workSchedule.id` | string | Schedule ID |
| `data.workSchedule.name` | string \| null | Name of a custom schedule. A platform preset has `null` here — identify it by `presetKey` |
| `data.workSchedule.presetKey` | string \| null | Preset key: `weekdays-9-18`, `weekdays-9-19`, `weekdays-9-20`, `weekdays-8-19`, `weekdays-8-20`, `daily-9-21`. `null` for a custom schedule |
| `data.workSchedule.timezone` | string | IANA time zone used to interpret the schedule windows |
| `data.workSchedule.windows` | array | Windows by weekday: `isoDay` from 1 (Monday) to 7, `start` and `end` in local `HH:MM`. A day can hold two windows |
| `data.nextScheduledWakeAt` | string (ISO 8601) \| null | The next planned wake-up, lead time included. `null` when no wake-up is planned |

## Response example

```json
{
  "success": true,
  "data": {
    "runMode": "SCHEDULE",
    "idleSleepProtected": false,
    "sleepAfterMinutes": 30,
    "workSchedule": {
      "id": "cmfp4r0q2000a1ocg7h2k9x3d",
      "name": "Warehouse shifts",
      "presetKey": null,
      "timezone": "Europe/Berlin",
      "windows": [
        { "isoDay": 1, "start": "09:00", "end": "18:00" },
        { "isoDay": 2, "start": "09:00", "end": "13:00" },
        { "isoDay": 2, "start": "14:00", "end": "24:00" }
      ]
    },
    "nextScheduledWakeAt": "2026-09-21T06:51:00.000Z"
  }
}
```

## Error response example

400 — idle sleep requested for a server that must stay reachable:

```json
{
  "success": false,
  "error": {
    "code": "AGENT_IDLE_SLEEP_FORBIDDEN",
    "message": "idle sleep is not available while the server must stay reachable"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `VALIDATION_ERROR` | The body matches none of the three shapes: an unknown field, `idleMinutes` not one of `15`, `30`, `60`, `240`, a missing `scheduleId` |
| 400 | `RUN_MODE_UNAVAILABLE` | Run modes are not enabled for the Bitrix24 account |
| 400 | `BLACKHOLE_ONLY` | The server is not in BLACKHOLE mode |
| 400 | `GALAXY_NOT_SUPPORTED` | The server is a galaxy host. A host has no run mode of its own: it runs while any of its apps runs, so the mode is set on the apps |
| 400 | `AGENT_IDLE_SLEEP_FORBIDDEN` | `IDLE` was requested for a server with `idleSleepProtected: true`. This is an agent or bot server, or an ordinary application with an active bot that receives events by polling. `SCHEDULE` and `ALWAYS` are available |
| 400 | `WORK_SCHEDULE_EMPTY` | The schedule has no windows |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 401 | `INVALID_API_KEY` | The key is not recognized: no such key exists on the platform |
| 403 | `INFRA_SCOPE_REQUIRED` | The key lacks the `vibe:infra` scope |
| 403 | `INFRA_FORBIDDEN_FOR_COWORK_KEY` | The call was made with a Cowork key, which can only access data and cannot perform state-changing operations. For deployment, see [Project key for deployment](/docs/cowork/deploy-key) |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key is in read-only mode — it cannot write the run mode |
| 403 | `GALAXY_DISABLED` | The server is a galaxy app, and galaxies are switched off for the portal |
| 403 | `SERVER_ROLE_FORBIDDEN` | You are on this server's development team with the Developer role, while the operation is open to the Administrator role. `error.hint` carries your role, the required threshold and the calls open to you. Roles are explained in [List servers](/docs/infra/servers/list) |
| 404 | `NOT_FOUND` | The server does not exist, is deleted or is bound to another API key, and you are not on its development team |
| 404 | `WORK_SCHEDULE_NOT_FOUND` | No schedule with this `scheduleId` exists in the key's Bitrix24 account. A schedule of another account gets the same answer — the response does not confirm that it exists |
| 429 | `RATE_LIMITED` | The limit of 20 requests per minute per key is exceeded. The exact value is in the `x-ratelimit-limit` header |

For the full list of shared API error codes, see [Errors](/docs/errors).

## Known specifics

- **A schedule outranks the idle threshold.** While a schedule is assigned, the server runs during its windows and sleeps after them, and the stored `sleepAfterMinutes` does not apply. Switching to `SCHEDULE` does not erase the threshold. If the schedule is later deleted with consent to move its machines, an ordinary app returns to `IDLE` with exactly this number, while a protected app stays in effective `ALWAYS` mode with the same remembered threshold — see [Delete a schedule](/docs/infra/work-schedules/delete).
- **Schedule windows become server wake-ups.** The platform brings the server up ahead of each window, and after the window ends the server goes to sleep when the platform idle timeout expires. These wake-ups do not appear in the [wake window list](/docs/infra/wake-schedules/list) and are not edited there — the schedule itself changes them. Windows created by hand are not touched by the run mode.
- **A request outside a window still wakes the server.** A request to the HTTPS subdomain wakes a sleeping server outside the schedule windows too — under the [automatic wake conditions](./wake.md). That running time is billed at the running-machine rate.
- **A galaxy app takes a run mode like a standalone server.** A galaxy sleeps only when all of its apps sleep. A schedule for one of two agents saves nothing while the other runs around the clock: set the mode on every app that must not keep the galaxy awake.
- **A write through [`/sleep`](./sleep.md) removes the schedule.** A number in `sleepAfterMinutes` moves the server to `IDLE`, `null` to `ALWAYS`, and the assigned schedule is removed in both cases.

## See also

- [Configure auto-sleep](./sleep.md)
- [Work schedule library](/docs/infra/work-schedules)
- [Get a server](/docs/infra/servers/get)
- [Scheduled wake](/docs/infra/wake-schedules)
