Maintenance windows and snooze
Planned work makes noise: a deploy restarts services, a database upgrade trips every check. A maintenance window mutes a group while it runs. A snooze mutes one noisy alert for a while.
What a window does
While a group is in a maintenance window:
- Alerts are still stored. You see them in the app, marked in maintenance.
- Nobody is notified: no push, no call, no SMS, no email.
- Escalation waits. Alerts that were already escalating don't move on to the next person.
- When the window ends (on time or ended early), every alert that is still open is notified again, once, and its escalation starts. Alerts you acked or closed in the meantime stay quiet.
Rules:
- A window is at most 7 days long, and can be scheduled up to 90 days ahead.
- A group has at most 20 running or upcoming windows. Windows may overlap; the group stays muted until the last one ends.
- Times in the API are UTC (ISO 8601, like
2026-10-22T06:00:00Z). Any explicit zone is accepted on input (2026-10-22T08:00:00+02:00); a time without a zone is refused. The apps and the admin app show your local time.
Start one with your key
Use the group's send key, the same one you send alerts with. This is what a deploy script or CI job does: it mutes only the group that key belongs to.
# Mute for 30 minutes, starting now
curl -H 'X-ZapaPush-Key: YOUR_KEY' -d minutes=30 https://api.zapapush.com/maintenance
# With a reason (shown in the app and the admin app)
curl -H 'X-ZapaPush-Key: YOUR_KEY' -d minutes=30 -d reason='deploy v2.4' https://api.zapapush.com/maintenance
# Until a time (UTC), as JSON
curl -H 'X-ZapaPush-Key: YOUR_KEY' -H 'Content-Type: application/json' \
-d '{"until": "2026-10-22T06:00:00Z", "reason": "db upgrade"}' \
https://api.zapapush.com/maintenance
# Scheduled: Saturday night, 02:00 to 04:00 UTC
curl -H 'X-ZapaPush-Key: YOUR_KEY' -H 'Content-Type: application/json' \
-d '{"starts_at": "2026-10-24T02:00:00Z", "ends_at": "2026-10-24T04:00:00Z", "reason": "kernel updates"}' \
https://api.zapapush.com/maintenance
Give one of:
| Field | Meaning |
|---|---|
minutes |
Starts now, ends after this many minutes (1 to 10080). |
until |
Starts now, ends at this time. |
starts_at, ends_at |
A scheduled window. A starts_at in the past means now. |
reason is optional (up to 200 characters). The key works the same ways as for sending: the
X-ZapaPush-Key header (best), Authorization: Bearer YOUR_KEY, or Basic auth with the key as the password.
The answer is 201 with the window:
{
"id": 42,
"group_id": 7,
"starts_at": "2026-10-22T05:30:00.000Z",
"ends_at": "2026-10-22T06:00:00.000Z",
"reason": "deploy v2.4",
"active": true,
"created_by": { "key": true },
"created_at": "2026-10-22T05:30:00.120Z",
"ended_at": null
}
List windows
curl -H 'X-ZapaPush-Key: YOUR_KEY' https://api.zapapush.com/maintenance
{
"group_id": 7,
"in_maintenance": true,
"until": "2026-10-22T06:00:00.000Z",
"windows": [ { "id": 42, "active": true, "starts_at": "…", "ends_at": "…", "reason": "deploy v2.4", "…": "…" } ]
}
Only running and upcoming windows are listed, soonest first.
End or cancel early
# End one window now (or cancel it, if it hasn't started)
curl -X DELETE -H 'X-ZapaPush-Key: YOUR_KEY' https://api.zapapush.com/maintenance/42
# End every running window of the group and cancel the upcoming ones
curl -X DELETE -H 'X-ZapaPush-Key: YOUR_KEY' https://api.zapapush.com/maintenance
Ending a running window notifies the alerts still open right away. The answer carries "status": "ended" or
"cancelled"; a window that is already over, or belongs to another group, is 404.
Starting and ending a window each count as one post toward the key's 60 posts per minute.
Wrap a deploy in CI
Start a window before the deploy and end it afterwards, even if the deploy fails. Give the window a safe length (here 30 minutes): if the job dies before its last step, the window still ends on its own.
Store the key as a secret named ZAPAPUSH_KEY in your CI, never in the pipeline file.
GitHub Actions
jobs:
deploy:
runs-on: ubuntu-latest
env:
ZAPAPUSH_KEY: ${{ secrets.ZAPAPUSH_KEY }}
steps:
- uses: actions/checkout@v4
- name: Mute ZapaPUSH alerts
run: curl -fsS -m 10 -H "X-ZapaPush-Key: ${ZAPAPUSH_KEY}" -d minutes=30 -d "reason=deploy ${GITHUB_SHA::7}" https://api.zapapush.com/maintenance
- name: Deploy
run: ./deploy.sh
- name: Unmute ZapaPUSH alerts
if: always()
run: curl -fsS -m 10 -X DELETE -H "X-ZapaPush-Key: ${ZAPAPUSH_KEY}" https://api.zapapush.com/maintenance
GitLab CI
Add ZAPAPUSH_KEY under Settings → CI/CD → Variables (masked).
deploy:
stage: deploy
before_script:
- curl -fsS -m 10 -H "X-ZapaPush-Key: ${ZAPAPUSH_KEY}" -d minutes=30 -d "reason=deploy ${CI_COMMIT_SHORT_SHA}" https://api.zapapush.com/maintenance
script:
- ./deploy.sh
after_script:
# Runs even when the script fails.
- curl -fsS -m 10 -X DELETE -H "X-ZapaPush-Key: ${ZAPAPUSH_KEY}" https://api.zapapush.com/maintenance
Gitea Actions
Same syntax as GitHub Actions; add ZAPAPUSH_KEY under the repository's Settings → Actions → Secrets.
jobs:
deploy:
runs-on: ubuntu-latest
env:
ZAPAPUSH_KEY: ${{ secrets.ZAPAPUSH_KEY }}
steps:
- uses: actions/checkout@v4
- run: curl -fsS -m 10 -H "X-ZapaPush-Key: ${ZAPAPUSH_KEY}" -d minutes=30 -d "reason=deploy ${GITHUB_SHA::7}" https://api.zapapush.com/maintenance
- run: ./deploy.sh
- if: always()
run: curl -fsS -m 10 -X DELETE -H "X-ZapaPush-Key: ${ZAPAPUSH_KEY}" https://api.zapapush.com/maintenance
Any shell script
#!/bin/sh
set -e
curl -fsS -m 10 -H "X-ZapaPush-Key: ${ZAPAPUSH_KEY}" -d minutes=30 -d reason=deploy https://api.zapapush.com/maintenance >/dev/null
trap 'curl -fsS -m 10 -X DELETE -H "X-ZapaPush-Key: ${ZAPAPUSH_KEY}" https://api.zapapush.com/maintenance >/dev/null' EXIT
./deploy.sh
In the admin app
Open Groups at app.zapapush.com and open a group. Under Maintenance, group admins and the company's owner can start a window for 30 minutes, 1 hour or 2 hours, until a time, or schedule one, and end or cancel any window. Every member sees the windows, and the group shows a maintenance badge while one runs. Times are in your local time (or UTC, if you chose it under Account).
With a session (apps and admin app)
The apps and the admin app use the same API with your sign-in instead of a key. Any member of a group lists its windows; group admins and the company's owner start and end them.
curl -H "Authorization: Bearer $TOKEN" https://api.zapapush.com/v1/groups/7/maintenance
curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"minutes": 60, "reason": "deploy"}' \
https://api.zapapush.com/v1/groups/7/maintenance
curl -X DELETE -H "Authorization: Bearer $TOKEN" https://api.zapapush.com/v1/groups/7/maintenance/42
# The company's owner: the same window for every group of the company
curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"minutes": 60}' \
https://api.zapapush.com/v1/companies/3/maintenance
GET /v1/groups also shows maintenance_until for each group (null when it isn't in maintenance).
Snooze an alert
A snooze mutes one alert: repeats update it quietly and its escalation waits. If it is still open when the snooze ends, you are notified again, once. Any member of the alert's group can snooze it, for 15 minutes, 1 hour, 4 hours or 1 day.
# Snooze alert 1234 for an hour
curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"minutes": 60}' \
https://api.zapapush.com/v1/alerts/1234/snooze
# Un-snooze it: escalation carries on
curl -X DELETE -H "Authorization: Bearer $TOKEN" https://api.zapapush.com/v1/alerts/1234/snooze
minutes is one of 15, 60, 240, 1440. A closed alert can't be snoozed (409). Alerts in the API carry
snoozed_until (UTC, or null) and in_maintenance (true while waiting for its group's window to end), and the
alert's timeline (GET /v1/alerts/{id}/timeline) shows maintenance, snooze, unsnooze and renotify
events.
Coming from OpsGenie or PagerDuty
| You used | In ZapaPUSH |
|---|---|
OpsGenie maintenance policy (POST /v1/maintenance, time.type: for-1-hour or schedule) |
POST /maintenance with the group's key: minutes=60, or starts_at/ends_at. |
| OpsGenie Cancel / Change end date of a maintenance | DELETE /maintenance/{id}, then start a new one with the new end. |
OpsGenie alert snooze (POST /v2/alerts/{id}/snooze with endTime) |
POST /v1/alerts/{id}/snooze with minutes. |
PagerDuty maintenance window (POST /maintenance_windows, services: [...]) |
One window per ZapaPUSH group (a group plays the part of a PagerDuty service), or POST /v1/companies/{id}/maintenance for every group. |
PagerDuty incident snooze (POST /incidents/{id}/snooze, duration in seconds) |
POST /v1/alerts/{id}/snooze with minutes (15, 60, 240 or 1440). |
| PagerDuty: events during maintenance are dropped | ZapaPUSH stores them (marked in maintenance) and notifies the open ones once when the window ends, so nothing slips through. |
The OpsGenie and PagerDuty compatible endpoints (/v2/alerts, /v2/enqueue) follow the windows too: alerts
they create during a window are stored, not sent.