Skip to main content

On-Call Schedules

Schedules define who is on-call and when. Signal uses schedules for alert routing and escalation.

Schedule Types

Weekly Schedule

Fixed days for each team member:
{
  "schedule": {
    "name": "Backend On-Call",
    "type": "weekly",
    "timezone": "America/New_York",
    "layers": [
      {
        "users": ["user_1", "user_2", "user_3"],
        "rotation": "weekly",
        "start_day": "monday",
        "start_time": "09:00"
      }
    ]
  }
}

Rotation Schedule

Round-robin rotation:
{
  "schedule": {
    "name": "Platform On-Call",
    "type": "rotation",
    "timezone": "UTC",
    "layers": [
      {
        "users": ["user_1", "user_2", "user_3", "user_4"],
        "rotation_length": "1w",
        "handoff_time": "09:00",
        "handoff_day": "monday"
      }
    ]
  }
}

Custom Schedule

Define specific time slots:
{
  "schedule": {
    "name": "Weekend Coverage",
    "type": "custom",
    "timezone": "America/Los_Angeles",
    "entries": [
      {
        "user": "user_1",
        "start": "2024-01-13T08:00:00",
        "end": "2024-01-14T20:00:00"
      },
      {
        "user": "user_2",
        "start": "2024-01-14T20:00:00",
        "end": "2024-01-15T08:00:00"
      }
    ]
  }
}

Creating Schedules

POST /api/v1/schedules
{
  "schedule": {
    "name": "API Team On-Call",
    "type": "rotation",
    "timezone": "America/New_York",
    "layers": [
      {
        "name": "Primary",
        "users": ["user_1", "user_2", "user_3"],
        "rotation_length": "1w"
      },
      {
        "name": "Secondary",
        "users": ["user_4", "user_5"],
        "rotation_length": "2w"
      }
    ]
  }
}

Schedule Layers

Schedules can have multiple layers for backup coverage:
┌─────────────────────────────────────┐
│ Primary    │ Alice │ Bob  │ Carol  │
├─────────────────────────────────────┤
│ Secondary  │   Dave    │   Eve    │
└─────────────────────────────────────┘
        Week 1    Week 2    Week 3
Alerts go to Primary first, then Secondary if not acknowledged.

Overrides

Temporary schedule changes:
POST /api/v1/schedules/:id/overrides
{
  "override": {
    "user": "user_backup",
    "start": "2024-01-15T00:00:00Z",
    "end": "2024-01-16T00:00:00Z",
    "reason": "user_1 on PTO"
  }
}

Who’s On-Call

Current On-Call

GET /api/v1/schedules/:id/oncall
{
  "oncall": {
    "primary": {
      "user": "user_1",
      "until": "2024-01-22T09:00:00Z"
    },
    "secondary": {
      "user": "user_4",
      "until": "2024-01-29T09:00:00Z"
    }
  }
}

Future On-Call

GET /api/v1/schedules/:id/oncall?date=2024-02-01

Escalation Policies

Link schedules to escalation policies:
{
  "policy": {
    "name": "Critical Alerts",
    "steps": [
      {
        "delay": "0m",
        "targets": [
          { "type": "schedule", "id": "sched_primary" }
        ]
      },
      {
        "delay": "15m",
        "targets": [
          { "type": "schedule", "id": "sched_secondary" },
          { "type": "channel", "id": "slack-escalation" }
        ]
      },
      {
        "delay": "30m",
        "targets": [
          { "type": "user", "id": "user_manager" }
        ]
      }
    ]
  }
}

Maintenance Windows

Mute alerts during planned maintenance:
POST /api/v1/windows
{
  "window": {
    "name": "Database Migration",
    "start": "2024-01-15T02:00:00Z",
    "end": "2024-01-15T04:00:00Z",
    "rules": ["rule_1", "rule_2"],
    "notify_on_start": true,
    "notify_on_end": true
  }
}
Or mute all alerts matching tags:
{
  "window": {
    "name": "API Deployment",
    "start": "2024-01-15T14:00:00Z",
    "end": "2024-01-15T15:00:00Z",
    "match_tags": {
      "service": "api"
    }
  }
}

Best Practices

Reasonable Shifts

1-week rotations balance coverage and burnout

Use Layers

Primary + secondary ensures coverage

Plan Overrides

Add PTO overrides in advance

Schedule Maintenance

Use windows for planned work