Schedules Overview
Schedules define who is on call and when. Each schedule becomes active at startAt, then hands off between team members on a daily or weekly cadence using handoffDay and handoffTime. If you omit startAt when creating a schedule, beeps starts it immediately.
Schedule Types
Section titled “Schedule Types”Daily Handoffs
Section titled “Daily Handoffs”Hands off every day at a specified time:
const schedule = await client.schedule.create({ name: "Daily On-Call", relayId: "rly_abc123", type: "daily", handoffDay: "monday", // Not used for daily schedules handoffTime: "09:00", // Hands off at 9:00 AM every day externalKey: "daily-oncall", // Idempotent - safe to re-run});Weekly Handoffs
Section titled “Weekly Handoffs”Hands off every week on a specified day and time:
const schedule = await client.schedule.create({ name: "Weekly On-Call", relayId: "rly_abc123", type: "weekly", handoffDay: "monday", // Hands off every Monday handoffTime: "09:00", // at 9:00 AM externalKey: "weekly-oncall", // Idempotent - safe to re-run});Schedule Members
Section titled “Schedule Members”Add team members to the schedule to take on-call shifts:
await client.schedule.addMember(schedule.id, { userId: "usr_alice",});
await client.schedule.addMember(schedule.id, { userId: "usr_bob",});Members hand off in the order they were added.
New members usually join at the next handoff boundary so the current on-call assignment is not interrupted. If the schedule has not started yet, they become active at startAt. If the schedule is active but currently has no active members, they become active immediately.
On-Call Status
Section titled “On-Call Status”Check who is currently on-call:
const onCall = await client.schedule.getOnCall(schedule.id);console.log(`${onCall.email} is on-call`);Assignments
Section titled “Assignments”View upcoming or past assignments to understand the handoff order:
// Get the next 14 days of assignmentsconst assignments = await client.schedule.getAssignments(schedule.id, { type: "days", count: 14,});
assignments.forEach((assignment) => { console.log( `Assignment ${assignment.assignmentNumber}: ${assignment.userId} from ${assignment.startDate} to ${assignment.endDate}`, );});assignmentNumber is the generated assignment index for the schedule timeline. It is not a permanent member position.
Schedule Start
Section titled “Schedule Start”Set startAt when you want a schedule to begin in the future:
const schedule = await client.schedule.create({ name: "Planned Cutover", relayId: "rly_abc123", type: "weekly", handoffDay: "monday", handoffTime: "09:00", startAt: "2025-03-15T09:00:00.000Z",});If you omit startAt, the server defaults it to the current time.
Schedule Overrides
Section titled “Schedule Overrides”Overrides let you temporarily replace the on-call user without changing the schedule itself. They’re useful for vacation coverage, sick days, or mid-shift handoffs.
An override applies to a specific time window (up to 30 days) and expires automatically. While active, getOnCall returns the override user instead of the regularly scheduled person. Overrides cannot overlap on the same schedule.
const override = await client.schedule.createOverride(schedule.id, { userId: "usr_bob", startAt: "2025-03-15T09:00:00.000Z", endAt: "2025-03-22T09:00:00.000Z", reason: "Covering for Alice — vacation",});See Schedule Overrides for the full management guide.
Common Use Cases
Section titled “Common Use Cases”Basic On-Call Schedule
Section titled “Basic On-Call Schedule”Create a simple weekly schedule:
const relay = await client.relay.create({ name: "Production Alerts",});
const schedule = await client.schedule.create({ name: "Weekly On-Call", relayId: relay.id, type: "weekly", handoffDay: "monday", handoffTime: "09:00",});
await client.schedule.addMember(schedule.id, { userId: "usr_alice" });await client.schedule.addMember(schedule.id, { userId: "usr_bob" });await client.schedule.addMember(schedule.id, { userId: "usr_charlie" });
await client.relay.rules.create(relay.id, { name: "Notify on-call engineer", ruleType: "schedule_notify", config: { scheduleId: schedule.id, },});Multi-Tier On-Call
Section titled “Multi-Tier On-Call”Create primary and backup schedules:
const primarySchedule = await client.schedule.create({ name: "Primary On-Call", relayId: relay.id, type: "weekly", handoffDay: "monday", handoffTime: "09:00",});
const backupSchedule = await client.schedule.create({ name: "Backup On-Call", relayId: relay.id, type: "weekly", handoffDay: "monday", handoffTime: "09:00",});
await client.relay.rules.create(relay.id, { name: "Notify primary first", order: 1, ruleType: "schedule_notify", config: { scheduleId: primarySchedule.id },});
await client.relay.rules.create(relay.id, { name: "Escalate to backup", order: 2, ruleType: "schedule_notify", config: { scheduleId: backupSchedule.id },});Follow-the-Sun Coverage
Section titled “Follow-the-Sun Coverage”Create different schedules for different time zones:
const usSchedule = await client.schedule.create({ name: "US Business Hours", relayId: relay.id, type: "daily", handoffDay: "monday", handoffTime: "08:00", // 8 AM US time});
const euSchedule = await client.schedule.create({ name: "EU Business Hours", relayId: relay.id, type: "daily", handoffDay: "monday", handoffTime: "08:00", // 8 AM EU time});