Blog Incident response

On-call schedules and escalation policies

An alert that emails a shared inbox at 3am is an alert nobody reads. You need one named person to be paged, a second person if the first doesn't answer, and a record of who was reached and when.

This post walks through setting that up in ZipLogger: an on-call team, a rotation, an override for when someone is away, and an escalation policy. The timeline output below comes from running ZipLogger's own incident engine against this exact setup.

The four pieces

Everything lives under Alerts. The On-call tab holds three pages, and Delivery holds the fourth.

PieceWhereWhat it decides
On-call teamAlerts → On-call → TeamsThe people who can be paged
ScheduleAlerts → On-call → SchedulesWhich team member is on call right now
Escalation policyAlerts → On-call → Escalation policiesWho is paged, in what order, and how long to wait
Maintenance windowAlerts → Delivery → Maintenance windowsWhen nobody should be paged at all

"On-call team" is not the same as the workspace Team page. That one manages accounts and roles. An on-call team is a group of those accounts who can be woken up.

1. Create the on-call team

Click + New on-call team. Give it a Name (say, Backend) and an optional Description, then tick the people who belong to it. They must already be users in the workspace, so invite them on the Team page first.

Each member has a role, member or manager, and a can be paged checkbox. Managers matter when a schedule gives no answer: they are the fallback. A member with can be paged switched off stays on the roster but is never paged.

Every team card has a Who is on call? button. Use it after each change. It tells you who would be paged at this moment and why.

2. Add a schedule

Click + New schedule. A schedule belongs to one team. The form asks for:

  • On-call team and Name.
  • Time zone: an IANA zone such as Europe/Berlin or America/New_York. Handoffs follow this zone's wall clock.
  • First shift starts: the date and time the first person in the rotation goes on call. It is entered in your browser's local time.
  • Shift length: 1 to 168 hours, 1 to 31 days, or 1 to 8 weeks.
  • Layer: primary (the default), secondary, backup, up to five. See layers below.
  • Rotation: team members in handoff order, added with + add participant… and reordered with the arrows. Up to 50 people.

Day and week shifts hand off at the same local time, even across a daylight saving change. Hour shifts are fixed lengths of real time. Here is a weekly Backend rotation in Europe/Berlin starting Monday 19 October 2026 at 09:00, printed by ZipLogger's shift calculation:

shift 0: Alice Mon 19 Oct 09:00 local (07:00Z) -> Mon 26 Oct 09:00 local (08:00Z)  length 169h
shift 1: Bob   Mon 26 Oct 09:00 local (08:00Z) -> Mon 02 Nov 09:00 local (08:00Z)  length 168h
shift 2: Alice Mon 02 Nov 09:00 local (08:00Z) -> Mon 09 Nov 09:00 local (08:00Z)  length 168h

Clocks go back on 25 October, so Alice's first shift is 169 hours long and the handover still happens at 09:00 local. The UTC time moves instead.

Primary and secondary layers

One rotation is often not enough. A common setup is a primary who is paged first and a secondary who is paged if the primary does not answer. In ZipLogger each of those is a schedule of the same team with its own Layer. Each layer rotates on its own, with its own people, shift length and time zone.

An escalation step that targets the team can then name a layer: step 1 pages the primary, step 2 the secondary. A step that names no layer pages the first layer that has someone on call, primary first. A team with a single schedule works exactly as before.

3. Cover for someone with an override

Alice is travelling on Wednesday. On the schedule card, click + Override and fill in Who covers, From, Until and an optional Reason. The cover has to be a member of the same team, and one override can last at most 60 days.

What the resolver answers for Wednesday afternoon, before and after the override:

Alice via rotation: Rotation on "Backend rotation", shift until 2026-10-26 08:00:00Z
Carol via override: Override on "Backend rotation" until 2026-10-22 08:00:00Z (Alice is travelling)

When the override ends, the rotation carries on as if nothing happened. If two overrides overlap, the one that starts later wins.

Who gets paged when the schedule has no answer

ZipLogger works down this list and stops at the first answer:

  1. An active override on an enabled schedule of the team.
  2. The rotation participant for this moment.
  3. The team's managers.
  4. Every enabled member of the team.
  5. Nobody. The incident timeline says so, with the reason.

So a team with no schedule still works. It pages its managers, or everyone if it has no managers.

4. Write the escalation policy

Click + New escalation policy. A policy is a list of up to 20 steps. Each step targets an on-call team (the schedule picks the person) or a user directly.

  • Step 1 is paged immediately when the incident opens.
  • Each later step has an after N min delay, from 1 to 1440 minutes. The delay counts from the previous step, not from when the incident opened.
  • A step that targets an on-call team can name a layer, or leave it at first layer on call.

A small policy that covers most teams:

StepWhenTarget
1immediatelyon-call team Backend
2after 10 minuser Carol (the team lead)

Then attach it where incidents come from:

Repeat until someone answers

If nobody acknowledges after the last step, the escalation ends, unless you ask it to repeat. Repeat the whole policy (0 to 9 times, 0 by default) starts again from step 1, a number of minutes after the last step that you choose (1 to 1440, 10 by default). Each round pages its steps again; it is not dropped as a duplicate of the round before. Acknowledging or resolving the incident stops it at once, and the timeline says which round each step belongs to.

What happens when it fires

This is the incident timeline from the run, for an incident opened at 01:12 UTC on a Friday when Alice is on the rotation. Alice has a phone number on her profile and opted into SMS and calls. Carol has not.

