Schedules
List and create Incident Management on-call schedules: the rotation that decides who is on call, and when, for a team.
GET /api/im/schedules · POST /api/im/schedules
A schedule belongs to exactly one team and defines its on-call rotation: one or more layers of users, each rotating on a daily, weekly, or custom cadence, optionally restricted to specific times of day. A background worker materializes the rotation into concrete shifts (im_schedule_shift) roughly 90 days ahead, which is what Who Is On Call and the escalation engine actually read at runtime. This endpoint manages the rotation's definition, not the materialized shifts directly.
Authentication
Requires the base IM access every endpoint in this API needs (an IM-eligible role, or an organization-wide API token; Incident Management must be enabled for the organization). Listing is available to any IM-eligible role. Creating a schedule additionally requires the write bar: your role must be admin, or you must be a team admin of the schedule's team. An organization-wide API token satisfies the organization-admin bar.
List schedules
GET /api/im/schedules
Returns every schedule in your organization, with its team's name.
Example (cURL)
curl -X GET "$BASE_URL/api/im/schedules" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json"Response
200 OK: an array, ordered by name.
[
{
"id": 7,
"organizationId": 1,
"teamId": 3,
"teamName": "Platform Team",
"name": "Primary On-Call",
"timezone": "Europe/Berlin",
"rotation": [
{
"users": ["u_abc123", "u_def456"],
"type": "weekly",
"handoverTime": "09:00",
"startDate": "2026-01-05"
}
],
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
]Create a schedule
POST /api/im/schedules
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
teamId | number | Yes | The team the schedule belongs to. Must belong to your organization. |
name | string | Yes | Schedule name, up to 120 characters. |
timezone | string | Yes | IANA timezone (e.g. Europe/Berlin). Every handover and time-of-day restriction is evaluated in this zone, across DST. |
rotation | array | No | Rotation layers, up to 20. Defaults to an empty array (a schedule with no rotation pages nobody until layers are added). Each layer: { users: string[], type: 'daily' | 'weekly' | 'custom', intervalDays?: number, handoverTime: "HH:mm", startDate: "yyyy-MM-dd", restrictions?: Array<{ dow: number[], from: "HH:mm", to: "HH:mm" }> }. intervalDays is required (and only meaningful) when type is custom. dow in restrictions uses ISO weekdays (1 = Monday .. 7 = Sunday), up to 30 restrictions per layer. Up to 200 users per layer. |
Every user ID in rotation[].users must belong to your organization. A rotation naming a user outside it is rejected outright, not silently dropped, since im_schedule_shift.user_id has no organization boundary of its own.
Example (cURL)
curl -X POST "$BASE_URL/api/im/schedules" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"teamId": 3,
"name": "Primary On-Call",
"timezone": "Europe/Berlin",
"rotation": [
{
"users": ["u_abc123", "u_def456"],
"type": "weekly",
"handoverTime": "09:00",
"startDate": "2026-01-05"
}
]
}'Response
200 OK: the newly created schedule row (same shape as the list above, without teamName).
Common errors
401 Unauthorizedwhen not authenticated403 Forbidden(imAccessDenied) when using a customer-scoped token, or a session without an IM-eligible role403 Forbidden(imNotEnabled) when Incident Management is not enabled for the organization403 Forbidden(imTeamWriteDenied) (create only) when your role is notadminand you are not a team admin ofteamId404 Not Found(imTeamNotFound) (create only) whenteamIddoes not exist, or belongs to another organization400 Bad Request(invalidRequestBody) (create only) whenname,timezone, or a rotation layer field is missing or malformed (see field descriptions above), or a layer/restriction/user-list bound is exceeded