Pingtower Docs
Docs The service Maintenance windows

Maintenance windows

A maintenance window is a mute that comes back on a schedule. Alerts are still recorded, nobody is paged, and the time is left out of uptime.

{
  "name": "Sunday patching",
  "description": "Kernel and package updates on the API hosts.",
  "timezone": "Europe/Oslo",
  "starts_at": "2026-10-04T02:00",
  "duration_minutes": 120,
  "rrule": "FREQ=WEEKLY;BYDAY=SU",
  "exdates": ["2026-10-18T02:00"],
  "scope": { "project": false, "checks": ["api-health"], "sources": ["api"], "rules": [] }
}
FieldMeaning
nameUp to 200 characters.
descriptionUp to 2000. Shown on a status page when the window is.
timezoneRequired. An IANA zone such as Europe/Oslo or UTC. Every time in the window is a wall clock in it.
starts_atLocal time as YYYY-MM-DDTHH:MM. For a recurring window it is the rule’s DTSTART: where the schedule starts and the time of day it repeats at.
ends_atOne-off windows only: local time, after starts_at.
duration_minutesRecurring windows only: how long each occurrence lasts.
rruleAn RFC 5545 recurrence rule, up to 1000 characters. Omit it for a one-off window.
exdatesRecurring windows only: up to 200 occurrences to skip, each written as its local start time.
scopeThe whole project, or the checks, sources and rules it covers. See What a window covers.
status_page, componentsShow the window on status pages. Off by default.

An occurrence lasts at most 90 days. A window must have an occurrence that ends after now, so one written entirely in the past is refused.

POST GET /v1/projects/{id}/maintenance-windows, then GET PUT DELETE /v1/projects/{id}/maintenance-windows/{wid}. In the web app it is Alerts → Maintenance; in the iOS app, a project’s Maintenance screen.

One-off or recurring

A one-off window has starts_at and ends_at and nothing else. Sending duration_minutes or exdates with it is a 400 naming the field.

{ "name": "DB migration", "timezone": "UTC", "starts_at": "2026-10-07T22:00", "ends_at": "2026-10-08T01:30",
  "scope": { "project": true } }

A recurring window has starts_at, duration_minutes and an rrule. The rule is the RRULE value, with or without its RRULE: prefix:

  • FREQ may be HOURLY or coarser. SECONDLY and MINUTELY are refused.
  • DTSTART and TZID are refused inside the rule: starts_at and timezone give them.
  • COUNT or UNTIL ends it, not both. UNTIL is a UTC instant (20261231T230000Z), a local date-time (20261231T230000), or a local date (20261231, which includes that whole day).

Times are wall clocks, so 02:00 on Sundays stays 02:00 local on both sides of a daylight-saving change. A start the zone skips moves forward by the gap (02:30 on a spring-forward day becomes 03:30), and one the zone repeats takes its first instance. The length is elapsed time: a two-hour window across a fall-back hour still ends two real hours after it starts.

An entry in exdates must be one of the window’s occurrences, by its local start time, or the save is refused; an exdate that silently excluded nothing would be worse.

Preview before saving

POST /v1/projects/{id}/maintenance-windows/preview?count=5 takes a window body, validates it exactly as a create would, saves nothing, and returns the next occurrences, the one in progress first. Bad input gets the same field errors a create would.

{
  "occurrences": [
    { "start": 1791072000, "end": 1791079200 },
    { "start": 1791676800, "end": 1791684000 },
    { "start": 1792886400, "end": 1792893600 },
    { "start": 1793494800, "end": 1793502000 },
    { "start": 1794099600, "end": 1794106800 }
  ]
}

Unix seconds, [start, end). count is 1 to 100, default 10. 18 October is missing because exdates skips it, and the occurrences from 1 November are an hour later in UTC because Oslo has left summer time. This is what the app’s editor calls as you type, so no client evaluates an RRULE itself. GET …/maintenance-windows/{wid}/occurrences?count= answers the same for a saved window.

Every read of a window carries active, the occurrence in progress as current (null when none), and up to five next_occurrences. The list puts windows in progress first, then upcoming ones by next start, then those that will not recur.