01:12:00Z  TRIGGERED          Incident triggered by generic webhook: 5xx rate is 8.4% over 5 minutes
01:12:00Z  ROUTED             Routed to on-call team "Backend" by policy "Backend critical".
01:12:00Z  ESCALATED          Escalation step 1 of 2: on-call team "Backend" → Alice. Next step at 01:22:00 UTC if not acknowledged.
01:12:00Z  NOTIFICATION_SENT  Email notification sent (escalation_step_1) → Alice (a…@acme.test)
01:12:00Z  NOTIFICATION_SENT  SMS notification sent (escalation_step_1) → Alice (+141…100)
01:12:00Z  NOTIFICATION_SENT  Voice call notification sent (escalation_step_1) → Alice (+141…100)
01:22:00Z  ESCALATED          Escalation step 2 of 2: Carol → Carol. This is the last step.
01:22:00Z  NOTIFICATION_SENT  Email notification sent (escalation_step_2) → Carol (c…@acme.test)

This policy has no repeat, so step 2 is the last step. Every page shows up with its channel and a masked address, so "was I actually paged?" has an answer. How the SMS and call are sent is covered in SMS and voice paging.

Acknowledging stops the escalation

Open the incident and click Acknowledge. A second incident from the same run, acknowledged by Alice three minutes in:

01:53:00Z  ESCALATED           Escalation step 1 of 2: on-call team "Backend" → Alice. Next step at 02:03:00 UTC if not acknowledged.
01:56:00Z  ACKNOWLEDGED        Acknowledged by [email protected].
01:56:00Z  ESCALATION_STOPPED  Escalation stopped: acknowledged by [email protected] at step 1.
01:56:00Z  ASSIGNED            Assigned to [email protected] (acknowledged).

Ten minutes later the escalation worker had nothing to do for it. Resolving the incident, or assigning it to someone by hand, stops the escalation the same way. If the source reports recovery (CloudWatch goes back to OK, say), the incident resolves itself and any pending step is cleared.

The escalation state lives in the database, not in memory. Each step is a conditional update, so an acknowledgement that races the next step cannot lead to that step paging anyway.

Your shifts in your calendar

On the Schedules page, Subscribe to my shifts gives you a private calendar address (ICS) with your shifts across every schedule. Subscribe on one schedule gives an address for that schedule's whole rotation. Add it in Google Calendar, Outlook or Apple Calendar as a subscription from a URL.

  • The address is shown once. Calendar apps re-read it, so rotation and override changes show up without exporting again.
  • It covers 14 days back and 90 days ahead.
  • It carries display names, the layer and the schedule, but no email addresses or phone numbers.
  • Anyone with the address can read it. If it leaks, revoke it on the same page. Each person manages only their own, up to 10.

Write the postmortem

Once an incident is resolved, its page has a Postmortem section. It holds:

  • A title, a summary, the impact, and the impact's start and end (the incident's own times by default).
  • The root cause, contributing factors, the resolution and preventive actions.
  • An owner, and up to 50 action items, each with a title, an owner, a due date, open or done, and an optional link to Jira, Linear, GitHub or another tracker.
  • A status: draft, in review or published.

Every save keeps the previous text as a revision, and adds a line to the incident's timeline. If someone else saved while you were editing, your save is refused instead of silently overwriting theirs. Viewers can read a postmortem; Editors can write one. The incident's own timeline sits next to it, so you don't copy it across.

Maintenance windows

For planned work, go to Alerts → Delivery → Maintenance windows and click + New maintenance window. Set Name, Starts, Ends, and optionally narrow it by Service, Environment, Alert rule or Integration. Environment matches the incident's free-text environment label, not the environment selected in the app. A window with every filter empty covers the whole workspace.

Incidents that open during the window are still recorded. Nobody is paged:

02:06:00Z  TRIGGERED   Incident triggered by generic webhook: Timeouts to the payment provider
02:06:00Z  SUPPRESSED  Suppressed by maintenance window "DB upgrade" (until 2026-10-23 03:06:00Z): the incident is recorded, nobody was notified or escalated for it.

A window only affects incidents that open while it is active. An incident that was already open when the window started keeps escalating. To quiet one of those, use Suppress… on the incident itself.

What it does not do

  • No month grid in the app. The schedules page shows who is on call now, who is next, and upcoming overrides. For a calendar view, subscribe to the ICS feed in your calendar app.
  • No restricted hours inside a layer, such as weekdays only, and no follow-the-sun handoffs between zones as one rotation.
  • No postmortem templates.
  • No mobile app or push notifications. Paging is email, plus SMS and voice calls for people who add a phone number on their profile.
  • No shift swaps by the on-call person. Overrides are added by someone with the Editor or Admin role.

Next step

The incident management page describes the rest of the incident lifecycle. To try it, create a free workspace, add an on-call team under Alerts → On-call → Teams, and press Who is on call? before you trust it with a real alert.

Microsoft Teams incident alerts with actions

Get ZipLogger incident cards in a Teams channel or chat, and acknowledge, assign or resolve them from the card. Setup, account linking and limits.

Slack incident alerts you can acknowledge

Post ZipLogger incidents to a Slack channel as one message that updates in place, with Acknowledge, Assign to me and Resolve buttons that act as the person who clicked.

Discord and Telegram incident alerts

Post ZipLogger incidents to a Discord channel or a Telegram chat as one message that is edited as the incident is acknowledged and resolved. Setup, payloads and limits.

Try it on your own logs

There is a free plan and no sales call. Install an SDK, send a few log lines, and search them.