Maintenance Windows
The real request/response field shape for POST /v1/maintenance-windows and its /updates route.
View as Markdown/v1/maintenance-windows is a thin proxy in front of the same engine that powers the dashboard’s
maintenance tab - so its request/response bodies are plain JSON objects, not one of the
reference’s typed schemas (see the API reference for why those two endpoints
show up there as a bare object). This page is the field-by-field shape. For the incident
counterpart, see the Incidents guide - the two are separate endpoints, not
variants of each other.
Schedule a maintenance window
POST /v1/maintenance-windows
{
"title": "Database upgrade",
"service_ids": ["5b1e...", "8a2f..."],
"message": "Upgrading the primary database. Expect brief connection drops.",
"scheduled_start": "2026-09-10T02:00:00Z",
"scheduled_end": "2026-09-10T03:00:00Z"
}
| Field | Type | Required | Notes |
|---|---|---|---|
title |
string | yes | Non-empty. |
service_ids |
array of uuid | yes | Non-empty; every id must belong to your org. |
message |
string | yes | The maintenance window’s first update. |
scheduled_start |
string (RFC 3339) | yes | When the window begins. |
scheduled_end |
string (RFC 3339) | yes | Must be after scheduled_start. |
kind is not a field you send - every maintenance window created through this endpoint is forced
to kind maintenance server-side, even if you send something else. severity is never accepted
here either - maintenance windows don’t carry one; sending it is rejected as invalid.
A 201 response is the full maintenance window, including its initial update:
{
"id": "c4e1...",
"kind": "maintenance",
"title": "Database upgrade",
"status": "scheduled",
"severity": null,
"created_by": null,
"created_at": "2026-09-05T12:00:00Z",
"resolved_at": null,
"scheduled_start": "2026-09-10T02:00:00Z",
"scheduled_end": "2026-09-10T03:00:00Z",
"service_ids": ["5b1e...", "8a2f..."],
"display_id": "MNT-7",
"updated_at": "2026-09-05T12:00:00Z",
"auto_managed": false,
"updates": [
{ "id": "...", "body": "Upgrading the primary database...", "status_at_update": "scheduled", "created_at": "..." }
]
}
created_by is always null for a maintenance window created through the API - there’s no human
session behind an API key, so it’s attributed to the system.
Post an update (including completing it)
POST /v1/maintenance-windows/{id}/updates
{ "body": "Upgrade underway.", "status": "in_progress" }
| Field | Type | Required | Notes |
|---|---|---|---|
body |
string | yes | Non-empty. |
status |
string | yes | One of scheduled, in_progress, completed. |
There’s no separate “complete” endpoint. Completing a maintenance window is posting an update
with "status": "completed" - that’s the one status this API treats as terminal (it sets
resolved_at and stops counting the window as active).
{ "body": "Upgrade complete, all services back to normal.", "status": "completed" }
Listing and reading
GET /v1/maintenance-windows?section=active
GET /v1/maintenance-windows/{id}
section is required on the list endpoint: active or resolved (a completed window counts as
resolved). Optional filters: q (searches title and display id, e.g. MNT-7), one or more
service_id params, and page/limit for pagination. There’s no severity filter - maintenance
windows don’t have one.