What a window covers

scope is {"project": true} for everything in the project, or any mix of check, source and rule names, up to 64 of each. Names are checked on save. Deleting a check, source or rule removes it from the windows that name it.

Coverage is decided per rule. A window covers a rule when it:

  • covers the whole project,
  • names the rule,
  • names the check whose alert_if wrote the rule, or
  • names a source the rule lists in match.sources or in its source_silent trigger.

A covered rule is held whole. A window naming one of a rule’s sources also holds that rule’s alerts from its other sources. A rule that matches every source is not covered by a window naming one source; name the rule or the project instead.

While a window is in progress

What it covers is recorded but pages nobody, the same as a muted rule:

  • Alerts still open, count and show in every feed and the history.
  • No push, no integration delivery, and no runbook action runs for them.
  • The escalation ladder does not climb.

When the window ends, or is deleted mid-occurrence, the alerts it held are released at the next tick. One still firing and unacknowledged pages as it would have: its alert.opened goes out then, and because a ladder climbs by the alert’s age, it pages the rung its age has reached. One acknowledged or resolved in the meantime pages nobody. An alert that had already paged before the window started keeps its ack and resolve notices, so a channel that heard it open hears it close.

Uptime

Maintenance time does not count against uptime.

  • A check run taken while a window covers the project, the check, the source that ran it or the check’s rule is left out of the check’s success rate and its uptime report: neither up nor down.
  • For a status-page component, a window that is not shown on a status page leaves the time it covered out of the component’s uptime entirely, outages included, for each component whose every binding it covers. A shown window works differently; see below.

Editing a window applies from now on. The time it already covered stays covered with the scope it had then, so uptime already measured does not move. Deleting it keeps the time it covered excluded as well.

On a status page

A window appears on a status page only if you opt in:

{ "status_page": true, "components": ["…", "…"] }

components are component ids from any of the team’s status pages, up to 50, and at least one when status_page is true. A component must be bound to a check or rule in the window’s own project; one with no bindings can be picked only by a member not limited to projects. Any other is a 400 component <id> is not bound to project <P>. Turning status_page off keeps the picks, so turning it back on restores them.

The page lists maintenance in progress, upcoming maintenance starting within the next seven days, and a 90-day maintenance history, each with the window’s name and description. While an occurrence runs, each picked component shows Under maintenance in blue, and a day spent entirely in maintenance draws a blue bar.

The worst state still wins. Picking components decides only where the window shows; it does not hide anything. An outage from a rule or check outside the window’s scope, or one posted by hand, shows over the maintenance and counts against uptime. Time whose worst state is maintenance is left out of uptime. Alerts the window’s scope does hold get no outage on the page while it runs; one still firing when it ends shows, and counts, from that moment. That happens whether or not the window is shown.

Who can, and how many

Viewers and up can read windows and preview them. Members and up, or any custom role with maintenance:write, can create, edit and delete them. Every write goes to the audit log and is announced to the project’s webhooks and chat integrations as maintenance.scheduled, maintenance.changed or maintenance.cancelled, naming the window, its schedule and who did it, so a window nobody mentioned cannot quietly hold pages back. See Maintenance notices.

A project holds at most 100 windows that are still to come or in progress. One whose every occurrence has ended does not count toward the cap, so a one-off window per deploy is never refused for being the hundred-and-first. A window that has ended and will not recur is removed 95 days after its last occurrence. Past the cap the create is a 429 naming max_maintenance_windows_per_project; see limits.

An AI assistant granted maintenance:write can schedule a one-off window that ends within 24 hours from now. It cannot schedule recurring windows, edit or delete them, or put them on a status page.

Window or mute?

A rule mute silences one rule from now until a time you pick, up to 90 days, and tells the project’s channels it happened. Use it for the noise you are looking at right now. Use a window for work you can put on a calendar: it starts and stops on its own, it repeats, it can cover a whole project or every rule behind a check or source, it keeps the time out of uptime, and it can tell your customers on the status page. Both release what they held in the same way when they end.

Last updated 3 Oct 2026 Report a problem with this page