## Update a wake window

`PATCH /v1/infra/servers/:id/wake-schedules/:scheduleId`

Replaces the window in full — despite the `PATCH` method, the semantics match a full replacement (`PUT`). Omitted optional fields reset to their defaults instead of keeping their previous value: `enabled` → `true`, `label`/`lead` → empty. Send the complete window object, not just the field that changed.

## Parameters

| Parameter | In | Type | Required | Description |
|-----------|----|------|:--------:|--------------|
| `id` | path | string (UUID) | yes | ID of the BLACKHOLE server |
| `scheduleId` | path | string | yes | Window ID, taken from `data[].id` in the [window list](./list.md) or `data.id` from [creating a window](./create.md) |

## Request fields (body)

Same field set as [creating a window](./create.md). Omitted optional fields don't keep their old value — they reset to the default.

| Field | Type | Required | Default when omitted | Description |
|-------|------|:--------:|---------|--------------|
| `cronExpr` | string | yes | — | A 5-field cron expression, 9 to 120 characters |
| `timezone` | string | yes | — | IANA timezone of the window |
| `label` | string | no | resets to empty | Window label, up to 80 characters |
| `lead` | number | no | resets to empty (the server's or the platform's margin is used) | Margin, in seconds, before the `cronExpr` moment. 0 to 3600 |
| `enabled` | boolean | no | resets to `true` | Whether the window is active |

## Examples

### curl — personal key

```bash
curl -X PATCH "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/wake-schedules/SCHEDULE_ID" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cronExpr": "0 10 * * 1-5",
    "timezone": "America/New_York",
    "label": "daily report",
    "enabled": true
  }'
```

### curl — OAuth app

```bash
curl -X PATCH "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/wake-schedules/SCHEDULE_ID" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "cronExpr": "0 10 * * 1-5",
    "timezone": "America/New_York",
    "label": "daily report",
    "enabled": true
  }'
```

### JavaScript — personal key

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/wake-schedules/${scheduleId}`,
  {
    method: 'PATCH',
    headers: {
      'X-Api-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    // Full window object — fields omitted here reset to their defaults
    body: JSON.stringify({
      cronExpr: '0 10 * * 1-5',
      timezone: 'America/New_York',
      label: 'daily report',
      enabled: true,
    }),
  }
)
```

### JavaScript — OAuth app

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/wake-schedules/${scheduleId}`,
  {
    method: 'PATCH',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      cronExpr: '0 10 * * 1-5',
      timezone: 'America/New_York',
      label: 'daily report',
      enabled: true,
    }),
  }
)
```

## Response fields

| Field | Type | Description |
|-------|------|--------------|
| `success` | boolean | Always `true` on success |
| `data.id` | string | Window ID |
| `data.serverId` | string (UUID) | ID of the owning server |
| `data.cronExpr` | string | The updated cron expression |
| `data.timezone` | string | The updated timezone |
| `data.label` | string \| null | Window label |
| `data.lead` | number \| null | Margin in seconds |
| `data.enabled` | boolean | Whether the window is active after the update |
| `data.source` | string | How the window was created. Unaffected by an update |
| `data.lastFiredAt` | string (ISO 8601) \| null | Timestamp of the last confirmed firing. An update doesn't touch this field |
| `data.wakeAttemptStartedAt` | string (ISO 8601) \| null | Internal scheduler marker, normally `null` |
| `data.lastWakeLateAt` | string (ISO 8601) \| null | Timestamp of the last late wake |
| `data.createdAt` | string (ISO 8601) | Record creation date |
| `data.updatedAt` | string (ISO 8601) | Date of this update |
| `tzWarning` | string \| null | Timezone-mismatch advisory, as text. `null` when the server has no active windows left after this mutation |
| `tzWarningCode` | string \| null | Machine-readable code for the same advisory: `"SINGLE_ZONE"`, `"MULTI_ZONE"`, or `null` |
| `preemptibleAdvisoryCode` | string \| null | `"PREEMPTIBLE_BEST_EFFORT"` when the server runs on a preemptible plan — otherwise `null` |
| `preemptibleAdvisory` | string \| null | An explanation of the same advisory, as text (in English, for non-UI API clients). `null` when the server is on a non-preemptible plan |

