Uptimeify Docs
Incident management

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

FieldTypeRequiredDescription
teamIdnumberYesThe team the schedule belongs to. Must belong to your organization.
namestringYesSchedule name, up to 120 characters.
timezonestringYesIANA timezone (e.g. Europe/Berlin). Every handover and time-of-day restriction is evaluated in this zone, across DST.
rotationarrayNoRotation 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 Unauthorized when not authenticated
  • 403 Forbidden (imAccessDenied) when using a customer-scoped token, or a session without an IM-eligible role
  • 403 Forbidden (imNotEnabled) when Incident Management is not enabled for the organization
  • 403 Forbidden (imTeamWriteDenied) (create only) when your role is not admin and you are not a team admin of teamId
  • 404 Not Found (imTeamNotFound) (create only) when teamId does not exist, or belongs to another organization
  • 400 Bad Request (invalidRequestBody) (create only) when name, timezone, or a rotation layer field is missing or malformed (see field descriptions above), or a layer/restriction/user-list bound is exceeded

On this page