---
title: "Replace a team's schedule"
method: PUT
path: "/api/{org_id}/oncall/teams/{team_id}/schedule"
tags: ["OnCall"]
---

# Replace a team's schedule

`PUT /api/{org_id}/oncall/teams/{team_id}/schedule`

## Request

- Base URL: `https://api.openobserve.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.openobserve.ai/api/{org_id}/oncall/teams/{team_id}/schedule`
- Auth: API key in header `Authorization`
- TypeScript: <https://skmtc.dev/openobserve/apis/openobserve-api-http/docs/api/:org_id/oncall/teams/:team_id/schedule/put.ts> (a function with typed inputs; its response is checked against the schema)

## Path parameters

- `org_id` string, required
- `team_id` string, required

## Request body

- SetScheduleRequest: A full replace: rotations absent from the body are removed, not left standing.
  - `rotations` Rotation[]
    - `id` string, required
    - `name` string, required: What an escalation level names it, and what a page calls it. Renameable — levels store the id, so a typo fix cannot move who is paged.
    - `shift_rules` ShiftRule[]: The stack. Highest priority whose restrictions match at `at` wins, and exactly one does. Follow-the-sun is several rules here, in one rotation. Two rotations are two people on call; three regional rules are one person across three timezones' working hours.
      - `anchor_micros` integer, required: Instant at which `members[0]`'s first shift begins, in microseconds. Shifts before it resolve too — the sequence extends backwards — so an anchor in the future is not an error. This is the whole "secondary" mechanism. Two rotations with the same roster and anchors one shift apart can never resolve to the same person, and that is data, not a rule: nothing at resolution time knows they are related.
      - `ends_at` integer, nullable: The rule stops applying at or after this instant. `None` means "until further notice". How a rule is retired (`architecture/02` §3b). Deleting it instead would throw away the record of who covered those hours. Exclusive, like every boundary here.
      - `members` string[], required: Participants in handover order. Emails, because email is the login and therefore the one identifier every user is guaranteed to have.
      - `name` string: What this rule is called on the calendar. "APAC business hours", "Weekend", "Base rotation".
      - `priority` integer: Higher wins when two rules in the same rotation both apply. Explicit rather than positional, so reordering the UI list cannot silently change who gets paged.
      - `restrictions` RotationTimeWindow[]: When this rule applies. Empty means always — the catch-all every follow-the-sun setup needs underneath the restricted rules.
        - `days` integer[]: 0 = Monday … 6 = Sunday. Empty means every day.
        - `end_minute` integer, required: Minutes from local midnight, exclusive. May be LESS than `start_minute`, meaning the window wraps midnight (a 22:00–06:00 night shift).
        - `start_minute` integer, required: Minutes from local midnight, inclusive.
      - `shift_micros` integer, required: Length of one shift, in microseconds. Must be > 0.
      - `starts_at` integer, nullable: The rule is not in effect before this instant. `None` means "since forever".
    - `source` string, nullable: Where this rotation came from. `Some("default")` marks one the system staffed when the team was created.
  - `timezone` string, nullable: Absent means the team's own zone; UTC silently shifts an Asia/Kolkata team by 5.5 hours.

## Response `200`

Success

- Schedule: A team's rotations, one per staffed level.
  - `created_at` integer, required
  - `id` string, required
  - `org_id` string, required
  - `overrides` ScheduleOverride[]: Covers in force over this schedule. Carried on the schedule rather than fetched at each call site because every resolution has to see them. An override the page path forgets to load is worse than no override feature at all: the engineer who arranged cover stops watching, and the page still goes to them. Stored in `oncall_overrides`, not in the schedule row — they have their own lifecycle and audit trail.
    - `covering_for` string, nullable: Who is being covered. Optional: "cover tonight" is a real request even when nobody has worked out whose shift tonight is.
    - `created_at` integer, required
    - `created_by` string, required
    - `end_at` integer, required: Exclusive, so two back-to-back overrides never both cover the instant between them — the same rule the handover boundary follows.
    - `id` string, required
    - `org_id` string, required
    - `reason` string, nullable
    - `rotation_id` string, required: Which rotation is being covered. A cover stands in for one position. Covering two of them is two covers, said out loud, rather than one that silently lands the same person in both.
    - `start_at` integer, required: Inclusive.
    - `team_id` string, required
    - `user_email` string, required: Who is actually holding the pager for this window.
  - `rotations` Rotation[], required
    - `id` string, required
    - `name` string, required: What an escalation level names it, and what a page calls it. Renameable — levels store the id, so a typo fix cannot move who is paged.
    - `shift_rules` ShiftRule[]: The stack. Highest priority whose restrictions match at `at` wins, and exactly one does. Follow-the-sun is several rules here, in one rotation. Two rotations are two people on call; three regional rules are one person across three timezones' working hours.
      - `anchor_micros` integer, required: Instant at which `members[0]`'s first shift begins, in microseconds. Shifts before it resolve too — the sequence extends backwards — so an anchor in the future is not an error. This is the whole "secondary" mechanism. Two rotations with the same roster and anchors one shift apart can never resolve to the same person, and that is data, not a rule: nothing at resolution time knows they are related.
      - `ends_at` integer, nullable: The rule stops applying at or after this instant. `None` means "until further notice". How a rule is retired (`architecture/02` §3b). Deleting it instead would throw away the record of who covered those hours. Exclusive, like every boundary here.
      - `members` string[], required: Participants in handover order. Emails, because email is the login and therefore the one identifier every user is guaranteed to have.
      - `name` string: What this rule is called on the calendar. "APAC business hours", "Weekend", "Base rotation".
      - `priority` integer: Higher wins when two rules in the same rotation both apply. Explicit rather than positional, so reordering the UI list cannot silently change who gets paged.
      - `restrictions` RotationTimeWindow[]: When this rule applies. Empty means always — the catch-all every follow-the-sun setup needs underneath the restricted rules.
        - `days` integer[]: 0 = Monday … 6 = Sunday. Empty means every day.
        - `end_minute` integer, required: Minutes from local midnight, exclusive. May be LESS than `start_minute`, meaning the window wraps midnight (a 22:00–06:00 night shift).
        - `start_minute` integer, required: Minutes from local midnight, inclusive.
      - `shift_micros` integer, required: Length of one shift, in microseconds. Must be > 0.
      - `starts_at` integer, nullable: The rule is not in effect before this instant. `None` means "since forever".
    - `source` string, nullable: Where this rotation came from. `Some("default")` marks one the system staffed when the team was created.
  - `team_id` string, required
  - `timezone` string, required
  - `unavailability` Unavailability[]: Absences in force over this schedule, for the people on its rotations. Carried here for the same reason as the covers, against the same failure: a path that loads the schedule and forgets the absences gets a plausible answer that pages somebody on a beach. Stored org-wide in `oncall_unavailability` and narrowed to this schedule's members by the loader.
    - `created_at` integer, required
    - `created_by` string, required
    - `end_at` integer, required: Exclusive, matching every other boundary here: the end instant already belongs to the shift they are back for.
    - `id` string, required
    - `org_id` string, required
    - `reason` string, nullable
    - `start_at` integer, required: Inclusive.
    - `user_email` string, required: Whose absence. Email, because email is the login.
  - `updated_at` integer, required

---

[API](https://skmtc.dev/openobserve/apis/openobserve-api-http.md) - [All operations](https://skmtc.dev/openobserve/apis/openobserve-api-http/llms.txt) - [OpenAPI document](https://skmtc.dev/openobserve/apis/openobserve-api-http/revisions/0529b8a10252?raw)
