---
title: "Schedules HTTP API | Grafana Cloud documentation"
description: "Schedules HTTP API Create a schedule Required permission: grafana-irm-app.schedules:write shell Copy curl \"{{API_URL}}/api/v1/schedules/\" \\ --request POST \\ --header \"Authorization: Bearer meowmeowmeow\" \\ --header \"Content-Type: application/json\" \\ --header \"X-Grafana-URL: https://your-stack.grafana.net\" \\ --data '{ \"name\": \"Demo schedule iCal\", \"ical_url_primary\": \"https://example.com/meow_calendar.ics\", \"slack\": { \"channel_id\": \"MEOW_SLACK_ID\", \"user_group_id\": \"MEOW_SLACK_ID\" } }' The above command returns JSON structured in the following way:"
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# Schedules HTTP API

## Create a schedule

**Required permission**: `grafana-irm-app.schedules:write`

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/" \
  --request POST \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "Content-Type: application/json" \
  --header "X-Grafana-URL: https://your-stack.grafana.net" \
  --data '{
    "name": "Demo schedule iCal",
    "ical_url_primary": "https://example.com/meow_calendar.ics",
    "slack": {
      "channel_id": "MEOW_SLACK_ID",
      "user_group_id": "MEOW_SLACK_ID"
    }
  }'