`tzWarning`, `tzWarningCode`, `preemptibleAdvisoryCode`, and `preemptibleAdvisory` are top-level response fields, not nested under `data`.

## Response example

```json
{
  "success": true,
  "data": {
    "id": "cm38x02qp0001ml08g7k3h2a",
    "serverId": "e765edfc-ba0a-43de-b8ea-838dd872c522",
    "cronExpr": "0 10 * * 1-5",
    "timezone": "America/New_York",
    "label": "daily report",
    "lead": null,
    "enabled": true,
    "source": "MANUAL",
    "lastFiredAt": "2026-07-10T06:00:04.000Z",
    "wakeAttemptStartedAt": null,
    "lastWakeLateAt": null,
    "createdAt": "2026-07-03T08:12:00.000Z",
    "updatedAt": "2026-07-10T09:30:00.000Z"
  },
  "tzWarning": "The VM may have been deployed with a different timezone (or not redeployed since this window was declared) — verify the in-VM cron or redeploy the app so TZ is re-injected.",
  "tzWarningCode": "SINGLE_ZONE",
  "preemptibleAdvisoryCode": null,
  "preemptibleAdvisory": null
}
```

A server on a preemptible plan would get `"preemptibleAdvisoryCode": "PREEMPTIBLE_BEST_EFFORT"` and a non-empty `preemptibleAdvisory` instead of `null` in those same two fields — the rest of the response shape is unchanged.

## Error response example

404 — window not found:

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

## Errors

| HTTP | Code | Description |
|------|------|--------------|
| 400 | `VALIDATION_ERROR` | Body validation failed — missing `cronExpr`/`timezone`, `timezone` not in the IANA timezone list, `label` over 80 characters, `lead` outside 0–3600, or an unknown field in the body |
| 400 | `BLACKHOLE_ONLY` | The server is not in BLACKHOLE mode |
| 400 | `GALAXY_NOT_SUPPORTED` | The server is a Galaxy host (`kind: "GALAXY"`). Nested apps (`kind: "GALAXY_APP"`) are accepted — only the host itself is rejected |
| 400 | `ALWAYS_ON_CONFLICT` | The server runs in always-on mode — no auto-sleep |
| 400 | `CADENCE_TOO_LOW` | The gap between consecutive `cronExpr` occurrences is below the platform's floor — currently 5 minutes |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 401 | `INVALID_API_KEY` | The API key is invalid or expired |
| 403 | `WAKE_SCHEDULE_DISABLED` | Scheduled wake is not enabled for this portal |
| 403 | `INFRA_SCOPE_REQUIRED` | The key lacks the `vibe:infra` scope |
| 404 | `NOT_FOUND` | The server was not found/deleted/belongs to a different API key, or no window with that `scheduleId` exists on this server |
| 429 | `RATE_LIMITED` | The 10-requests-per-minute limit for this endpoint was exceeded |

Full list of shared error codes — [Errors](/docs/errors).

## Known behavior

- **`PATCH` replaces the window in full.** This is a departure from the general partial-`PATCH` standard across the Vibecode API — compare with [updating a deal](/docs/entities/deals/update), where omitted fields keep their previous value. Sending only the field that changed silently zeroes out the other optional fields — `enabled` becomes `true`, `label` and `lead` clear out. Build the body from the window's last known full state, not just the fields that changed.
- **`preemptibleAdvisoryCode`/`preemptibleAdvisory` are an advisory, not a gate.** The window is updated regardless of these fields' value — on a preemptible plan the platform does not forbid a schedule, it only honestly reports that waking by the window's moment is best-effort. Localize on the `preemptibleAdvisoryCode` code, not on the `preemptibleAdvisory` string — it is a fixed English string for non-UI clients.
- **Window existence is checked before the other gates.** A non-existent `scheduleId` returns `404 NOT_FOUND` even if the request also violates `BLACKHOLE_ONLY` or another server-mode gate.

## See also

- [List windows](./list.md)
- [Create a window](./create.md)
- [Delete a window](./delete.md)
