> ## Documentation Index
> Fetch the complete documentation index at: https://docs.happyuptime.com/llms.txt
> Use this file to discover all available pages before exploring further.

# On-Call & Schedules

Multi-layer rotations with restrictions, fixed shifts, overrides, and escalation policies — the resolution stack for "who is on call right now".

# On-Call & Schedules

A **schedule** is a stack of **layers**. A layer is a rotation of users with optional time restrictions. At any moment, the schedule resolves to a *primary* (the first layer whose restriction is active right now) plus any *secondary*, *tertiary*, etc. that also happen to be active.

This mirrors PagerDuty's layered schedules. Add BetterStack's idea of pinning a specific user to a specific time range — a **fixed shift** — and you have full coverage for any rotation pattern people actually use.

## The resolution stack

For each layer at time T, Happy Uptime walks this priority list:

**Override (highest)**

Active if `now ∈ [start_at, end_at)`. Scope: schedule-wide (covers every layer) or layer-specific. Used when someone needs to cover for someone else for a defined window — vacation, conference, sick day.
  **Fixed shift**

A specific user pinned to a specific (`layer_id`, `start_at`, `end_at`) tuple. Wins over rotation but loses to overrides. Used for one-off coverage that doesn't fit the rotation pattern (weekend swap, holiday).
  **Rotation**

The default. `shiftIdx = floor((now − layerEpoch) / rotationLength)`, then `members[shiftIdx % members.length]`. The epoch is the most recent handoff day + hour (in the layer's timezone) at-or-before the layer's `created_at`, so handoffs always land on Monday 9am (or whenever you configured).
  **Restriction gate**

If the layer's restriction (none / weekday-business-hours / weekends-only / custom) does NOT include `now`, the layer is **off-duty** for this moment. Resolution falls through to the next layer.

The schedule's **primary** is the first layer whose user is non-null AND whose restriction is active at the current moment.

## Common patterns

**Single rotation (everyone takes a week)**

One layer. Weekly rotation. No restriction. 5 members. Each gets every 5th week.

  **Business hours + after hours**

Two layers:
    - **Primary** — Mon–Fri 9am–5pm local. Senior engineers.
    - **Secondary** — always on. Junior engineers (or external paging service).

    During business hours: primary is active, secondary is also active but ignored.
    Nights/weekends: primary is off-duty, secondary takes over.

  **Follow-the-sun**

Three layers, each with weekday-hours restriction in a different timezone:
    - **Asia primary** — Mon–Fri 9am–6pm Asia/Tokyo
    - **Europe primary** — Mon–Fri 9am–6pm Europe/London
    - **Americas primary** — Mon–Fri 9am–6pm America/New_York

    At any given moment, exactly one layer is active. Coverage rolls around the globe.

  **Primary + escalation backup**

One rotation layer + an escalation policy:
    - Layer 0: Engineering team rotates weekly.
    - Escalation level 1 (after 5 min unack): page CTO directly.
    - Escalation level 2 (after 15 min unack): post to #incident channel.

    The escalation policy is independent of layers — it fires only on unacked incidents.

## What each thing controls

| Concept | Controls | Storage |
|---|---|---|
| **Schedule** | Name, organization, default Slack routing, ICS token | `oncall_schedules` |
| **Layer** | Rotation cadence, handoff time, timezone, restriction, ordered members | `oncall_layers` + `oncall_members` |
| **Override** | One person covering for the rotation in a time window | `oncall_overrides` |
| **Fixed shift** | One person pinned to a specific layer for a specific window | `oncall_fixed_shifts` |
| **Escalation policy** | What happens N minutes after an incident is unacked | `oncall_escalation_policies` |

## Restrictions in detail

A layer's `restriction_type` is one of:

| Type | Active when |
|---|---|
| `none` | Always (24/7) |
| `weekday_hours` | Mon–Fri, hours `[start, end)` in layer's timezone |
| `weekends` | Sat + Sun, all hours |
| `custom` | A subset of weekdays you pick + optional hour window. Hour windows can wrap midnight (e.g. `22:00 → 06:00` covers nights). |

Restrictions are evaluated in the layer's timezone, so DST transitions resolve cleanly.

## Quick links

**[Build your first rotation](/guides/first-rotation)**

Step-by-step: 5 members, weekly handoff Monday 9am.
  **[Multi-layer schedules](/guides/multi-layer-schedules)**

Primary + secondary, follow-the-sun, business-hours-only.
  **[Restrictions](/guides/restrictions)**

"Layer X is only active Mon–Fri 9am–5pm".
  **[Overrides + fixed shifts](/guides/overrides-and-shifts)**

Cover for someone, swap a weekend.
  **[Escalation policies](/guides/escalation-policies)**

"If unacked in 5 min, page the CTO".
  **[Slack paging](/guides/slack-paging)**

Make alerts @-mention the on-call person + ack/resolve from Slack buttons.

## API

Full CRUD lives under `/oncall/schedules/:id` — see [schedules](/api/oncall/schedules), [layers](/api/oncall/layers), [overrides](/api/oncall/overrides), [shifts](/api/oncall/shifts), [escalation](/api/oncall/escalation), [timeline](/api/oncall/timeline), and [iCalendar export](/api/oncall/calendar).