Para agentes de IA: markdown desta página — /docs-content-en/infra/wake-schedules.md índice da documentação — /llms.txt
Os artigos da documentação estão disponíveis atualmente em inglês.
Scheduled wake
Recurring wake windows for a server in BLACKHOLE mode: the platform brings the server up in time for a moment set by a cron expression, and the task itself is fired by the app's own cron once the machine is up — the platform never reads or runs the app's code.
On a preemptible plan, waking is best-effort, not a guarantee. A server of this type comes up as capacity frees up — through a queue — so there may be no free capacity by the window's moment, and the window can be skipped. The create and update response carries preemptibleAdvisoryCode: "PREEMPTIBLE_BEST_EFFORT" for exactly this case — see Create a window. For time-critical tasks, use a non-preemptible plan or the always-on (24/7) mode.
A deploy changes neither the windows nor the sleep timer. The wake schedule and the auto-sleep timer are stored on the platform, not in the application code, so shipping a new version does not reset them — the windows keep working with no extra action. The only thing the deploy takes from the schedule is the windows' timezone: on a dedicated virtual machine it lands in the TZ environment variable when every enabled window declares the same zone. Details — Full application deploy.
Creating or updating a wake schedule normally requires a numeric auto-sleep setting. Creating or updating any window while sleepAfterMinutes is null (“Never”) is rejected as an always-on conflict, even when that window is disabled. In the reverse direction, setting null is rejected only while an enabled window exists; existing disabled windows neither affect the timeout nor block that sleep-setting change. An explicit numeric sleepAfterMinutes remains the effective timeout and is not shortened by the schedule. Existing older records are not rewritten: if one already contains null plus an enabled and entitled schedule with a computed next wake, the platform's post-window timeout applies — 15 minutes of inactivity by default.
Scope: vibe:infra
Create a wake window
POST /v1/infra/servers/:id/wake-schedules
Declares a new recurring wake window for a server in BLACKHOLE mode. The body is a flat object, with no fields wrapper.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string (UUID) | yes | ID of the BLACKHOLE server |
Request fields (body)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
cronExpr |
string | yes | — | A 5-field cron expression (minute hour day-of-month month day-of-week), 9 to 120 characters |
timezone |
string | yes | — | IANA timezone of the window, e.g. America/New_York. Checked against Intl.supportedValuesOf('timeZone') — an unknown value is rejected at validation time |
label |
string | no | — | Display label for the window, up to 80 characters |
lead |
number | no | the server's margin, else the platform's | Margin, in seconds, before the cronExpr moment by which the server must already be up — covers VM boot time. 0 to 3600 |
enabled |
boolean | no | true |
Whether the window is active. A disabled window never triggers a wake but stays in the list |
The lead margin resolves in three tiers: the window's own value, else a server-level margin set outside this CRUD, else the platform default — currently 180 seconds. For a server with kind: "GALAXY_APP", waking runs in two phases — bringing up the host and starting the container — so budget up to 900 seconds for lead.
This schedule write requires a numeric auto-sleep setting. If the server stores sleepAfterMinutes: null (“Never”), the request is rejected as an always-on conflict even when enabled is false. In the reverse direction, setting null is rejected only while an enabled window exists; an already stored disabled window neither affects the timeout nor blocks that sleep-setting change. An explicit numeric value remains the effective timeout and is not shortened by the schedule. Existing older records are not rewritten: if one already contains null plus an effective schedule, the platform's post-window fallback applies — 15 minutes of inactivity by default.
Examples
curl — personal key
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/wake-schedules" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"cronExpr": "0 9 * * 1-5",
"timezone": "America/New_York",
"label": "daily report",
"enabled": true
}'
curl — OAuth application
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/wake-schedules" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cronExpr": "0 9 * * 1-5",
"timezone": "America/New_York",
"label": "daily report",
"enabled": true
}'
JavaScript — personal key
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/wake-schedules`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
cronExpr: '0 9 * * 1-5',
timezone: 'America/New_York',
label: 'daily report',
enabled: true,
}),
}
)
const body = await res.json()
if (body.tzWarningCode === 'MULTI_ZONE') {
console.warn('Windows span more than one timezone — TZ is not injected at deploy time')
}
JavaScript — OAuth application
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/wake-schedules`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
cronExpr: '0 9 * * 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 for a later update or delete |
data.serverId |
string (UUID) | ID of the owning server |
data.cronExpr |
string | The stored cron expression |
data.timezone |
string | The stored timezone |
data.label |
string | null | Window label |
data.lead |
number | null | Margin in seconds. null when not set on the window itself |
data.enabled |
boolean | Whether the window is active |
data.source |
string | How the window was created. Always "MANUAL" for windows created through this CRUD |
data.lastFiredAt |
string (ISO 8601) | null | Timestamp of the last confirmed firing. null for a freshly created window |
data.wakeAttemptStartedAt |
string (ISO 8601) | null | Internal scheduler claim marker — non-null only for the short interval while a wake is in flight. Normally null |
data.lastWakeLateAt |
string (ISO 8601) | null | Timestamp of the last wake that fired later than its computed window. null if there have been no late wakes |
data.createdAt |
string (ISO 8601) | Record creation date |
data.updatedAt |
string (ISO 8601) | Date of the last update to the record |
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
The server's only active window — the timezone is unambiguous:
{
"success": true,
"data": {
"id": "cm38x02qp0001ml08g7k3h2a",
"serverId": "e765edfc-ba0a-43de-b8ea-838dd872c522",
"cronExpr": "0 9 * * 1-5",
"timezone": "America/New_York",
"label": "daily report",
"lead": null,
"enabled": true,
"source": "MANUAL",
"lastFiredAt": null,
"wakeAttemptStartedAt": null,
"lastWakeLateAt": null,
"createdAt": "2026-07-10T08:12:00.000Z",
"updatedAt": "2026-07-10T08:12: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
400 — the window conflicts with an always-on server:
{
"success": false,
"error": {
"code": "ALWAYS_ON_CONFLICT",
"message": "Scheduled wake conflicts with an always-on (24/7) server, which never auto-sleeps. Turn off the always-on toggle, or set a sleep timeout instead of \"Never\", to schedule wake windows."
}
}
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"). A host holds no windows at all: the schedule is declared on an app inside the galaxy, not on the host itself |
| 400 | ALWAYS_ON_CONFLICT |
The server runs in always-on mode — no auto-sleep. A wake schedule would silently break that guarantee |
| 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 | WAKE_SCHEDULE_GALAXY_DISABLED |
The server is an app inside a galaxy (kind: "GALAXY_APP"). Such an app can hold windows, but the capability for Galaxy apps is not enabled yet. The capability is enabled separately from ordinary servers, so a kind: "STANDALONE" server never receives this code |
| 403 | WAKE_SCHEDULE_LIMIT |
The server already has 50 windows — the per-server cap |
| 403 | INFRA_SCOPE_REQUIRED |
The key lacks the vibe:infra scope |
| 403 | INFRA_FORBIDDEN_FOR_COWORK_KEY |
The call was made with a Cowork/Code key — such a key works with data only and cannot perform write operations. To issue a key that can, see Project key for deploy |
| 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 |
| 404 | NOT_FOUND |
The server was not found, was deleted, or belongs to a different API key while you are not on its development team |
| 429 | RATE_LIMITED |
The 10-requests-per-minute limit for this endpoint was exceeded |
Full list of shared error codes — Errors.
Known specifics
- Check order. The key's scope and server ownership are checked before the other conditions, so
WAKE_SCHEDULE_DISABLED/BLACKHOLE_ONLY/GALAXY_NOT_SUPPORTED/WAKE_SCHEDULE_GALAXY_DISABLED/ALWAYS_ON_CONFLICTonly ever surface for a request with a valid key against an owned, existing server. The checks run in that same order, so a Galaxy app receivesWAKE_SCHEDULE_GALAXY_DISABLEDrather thanALWAYS_ON_CONFLICTeven when it also runs in always-on mode. The cron-expression checks (VALIDATION_ERROR, thenCADENCE_TOO_LOW) run after these gates. - Two different galaxy refusals.
GALAXY_NOT_SUPPORTEDis about the galaxy host: it holds no windows at all, and enabling the capability will not change that.WAKE_SCHEDULE_GALAXY_DISABLEDis about an app inside a galaxy: it can hold windows, but the capability for such apps is switched off for now. The first arrives with status400, the second with403, so branch on the response code. tzWarning/tzWarningCodeare a conservative heuristic. The platform does not track whether the application was redeployed since a window was declared, so the advisory fires for any set of active windows after a mutation — even when the timezone is already correctly set in the environment. Go by the code:"SINGLE_ZONE"means the timezone is unambiguous and lands inTZon the next deploy of a dedicated virtual machine,"MULTI_ZONE"means the windows span more than one zone andTZis not auto-injected.preemptibleAdvisoryCode/preemptibleAdvisoryare an advisory, not a gate. The window is created or updated regardless of these fields' values — on a preemptible plan the platform does not forbid a schedule (that is its intended use case); it only states up front that waking the server in time for the window is best-effort. Localize bypreemptibleAdvisoryCode, not by thepreemptibleAdvisorystring — that is a fixed English string for non-UI clients, not meant to be shown to a user as-is.nullin the sleep setting blocks schedule creation and update. It is the stored “Never” choice and disables idle auto-sleep; creating or updating a window while it is stored is rejected as an always-on conflict, even when the submitted window is disabled. Settingnullthrough the sleep endpoint is narrower: that write is rejected only while an enabled window exists. If an older record already containsnullplus an effective schedule, the platform's post-window timeout applies between windows. V1 does not return the computed session policy: for long-running background work, keepnullwith no enabled windows or choose a numeric timeout long enough for the task.- The platform does not configure the cron inside the VM.
POST /wake-schedulesonly guarantees the server is up by the target moment (best-effort on a preemptible plan — seepreemptibleAdvisoryCodeabove). Firing the task at that moment is the job of the app's owncron/timer.