```

The above command returns JSON structured in the following way:

JSON ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```json
{
  "id": "SBM7DV7BKFUYU",
  "name": "Demo schedule iCal",
  "type": "ical",
  "team_id": null,
  "ical_url_primary": "https://example.com/meow_calendar.ics",
  "ical_url_overrides": "https://example.com/meow_calendar_overrides.ics",
  "on_call_now": ["U4DNY931HHJS5"],
  "slack": {
    "channel_id": "MEOW_SLACK_ID",
    "user_group_id": "MEOW_SLACK_ID"
  }
}
```

Expand table

| Parameter              | Unique | Required         | Description                                                                                                                                                                                                                                                                                     |
|------------------------|--------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name`                 | Yes    | Yes              | Schedule name.                                                                                                                                                                                                                                                                                  |
| `type`                 | No     | Yes              | Schedule type. May be `ical` (used for iCalendar integration), `calendar` (used for manually created on-call shifts) or `web` (for web UI managed schedules).                                                                                                                                   |
| `team_id`              | No     | No               | ID of the team.                                                                                                                                                                                                                                                                                 |
| `time_zone`            | No     | Yes              | Schedule time zone. It is used for manually added on-call shifts in Schedules with type `calendar`. Default time zone is `UTC`. For more information about time zones, see [time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Not used for schedules with type `ical`. |
| `ical_url_primary`     | No     | If type = `ical` | URL of external iCal calendar for schedule with type `ical`.                                                                                                                                                                                                                                    |
| `ical_url_overrides`   | No     | Optional         | URL of external iCal calendar for schedule with any type. Events from this calendar override events from primary calendar or from on-call shifts.                                                                                                                                               |
| `enable_web_overrides` | No     | Optional         | Whether to enable web overrides or not. Setting specific for API/Terraform based schedules (`calendar` type).                                                                                                                                                                                   |
| `slack`                | No     | Optional         | Dictionary with Slack-specific settings for a schedule. Includes `channel_id` and `user_group_id` fields, that take a channel ID and a user group ID from Slack.                                                                                                                                |
| `shifts`               | No     | Optional         | List of shifts. Used for manually added on-call shifts in Schedules with type `calendar`.                                                                                                                                                                                                       |

**HTTP request**

`POST {{API_URL}}/api/v1/schedules/`

## Get a schedule

**Required permission**: `grafana-irm-app.schedules:read`

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \
  --request GET \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "Content-Type: application/json" \
  --header "X-Grafana-URL: https://your-stack.grafana.net"
```

The above command returns JSON structured in the following way:

JSON ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```json
{
  "id": "SBM7DV7BKFUYU",
  "name": "Demo schedule iCal",
  "type": "ical",
  "team_id": null,
  "ical_url_primary": "https://example.com/meow_calendar.ics",
  "ical_url_overrides": "https://example.com/meow_calendar_overrides.ics",
  "on_call_now": ["U4DNY931HHJS5"],
  "slack": {
    "channel_id": "MEOW_SLACK_ID",
    "user_group_id": "MEOW_SLACK_ID"
  }
}
```

**HTTP request**

`GET {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/`

## List schedules

**Required permission**: `grafana-irm-app.schedules:read`

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/" \
  --request GET \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "Content-Type: application/json" \
  --header "X-Grafana-URL: https://your-stack.grafana.net"
```

The above command returns JSON structured in the following way:

JSON ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```json
{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "SBM7DV7BKFUYU",
      "name": "Demo schedule iCal",
      "type": "ical",
      "team_id": null,
      "ical_url_primary": "https://example.com/meow_calendar.ics",
      "ical_url_overrides": "https://example.com/meow_calendar_overrides.ics",
      "on_call_now": ["U4DNY931HHJS5"],
      "slack": {
        "channel_id": "MEOW_SLACK_ID",
        "user_group_id": "MEOW_SLACK_ID"
      }
    },
    {
      "id": "S3Z477AHDXTMF",
      "name": "Demo schedule Calendar",
      "type": "calendar",
      "team_id": null,
      "time_zone": "America/New_York",
      "on_call_now": ["U4DNY931HHJS5"],
      "shifts": ["OH3V5FYQEYJ6M", "O9WTH7CKM3KZW"],
      "ical_url_overrides": null,
      "slack": {
        "channel_id": "MEOW_SLACK_ID",
        "user_group_id": "MEOW_SLACK_ID"
      }
    }
  ],
  "current_page_number": 1,
  "page_size": 50,
  "total_pages": 1
}
```

> **Note**: The response is [paginated](/docs/grafana-cloud/alerting-and-irm/irm/reference/oncall-api/#pagination). You may need to make multiple requests to get all records.

The following available filter parameter should be provided as a `GET` argument:

- `name` (Exact match)
- `team_id` (Exact match, team ID)

**HTTP request**

`GET {{API_URL}}/api/v1/schedules/`

## Update a schedule

**Required permission**: `grafana-irm-app.schedules:write`

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \
  --request PUT \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "Content-Type: application/json" \
  --header "X-Grafana-URL: https://your-stack.grafana.net" \
  --data '{
    "name": "Demo schedule iCal",
    "ical_url": "https://example.com/meow_calendar.ics",
    "slack": {
        "channel_id": "MEOW_SLACK_ID"
    }
  }'
```

The above command returns JSON structured in the following way:

JSON ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```json
{
  "id": "SBM7DV7BKFUYU",
  "name": "Demo schedule iCal",
  "type": "ical",
  "team_id": null,
  "ical_url_primary": "https://example.com/meow_calendar.ics",
  "ical_url_overrides": "https://example.com/meow_calendar_overrides.ics",
  "on_call_now": ["U4DNY931HHJS5"],
  "slack": {
    "channel_id": "MEOW_SLACK_ID",
    "user_group_id": "MEOW_SLACK_ID"
  }
}
```

**HTTP request**

`PUT {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/`

## Delete a schedule

**Required permission**: `grafana-irm-app.schedules:write`

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \
  --request DELETE \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "Content-Type: application/json" \
  --header "X-Grafana-URL: https://your-stack.grafana.net"
```

**HTTP request**

`DELETE {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/`

## Get current on-call users for a schedule

**Required permission**: `grafana-irm-app.schedules:read`

Returns the users currently on-call for a given schedule, including their phone number, phone number status, and shift timing. This endpoint is designed for use with automated call routing systems such as Twilio Studio Flows.

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/current_oncall/" \
  --request GET \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "X-Grafana-URL: https://your-stack.grafana.net"
```

The above command returns JSON structured in the following way:

JSON ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```json
{
  "count": 1,
  "users": [
    {
      "id": "U4DNY931HHJS5",
      "grafana_id": 456,
      "email": "alice@example.com",
      "slack": {
          "user_id": "UALICESLACKID",
          "team_id": "TSLACKTEAMID"
      },
      "username": "alice",
      "role": "admin",
      "is_phone_number_verified": true,
      "phone_number": "+12345678901",
      "phone_number_status": "available",
      "timezone": "America/New_York",
      "teams": ["TAAM1K1NNEHAG"]
    }
  ],
  "shifts": [
    {
      "schedule_id": "SBM7DV7BKFUYU",
      "schedule_name": "Demo schedule iCal",
      "team_id": null,
      "shift_start": "2026-03-24T09:00:00+00:00",
      "shift_end": "2026-03-25T09:00:00+00:00",
      "users": ["U4DNY931HHJS5"]
    }
  ]
}
```

Expand table

| Parameter             | Description                                                                                                                        |
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------|
| `slack`               | A Slack user identity (`user_id`, `team_id`), or `null` if not connected.                                                          |
| `timezone`            | The user’s timezone (e.g. `America/New_York`), or `null`.                                                                          |
| `teams`               | List of team IDs the user belongs to.                                                                                              |
| `phone_number`        | The user’s verified phone number, or `null` if not configured or private.                                                          |
| `phone_number_status` | One of: `available` (number returned), `private` (user has hidden their number), `not_configured` (user has no verified number).   |
| `shifts`              | List of shift entries with schedule reference (`schedule_id`, `schedule_name`, `team_id`) and timing (`shift_start`, `shift_end`). |

> Note
> 
> When the organization setting **Override phone number privacy for API** is enabled, users who have set their phone number to private will still have their number returned with status `available`. This setting does not affect phone number visibility in the UI.

When no one is currently on-call, `count` is `0`, `users` is an empty list, and `shifts` is an empty list.

**HTTP request**

`GET {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/current_oncall/`

## Get previous on-call users for a schedule

**Required permission**: `grafana-irm-app.schedules:read`

Returns the users who were on-call during the most recently completed shift for a given schedule.

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/previous_oncall/" \
  --request GET \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "X-Grafana-URL: https://your-stack.grafana.net"
```

The response format is identical to `current_oncall`. The `shifts` array contains the most recently ended shift(s).

Expand table

| Query Parameter | Required | Default | Description                                                     |
|-----------------|----------|---------|-----------------------------------------------------------------|
| `days`          | No       | `7`     | Number of days to look back for past shifts. Maximum value: 30. |

**HTTP request**

`GET {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/previous_oncall/`

## Get next on-call users for a schedule

**Required permission**: `grafana-irm-app.schedules:read`

Returns the users who will be on-call during the next upcoming shift for a given schedule. Shifts that overlap with the current time are skipped.

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/next_oncall/" \
  --request GET \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "X-Grafana-URL: https://your-stack.grafana.net"
```

The response format is identical to `current_oncall`. The `shifts` array contains the next upcoming shift(s).

Expand table

| Query Parameter | Required | Default | Description                                                        |
|-----------------|----------|---------|--------------------------------------------------------------------|
| `days`          | No       | `7`     | Number of days to look ahead for future shifts. Maximum value: 30. |

**HTTP request**

`GET {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/next_oncall/`

## Export a schedule’s final shifts

**Required permission**: `grafana-irm-app.schedules:read`

**HTTP request**

shell ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/final_shifts?start_date=2023-01-01&end_date=2023-02-01" \
  --request GET \
  --header "Authorization: Bearer meowmeowmeow" \
  --header "X-Grafana-URL: https://your-stack.grafana.net"
```

The above command returns JSON structured in the following way:

JSON ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```json
{
  "count": 12,
  "next": null,
  "previous": null,
  "results": [
    {
      "user_pk": "UC2CHRT5SD34X",
      "user_email": "alice@example.com",
      "user_username": "alice",
      "shift_start": "2023-01-02T09:00:00Z",
      "shift_end": "2023-01-02T17:00:00Z"
    },
    {
      "user_pk": "U7S8H84ARFTGN",
      "user_email": "bob@example.com",
      "user_username": "bob",
      "shift_start": "2023-01-04T09:00:00Z",
      "shift_end": "2023-01-04T17:00:00Z"
    },
    {
      "user_pk": "UC2CHRT5SD34X",
      "user_email": "alice@example.com",
      "user_username": "alice",
      "shift_start": "2023-01-06T09:00:00Z",
      "shift_end": "2023-01-06T17:00:00Z"
    },
    {
      "user_pk": "U7S8H84ARFTGN",
      "user_email": "bob@example.com",
      "user_username": "bob",
      "shift_start": "2023-01-09T09:00:00Z",
      "shift_end": "2023-01-09T17:00:00Z"
    },
    {
      "user_pk": "UC2CHRT5SD34X",
      "user_email": "alice@example.com",
      "user_username": "alice",
      "shift_start": "2023-01-11T09:00:00Z",
      "shift_end": "2023-01-11T17:00:00Z"
    },
    {
      "user_pk": "U7S8H84ARFTGN",
      "user_email": "bob@example.com",
      "user_username": "bob",
      "shift_start": "2023-01-13T09:00:00Z",
      "shift_end": "2023-01-13T17:00:00Z"
    },
    {
      "user_pk": "UC2CHRT5SD34X",
      "user_email": "alice@example.com",
      "user_username": "alice",
      "shift_start": "2023-01-16T09:00:00Z",
      "shift_end": "2023-01-16T17:00:00Z"
    },
    {
      "user_pk": "U7S8H84ARFTGN",
      "user_email": "bob@example.com",
      "user_username": "bob",
      "shift_start": "2023-01-18T09:00:00Z",
      "shift_end": "2023-01-18T17:00:00Z"
    },
    {
      "user_pk": "UC2CHRT5SD34X",
      "user_email": "alice@example.com",
      "user_username": "alice",
      "shift_start": "2023-01-20T09:00:00Z",
      "shift_end": "2023-01-20T17:00:00Z"
    },
    {
      "user_pk": "U7S8H84ARFTGN",
      "user_email": "bob@example.com",
      "user_username": "bob",
      "shift_start": "2023-01-23T09:00:00Z",
      "shift_end": "2023-01-23T17:00:00Z"
    },
    {
      "user_pk": "UC2CHRT5SD34X",
      "user_email": "alice@example.com",
      "user_username": "alice",
      "shift_start": "2023-01-25T09:00:00Z",
      "shift_end": "2023-01-25T17:00:00Z"
    },
    {
      "user_pk": "U7S8H84ARFTGN",
      "user_email": "bob@example.com",
      "user_username": "bob",
      "shift_start": "2023-01-27T09:00:00Z",
      "shift_end": "2023-01-27T17:00:00Z"
    }
  ],
  "current_page_number": 1,
  "page_size": 50,
  "total_pages": 1
}
```

> **Note**: The response is [paginated](/docs/grafana-cloud/alerting-and-irm/irm/reference/oncall-api/#pagination). You may need to make multiple requests to get all records.

### Caveats

Some notes on the `start_date` and `end_date` query parameters:

- they are both required and should represent ISO 8601 formatted dates
- `end_date` must be greater than or equal to `start_date`
- `end_date` cannot be more than 365 days in the future from `start_date`

> **Note**: you can update schedules affecting past events, which will then change the output you get from this endpoint. To get consistent information about past shifts you must be sure to avoid updating rotations in-place but apply the changes as new rotations with the right starting dates.

### Example script to transform data to .csv for all of your schedules

The following Python script will generate a `.csv` file, `oncall-report-2023-01-01-to-2023-01-31.csv`. This file will contain three columns, `user_pk`, `user_email`, and `hours_on_call`, which represents how many hours each user was on call during the period starting January 1, 2023 to January 31, 2023 (inclusive).

Python ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```python
import csv
import requests
from datetime import datetime

# CUSTOMIZE THE FOLLOWING VARIABLES
START_DATE = "2023-01-01"
END_DATE = "2023-01-31"
OUTPUT_FILE_NAME = f"oncall-report-{START_DATE}-to-{END_DATE}.csv"
MY_ONCALL_API_BASE_URL = "https://oncall-prod-us-central-0.grafana.net/oncall/api/v1/schedules"
MY_ONCALL_API_KEY = "meowmeowwoofwoof"

headers = {"Authorization": f"Bearer {MY_ONCALL_API_KEY}"}
schedule_ids = [schedule["id"] for schedule in requests.get(MY_ONCALL_API_BASE_URL, headers=headers).json()["results"]]
user_on_call_hours = {}

for schedule_id in schedule_ids:
  response = requests.get(
    f"{MY_ONCALL_API_BASE_URL}/{schedule_id}/final_shifts?start_date={START_DATE}&end_date={END_DATE}",
    headers=headers)

  for final_shift in response.json()["results"]:
    user_pk = final_shift["user_pk"]
    end = datetime.fromisoformat(final_shift["shift_end"])
    start = datetime.fromisoformat(final_shift["shift_start"])
    shift_time_in_seconds = (end - start).total_seconds()
    shift_time_in_hours = shift_time_in_seconds / (60 * 60)

    if user_pk in user_on_call_hours:
      user_on_call_hours[user_pk]["hours_on_call"] += shift_time_in_hours
    else:
      user_on_call_hours[user_pk] = {
        "email": final_shift["user_email"],
        "hours_on_call": shift_time_in_hours,
      }

with open(OUTPUT_FILE_NAME, "w") as fp:
  csv_writer = csv.DictWriter(fp, ["user_pk", "user_email", "hours_on_call"])
  csv_writer.writeheader()

  for user_pk, user_info in user_on_call_hours.items():
    csv_writer.writerow({
      "user_pk": user_pk, "user_email": user_info["email"], "hours_on_call": user_info["hours_on_call"]})
```
