2022-06-09 10:08:07 +01:00
---
2022-10-26 12:32:47 -04:00
canonical: https://grafana.com/docs/oncall/latest/oncall-api-reference/schedules/
2024-06-17 11:31:35 -04:00
title: Schedules HTTP API
2024-08-15 15:29:56 -04:00
weight: 0
2024-08-09 15:24:24 +01:00
refs:
pagination:
- pattern: /docs/oncall/
destination: /docs/oncall/< ONCALL_VERSION > /oncall-api-reference/#pagination
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/oncall/oncall-api-reference/#pagination
2022-06-09 10:08:07 +01:00
---
2022-06-03 08:09:47 -06:00
2024-06-17 11:31:35 -04:00
# Schedules HTTP API
## Create a schedule
2022-06-03 08:09:47 -06:00
2024-12-11 08:54:00 -03:00
**Required permission**: `grafana-oncall-app.schedules:write`
2022-06-03 08:09:47 -06:00
```shell
curl "{{API_URL}}/api/v1/schedules/" \
--request POST \
--header "Authorization: meowmeowmeow" \
--header "Content-Type: application/json" \
--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
{
2022-06-09 10:08:07 +01:00
"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"
}
2022-06-03 08:09:47 -06:00
}
```
2022-06-09 10:08:07 +01:00
| Parameter | Unique | Required | Description |
| -------------------- | :----: | :--------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | Yes | Yes | Schedule name. |
2023-12-04 16:01:56 -03:00
| `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). |
2022-06-09 10:08:07 +01:00
| `team_id` | No | No | ID of the team. |
2023-12-04 16:01:56 -03:00
| `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` . |
2022-06-09 10:08:07 +01:00
| `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. |
2023-09-26 14:04:53 -03:00
| `enable_web_overrides` | No | Optional | Whether to enable web overrides or not. Setting specific for API/Terraform based schedules (`calendar` type). |
2022-06-09 10:08:07 +01:00
| `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` . |
2022-06-03 08:09:47 -06:00
**HTTP request**
`POST {{API_URL}}/api/v1/schedules/`
2024-06-17 11:31:35 -04:00
## Get a schedule
2022-06-03 08:09:47 -06:00
2024-12-11 08:54:00 -03:00
**Required permission**: `grafana-oncall-app.schedules:read`
2022-06-03 08:09:47 -06:00
```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \
--request GET \
--header "Authorization: meowmeowmeow" \
--header "Content-Type: application/json" \
```
The above command returns JSON structured in the following way:
```json
{
2022-06-09 10:08:07 +01:00
"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"
}
2022-06-03 08:09:47 -06:00
}
```
**HTTP request**
`GET {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/`
2024-06-17 11:31:35 -04:00
## List schedules
2022-06-03 08:09:47 -06:00
2024-12-11 08:54:00 -03:00
**Required permission**: `grafana-oncall-app.schedules:read`
2022-06-03 08:09:47 -06:00
```shell
curl "{{API_URL}}/api/v1/schedules/" \
--request GET \
--header "Authorization: meowmeowmeow" \
2022-06-09 10:08:07 +01:00
--header "Content-Type: application/json"
2022-06-03 08:09:47 -06:00
```
The above command returns JSON structured in the following way:
```json
{
2022-06-09 10:08:07 +01:00
"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"
}
}
augment API response pagination attributes (#2471)
# What this PR does
This PR:
- adds a few attributes to paginated API responses
- removes channel filter "send demo alert" internal API endpoint + tests
(this endpoint was marked as deprecated + not consumed by the web UI)
With the new paginated API response schema, the web UI will no longer
need to:
- hardcode `ITEMS_PER_PAGE` for each table
- manually calculate total number of pages
(these two things ☝️ will be done in
https://github.com/grafana/oncall/issues/2476)
For `GET /api/internal/v1/alertgroups` the response will now look like
this:
```diff
{
"next": <url> | None,
"previous": <url> | None,
"results": [],
++ "page_size": <int>
}
```
For all other paginated API responses, the response will now look like:
```diff
{
"count": <int>,
"next": <url> | None,
"previous": <url> | None,
"results": [],
++ "page_size": <int>,
++ "current_page_number": <int>,
++ "total_pages": <int>
}
```
## TODO
- [x] update public API docs to include these new attributes
## Checklist
- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
2023-07-14 17:19:40 +02:00
],
"current_page_number": 1,
"page_size": 50,
"total_pages": 1
2022-06-03 08:09:47 -06:00
}
```
2024-08-09 15:24:24 +01:00
> **Note**: The response is [paginated](ref:pagination). You may need to make multiple requests to get all records.
2022-06-03 08:09:47 -06:00
The following available filter parameter should be provided as a `GET` argument:
2022-06-09 10:08:07 +01:00
- `name` (Exact match)
2024-09-24 15:16:22 -04:00
- `team_id` (Exact match, team ID)
2022-06-03 08:09:47 -06:00
**HTTP request**
`GET {{API_URL}}/api/v1/schedules/`
2024-06-17 11:31:35 -04:00
## Update a schedule
2022-06-03 08:09:47 -06:00
2024-12-11 08:54:00 -03:00
**Required permission**: `grafana-oncall-app.schedules:write`
2022-06-03 08:09:47 -06:00
```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \
--request PUT \
--header "Authorization: meowmeowmeow" \
--header "Content-Type: application/json" \
--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
{
2022-06-09 10:08:07 +01:00
"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"
}
2022-06-03 08:09:47 -06:00
}
```
**HTTP request**
`PUT {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/`
2024-06-17 11:31:35 -04:00
## Delete a schedule
2022-06-03 08:09:47 -06:00
2024-12-11 08:54:00 -03:00
**Required permission**: `grafana-oncall-app.schedules:write`
2022-06-03 08:09:47 -06:00
```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \
--request DELETE \
--header "Authorization: meowmeowmeow" \
--header "Content-Type: application/json"
```
**HTTP request**
2022-06-09 10:08:07 +01:00
`DELETE {{API_URL}}/api/v1/schedules/<SCHEDULE_ID>/`
2023-06-05 16:06:10 +02:00
2024-06-17 11:31:35 -04:00
## Export a schedule's final shifts
2023-06-05 16:06:10 +02:00
2024-12-11 08:54:00 -03:00
**Required permission**: `grafana-oncall-app.schedules:read`
2023-06-05 16:06:10 +02:00
**HTTP request**
```shell
curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/final_shifts?start_date=2023-01-01& end_date=2023-02-01" \
--request GET \
--header "Authorization: meowmeowmeow"
```
The above command returns JSON structured in the following way:
```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"
}
augment API response pagination attributes (#2471)
# What this PR does
This PR:
- adds a few attributes to paginated API responses
- removes channel filter "send demo alert" internal API endpoint + tests
(this endpoint was marked as deprecated + not consumed by the web UI)
With the new paginated API response schema, the web UI will no longer
need to:
- hardcode `ITEMS_PER_PAGE` for each table
- manually calculate total number of pages
(these two things ☝️ will be done in
https://github.com/grafana/oncall/issues/2476)
For `GET /api/internal/v1/alertgroups` the response will now look like
this:
```diff
{
"next": <url> | None,
"previous": <url> | None,
"results": [],
++ "page_size": <int>
}
```
For all other paginated API responses, the response will now look like:
```diff
{
"count": <int>,
"next": <url> | None,
"previous": <url> | None,
"results": [],
++ "page_size": <int>,
++ "current_page_number": <int>,
++ "total_pages": <int>
}
```
## TODO
- [x] update public API docs to include these new attributes
## Checklist
- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
2023-07-14 17:19:40 +02:00
],
"current_page_number": 1,
"page_size": 50,
"total_pages": 1
2023-06-05 16:06:10 +02:00
}
```
2024-08-09 15:24:24 +01:00
> **Note**: The response is [paginated](ref:pagination). You may need to make multiple requests to get all records.
2024-06-17 11:31:35 -04:00
### Caveats
2023-06-05 16:06:10 +02:00
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`
2023-08-23 11:07:06 -03:00
>**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.
2023-06-05 16:06:10 +02:00
2024-06-17 11:31:35 -04:00
### Example script to transform data to .csv for all of your schedules
2023-06-05 16:06:10 +02:00
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
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": 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"]})
```