Get a rotation's recorded history
using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://api.roundrobinbot.eu/v1/rotations/example/history?from=2026-04-15T12%3A00%3A00Z&to=2026-04-15T12%3A00%3A00Z"), Headers = { { "Authorization", "Bearer <token>" }, },};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://api.roundrobinbot.eu/v1/rotations/example/history?from=2026-04-15T12%3A00%3A00Z&to=2026-04-15T12%3A00%3A00Z"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() .url("https://api.roundrobinbot.eu/v1/rotations/example/history?from=2026-04-15T12%3A00%3A00Z&to=2026-04-15T12%3A00%3A00Z") .get() .addHeader("Authorization", "Bearer <token>") .build();
Response response = client.newCall(request).execute();import axios from 'axios';
const options = { method: 'GET', url: 'https://api.roundrobinbot.eu/v1/rotations/example/history', params: {from: '2026-04-15T12:00:00Z', to: '2026-04-15T12:00:00Z'}, headers: {Authorization: 'Bearer <token>'}};
try { const { data } = await axios.request(options); console.log(data);} catch (error) { console.error(error);}const url = 'https://api.roundrobinbot.eu/v1/rotations/example/history?from=2026-04-15T12%3A00%3A00Z&to=2026-04-15T12%3A00%3A00Z';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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 duty 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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Query Parameters
Section titled “Query Parameters”Responses
Section titled “Responses”OK
The envelope every /v1 collection comes back in.
object
One turn a rotation recorded: who held it, where, and between which instants.
object
Recorded identity: slackUser, externalPerson, nobody or unknown. An external person held duty without a Slack mapping. Unknown means the record cannot identify the holder; it does not mean nobody was on duty.
Recorded external participant name, when available. Absent when nobody was on duty.
Recorded external participant email address, when available. Absent when nobody was on duty.
Identity of the lane in a window where duty was held, when recorded.
Local identity of the external assignment, when recorded. Separate history entries can share this identity.
Recorded provider assignment details. Absent when the history entry has no provider metadata.
object
Round Robin binding identity recorded with this assignment.
Provider member kind: user for a participant or empty for an explicitly unassigned slot.
Provider participant ID. Absent for an explicitly unassigned slot.
Provider assignment source type, when recorded.
Provider rotation ID, when present in the assignment source.
Provider custom shift ID, when present in the assignment source.
Provider override ID, when present in the assignment source.
Original provider assignment start in UTC. The recorded duty segment can start later.
Recorded provider assignment end in UTC. The recorded duty segment can end earlier.
When the turn started.
When it ended.
Slack user ID of the recorded holder. Absent for external participants without a Slack mapping, unidentified holders and stretches nobody covered. Read identityKind to distinguish them.
True when nobody was on duty, including an explicitly unassigned provider slot. False for an external participant who has no Slack mapping.
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.
Which lane and which window the turn was held on, named as they were called at the time.
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, after-mention, interval-reconciliation (an automatic change that no other code names), direct-assignment-changed or direct-assignment-ended.
The hours the window covered while this turn was held, which is not necessarily what it covers now. Absent when there is no record of that window’s hours to read.
object
The one zone these hours are expressed in, as its own identifier (Europe/Rome). Empty on a rotation with no cadence, which has no zone to express one in.
Whether the window restricted nothing: open the whole week, in which case coverage is empty.
The repeating week the window was open for. Empty means the whole week.
One stretch of the coverage week, which may run past midnight into the next day.
object
A weekday name, monday through sunday.
A time of day, HH:mm.
Examplegenerated
{ "data": [ { "identityKind": "example", "externalName": "example", "externalEmail": "example", "cellId": "example", "localAssignmentId": "example", "externalAssignment": { "bindingId": "example", "memberKind": "example", "participantId": "example", "sourceType": "example", "rotationId": "example", "shiftId": "example", "overrideId": "example", "originalStartUtc": "2026-04-15T12:00:00Z", "originalEndUtc": "2026-04-15T12:00:00Z" }, "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
object
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example"}Unauthorized
object
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example"}Forbidden
object
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example"}Not Found
object
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example"}