Skip to content

Managing Schedules

Create schedules and add team members to on-call rotations.

import { BeepsClient } from "@beepsdev/sdk";
const client = new BeepsClient({
apiKey: process.env.BEEPS_API_KEY,
});
const schedule = await client.schedule.create({
name: "Weekly On-Call",
relayId: "rly_abc123", // Schedule must belong to a relay
type: "weekly", // or "daily"
handoffDay: "monday", // Day of week for weekly schedules
handoffTime: "09:00", // Time when rotation occurs (HH:MM format)
// startAt: "2025-03-15T09:00:00.000Z", // Optional: omit to start immediately
externalKey: "prod-oncall", // Optional: custom identifier
});
console.log(`Created schedule: ${schedule.id}`);
const dailySchedule = await client.schedule.create({
name: "Daily On-Call",
relayId: "rly_abc123",
type: "daily",
handoffDay: "monday", // Not used for daily schedules
handoffTime: "09:00", // Rotates every day at this time
});
const weeklySchedule = await client.schedule.create({
name: "Weekly On-Call",
relayId: "rly_abc123",
type: "weekly",
handoffDay: "friday", // Rotates every Friday
handoffTime: "17:00", // at 5:00 PM
});

If you omit startAt, beeps starts the schedule immediately. Set it explicitly when you want the schedule to begin in the future.

const schedules = await client.schedule.list();
console.log(`Found ${schedules.length} schedules:`);
schedules.forEach((schedule) => {
console.log(`- ${schedule.name} (${schedule.type})`);
});

Members are added to the rotation in the order they’re 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. You can add members by email address or user ID.

// Add by email address
const member1 = await client.schedule.addMember(schedule.id, {
email: "alice@example.com",
});
console.log(`Added ${member1.userId} to schedule`);
const member2 = await client.schedule.addMember(schedule.id, {
email: "bob@example.com",
});
console.log(`Added ${member2.userId} to schedule`);
// Or add by user ID
const member3 = await client.schedule.addMember(schedule.id, {
userId: "usr_charlie",
});
const onCall = await client.schedule.getOnCall(schedule.id);
console.log(`Current on-call: ${onCall.email}`);
console.log(`User ID: ${onCall.userId}`);
console.log(`Assignment #: ${onCall.assignmentNumber}`);
const result = await client.schedule.getOnCallSafe(schedule.id);
if (result.error) {
console.error("Failed to get on-call:", result.error.message);
} else {
console.log(`On-call: ${result.data.email}`);
}
type CreateScheduleInput = {
name: string; // Schedule name
relayId: string; // Parent relay ID
type: "daily" | "weekly"; // Rotation frequency
handoffDay:
| "monday"
| "tuesday"
| "wednesday"
| "thursday"
| "friday"
| "saturday"
| "sunday"; // Day of week (used for weekly schedules)
handoffTime: string; // Time of day in HH:MM format (24-hour)
startAt?: string | Date; // Optional: omit to start immediately
externalKey?: string; // Optional custom identifier
};
type Schedule = {
id: string;
organizationId: string;
relayId: string;
name: string;
type: "daily" | "weekly";
handoffDay:
| "monday"
| "tuesday"
| "wednesday"
| "thursday"
| "friday"
| "saturday"
| "sunday";
handoffTime: string;
startAt: string;
externalKey: string | null;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
};
type AddScheduleMemberInput =
| { userId: string } // Add by user ID
| { email: string }; // Add by email address
type ScheduleMember = {
id?: string;
scheduleId: string;
userId: string;
createdAt?: string;
effectiveAt?: string;
removedAt?: string | null;
};
type BeepsUser = {
userId: string; // User ID of the person currently on-call
email: string; // Email address
assignmentNumber: number | null; // Current generated assignment index (null when schedule has no members)
scheduleId: string; // Schedule ID
isOverride?: boolean; // True if user is on-call via schedule override
overrideId?: string; // ID of the schedule override (if applicable)
};

Always set externalKey for idempotent configuration. All times are in UTC. See Best Practices for details.

const relay = await client.relay.create({
name: "Backend Team Alerts",
});
const schedule = await client.schedule.create({
name: "Backend Weekly Rotation",
relayId: relay.id,
type: "weekly",
handoffDay: "monday",
handoffTime: "09:00",
});
const teamEmails = ["alice@example.com", "bob@example.com", "charlie@example.com"];
for (const email of teamEmails) {
await client.schedule.addMember(schedule.id, { email });
}
await client.relay.rules.create(relay.id, {
name: "Notify on-call engineer",
ruleType: "schedule_notify",
config: {
scheduleId: schedule.id,
},
});
const onCall = await client.schedule.getOnCall(schedule.id);
console.log(`${onCall.email} is currently on-call`);