Skip to content

Get a rotation's recorded history

GET
/v1/rotations/{rotationId}/history
curl --request GET \
--url 'https://api.roundrobinbot.eu/v1/rotations/example/history?from=2026-04-15T12%3A00%3A00Z&to=2026-04-15T12%3A00%3A00Z' \
--header 'Authorization: Bearer <token>'

The turns this rotation actually recorded inside a window you name, one entry per turn held: who held it, the lane and coverage window it was held on, when it started, when it ended, and the hours the window covered at the time. This is the record, not the plan. Read GET /v1/rotations/{rotationId}/schedule for what the rules say should happen; the two disagree whenever a hand-over was skipped for absence, a window shut early, or somebody was put on duty by hand, and the record is the one that answers “who was on call when this fired”. A stretch nobody covered is an entry of its own with nobodyWasOnCall true and no userId, so a gap is a fact you can read rather than a hole between two entries you have to infer. And an entry keeps the hours it was held under: widening a window does not rewrite what last month looked like. from and to are both required and no more than 90 days apart. An entry that began before the window or ended after it is included, because the turn in progress when the window opened is usually the one you wanted. No ETag: a page is assembled from many entries, so no single version stands behind it.

rotationId
required
string
from
required
string format: date-time
to
required
string format: date-time
page
integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
pageSize
integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
sort
string

OK

Media typeapplication/json

The envelope every /v1 collection comes back in.

object
data
Array<object>

One turn a rotation recorded: who held it, where, and between which instants.

object
start

When the turn started.

string format: date-time
end

When it ended.

string format: date-time
userId

The Slack user id of whoever held the turn, absent on a stretch nobody covered.

null | string
nobodyWasOnCall

True when this entry is a stretch nobody was on call for.

boolean
turn

The holder’s position in the turn order of the list the turn was dealt from. Absent on a stretch nobody covered, which has no position.

null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
laneId

Which lane and which window the turn was held on, named as they were called at the time.

null | string
laneName
null | string
windowId
null | string
windowName
null | string
reason

Why the turn ended, as a stable code: one of next-on-scheduled-duty (the cadence came round), rotate-to-next-on-duty, explicit-change, duty-cleared, removal-of-active-duty, coverage-changed, business-hours-offline, business-hours-back-online, change-in-external-schedule or after-mention.

string
hoursAsHeld
One of:
null
page
integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
pageSize
integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
totalItems
integer | string format: int64
/^-?(?:0|[1-9]\d*)$/
totalPages
integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
Examplegenerated
{
"data": [
{
"start": "2026-04-15T12:00:00Z",
"end": "2026-04-15T12:00:00Z",
"userId": "example",
"nobodyWasOnCall": true,
"turn": 1,
"laneId": "example",
"laneName": "example",
"windowId": "example",
"windowName": "example",
"reason": "example",
"hoursAsHeld": {
"timeZone": "example",
"alwaysOpen": true,
"coverage": [
{
"startDay": "example",
"startTime": "example",
"endDay": "example",
"endTime": "example"
}
]
}
}
],
"page": 1,
"pageSize": 1,
"totalItems": 1,
"totalPages": 1
}

Bad Request

Media typeapplication/json
object
type
null | string
title
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
detail
null | string
instance
null | string
Examplegenerated
{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example"
}

Unauthorized

Media typeapplication/json
object
type
null | string
title
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
detail
null | string
instance
null | string
Examplegenerated
{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example"
}

Forbidden

Media typeapplication/json
object
type
null | string
title
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
detail
null | string
instance
null | string
Examplegenerated
{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example"
}

Not Found

Media typeapplication/json
object
type
null | string
title
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
detail
null | string
instance
null | string
Examplegenerated
{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example"
}