Uptimeify Docs
Incident management

Update a schedule

Change an on-call schedule's name, timezone, validity range, weekly windows, rotation cadence and rotation membership — in one atomic call.

PATCH /api/im/schedules/:id

This is where a schedule's rotation actually gets configured. Creating a schedule gives you an empty shell (rotationMode: "none", no members); this endpoint sets the cadence, the weekly windows and who is in the rotation.

Every field is optional. Only the fields present in the body are touched — a request body of { "name": "Primary" } renames the schedule and changes nothing else.

Authentication

Requires the base IM access every endpoint in this API needs, plus 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 is treated as an organization admin.

Incident Management is currently restricted to platform administrators. While that restriction is in place, organization roles and organization-wide API tokens receive 403 imAccessDenied on every /api/im/** endpoint, including this one.

Request body

FieldTypeDescription
namestringSchedule name, up to 120 characters.
timezonestringIANA timezone (e.g. Europe/Berlin). Every handover boundary and weekly window is evaluated in this zone, across DST.
effectiveFromstring | nullISO timestamp. The schedule pages nobody before it. null = no start bound.
effectiveUntilstring | nullISO timestamp. The schedule pages nobody after it. null = no end bound.
weeklySchedulesarrayRecurring on-call windows, up to 50. Each: { from: "HH:mm", until: "HH:mm", days: number[] }, days as ISO weekdays (1 = Monday .. 7 = Sunday). until may be "24:00" for end-of-day, and an until at or before from wraps past midnight. An empty array means 24/7.
rotationsarrayFull replacement of the rotation groups, up to 50. Each: { members: string[] }, up to 200 members. Array order is the rotation order; member order within a group is preserved. Every member must already be a member of the schedule's team. Omit the field to leave rotations untouched — sending [] removes them all.

Plus the cadence fields — rotationMode, rotationRepeats, customRepeatUnit, customRepeatValue, autoRotationSize, roundRobinSize, startsOnTime, startsOnDayOfWeek, startsOnDateOfMonth. They are documented once, with their allowed values, under Rotation cadence.

teamId and tierId are not patchable. Moving a schedule between teams or tiers would silently re-point who it pages, so it is not a partial update.

How the cadence is validated

Cadence fields are merged onto the schedule's current row and then validated as a whole, not field by field. That is what lets you patch a single field — say roundRobinSize — without re-sending the rest of the cadence, while still rejecting a combination that cannot work (for example rotationRepeats: "weekly" with no startsOnDayOfWeek anywhere in the effective config).

Validation happens before anything is written. A cadence the materializer would choke on never reaches the database.

When shifts are recomputed

Changing the timezone, the validity range, the weekly windows, any cadence field, or the rotations re-materializes the schedule's shifts. A bare rename does not — it would take the schedule's lock and rewrite rows for nothing.

Example (cURL)

curl -X PATCH "$BASE_URL/api/im/schedules/7" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "rotationMode": "explicit",
    "rotationRepeats": "custom",
    "customRepeatUnit": "minutes",
    "customRepeatValue": 30,
    "startsOnTime": "09:00",
    "rotations": [
      { "members": ["u_abc123"] },
      { "members": ["u_def456"] }
    ]
  }'

Response

200 OK: the schedule's configuration after the update.

{
  "id": 7,
  "displayName": "Primary On-Call",
  "timezone": "Europe/Berlin",
  "effectiveFrom": "2026-08-01T00:00:00.000Z",
  "effectiveUntil": null,
  "weeklySchedules": [],
  "rotationMode": "explicit",
  "rotationRepeats": "custom",
  "customRepeatUnit": "minutes",
  "customRepeatValue": 30,
  "autoRotationSize": null,
  "roundRobinSize": 1,
  "startsOnDayOfWeek": null,
  "startsOnDateOfMonth": null,
  "startsOnTime": "09:00",
  "rotations": [
    {
      "id": 12,
      "rotationOrder": 1,
      "members": [{ "userId": "u_abc123", "userName": "Ada Lovelace", "memberOrder": 1 }]
    },
    {
      "id": 13,
      "rotationOrder": 2,
      "members": [{ "userId": "u_def456", "userName": "Bob Fixit", "memberOrder": 1 }]
    }
  ]
}

The rotations key is present only when you sent rotations. A request that did not touch them omits the key entirely rather than re-reading state you did not change; use GET /api/im/teams/:id/escalations if you need the current rotation set.

Common errors

Statusdata.codeCause
400invalidRequestBodyNon-numeric :id, a missing or over-long name, an invalid timezone, or a malformed rotations entry (not an object, members not an array, an empty user id).
403imAccessDeniedNo Incident Management access — see the note under Authentication.
403imTeamWriteDeniedIM access, but neither organization admin nor team admin of this schedule's team.
404imScheduleNotFoundNo such schedule in your organization.
404imTeamMemberNotFoundA user id in rotations is not a member of the schedule's team.
422imInvalidRotationModerotationMode is not none, auto or explicit.
422imInvalidRotationThe cadence does not hold together — an unknown rotationRepeats or customRepeatUnit, a startsOnDateOfMonth outside 1–31, a required field missing for the effective cadence, too many rotations or members, or a duplicate user id inside one group.
422imInvalidNumbercustomRepeatValue, autoRotationSize or roundRobinSize is not an integer ≥ 1.
422imInvalidTimeOfDaystartsOnTime or a weekly window's from/until is not "HH:mm".
422imInvalidDayOfWeekstartsOnDayOfWeek, or a day inside weeklySchedules, is not an ISO weekday 1–7.
422imInvalidTimeFilterweeklySchedules is not an array, has more than 50 entries, or an entry is not an object.

On this page