Skip to content

On-call schedules

An on-call schedule is a named rotation of users that, given any timestamp, returns the user on call at that moment. Escalation policies reference schedules instead of hard-coded user lists, so when the rotation hands off, paging follows automatically.

Rotations of more than one person need a responder pack. See plans and limits.

  • You want pages to follow a rotation rather than always hitting the same person.
  • You want handoffs at a fixed local time, kept steady across DST changes.
  • You want to handle holiday cover and one-off swaps cleanly.

If you only have one engineer, you don’t need a schedule: point your escalation policy at that user directly.

How a schedule produces “who’s on call”

Section titled “How a schedule produces “who’s on call””

Each schedule has:

  • A name (Primary on-call, Eng manager backup).
  • A timezone, an IANA name (America/Los_Angeles, Europe/Berlin, Asia/Singapore, UTC). Handoff times, handoff days and coverage windows are local times in this zone, so a 09:00 handoff stays at 09:00 local across DST changes.
  • One or more layers, stacked in priority order. A layer can carry its own timezone, for follow-the-sun.
  • Zero or more overrides: hard “user X is on from time A to time B” entries.

When asked who’s on call at time T:

  1. Apply overrides first. If any override covers T, that user is on call. Done.
  2. Otherwise walk layers from the highest layer_number downward. A layer answers when T is inside its effective_from/effective_until window and inside one of its coverage windows (a layer without windows covers around the clock). The first layer that answers wins; whose turn it is follows from the rotation.

This means higher layers shadow lower ones while they cover. Give a higher layer coverage windows (restrictions) to make it a night, weekend or regional shift; lower layers answer the rest of the time.

rotation_typeBehavior
dailyHand off every day at handoff_time.
weeklyHand off once a week at handoff_time, on handoff_day (0=Sunday .. 6=Saturday) when one is set.
customHand off every rotation_interval_days days at handoff_time.

The rotation walks the ordered participants list and wraps around.

restrictions limits a layer to recurring windows in its zone:

  • Daily: { "type": "daily", "start_time": "22:00", "duration_minutes": 600 } covers 22:00 to 08:00 every night. A window can run past midnight.
  • Weekly: { "type": "weekly", "start_day_of_week": 5, "start_time": "18:00", "duration_minutes": 3840 } covers Friday 18:00 to Monday 10:00 every week.

Windows keep their local hours across DST changes, so the night window above is 22:00 to 08:00 local on the night the clocks change too. Rotation handoffs happen on schedule whether or not the layer covers at that moment.

An override beats every layer. Use them for:

  • Holiday cover: “Bob is covering for Alice from Dec 22 to Jan 2.”
  • Swap weekends: “I’ll take next weekend if you take this one.”
  • Filling gaps: if the layers leave a hole, an override patches it.

Overrides are time-bounded with start_at and end_at. When overrides overlap, the most recently created one wins.

  • Dashboard: the on-call page shows who is on call now on every schedule. A schedule’s page shows who is on call, until when, who is next, and every shift of the next two weeks, gaps included.
  • API: GET /on_call_schedule/{id}/on_call answers for now (or any at), GET /on_call_schedule/{id}/shifts returns the timeline over a range, and GET /on_call lists who is on call across your schedules, or one person’s shifts with user_id. See the reference.
  • Calendar: subscribe to your own shifts, or a whole schedule, in Google Calendar, Outlook or Apple Calendar. See In a calendar.
  • CLI: siteqwality oncall now in @siteqwality/cli 0.1.3 or later. See From the CLI.
  • Assistants and Slack: the MCP tools, and /oncall in a connected Slack workspace, give the same answer. See From Slack.
  • Notices: whoever goes on call can be told through their low-urgency notification rules. See Handoff notices.

Escalation policies use the same answer when they page a schedule.

Edit a layer to change its people, order, handoff time or windows. Whoever is on call stays on until the next handoff, past shifts stay as they were, and the layer keeps its id. The on-call report shows who carried how much on-call time, including nights and weekends.

One layer, rotation_type: weekly, handoff_day: 1 (Monday), handoff_time: 09:00, participants: [alice, bob, carol]. Each engineer is on for one week starting Monday morning.

Business hours with an after-hours rotation

Section titled “Business hours with an after-hours rotation”

Layer 1: a weekly rotation of the whole team, no windows. Layer 2: a daily rotation of the day crew with restrictions: [{ "type": "weekly", "start_day_of_week": 1, "start_time": "09:00", "duration_minutes": 480 }, ...], one window per weekday. The day crew answers 09:00 to 17:00 on weekdays; layer 1 answers nights and weekends.

A higher layer with one weekly window, start_day_of_week: 5, start_time: "18:00", duration_minutes: 3840 (Friday 18:00 to Monday 10:00), and the weekend volunteers as participants.

Three layers, each with its own timezone and one daily 8-hour window in local time: an APAC layer in Australia/Sydney from 09:00, an EMEA layer in Europe/London from 09:00, and an Americas layer in America/New_York from 09:00. Add a bottom layer without windows to catch any hour the three leave uncovered.

Two separate schedules: Primary and Secondary. Escalation policy: level 1 → Primary, level 2 → Secondary. Each schedule rotates independently.