Skip to content

Create a rotation

POST
/v1/rotations
curl --request POST \
--url https://api.roundrobinbot.eu/v1/rotations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json-patch+json' \
--header 'Idempotency-Key: example' \
--data '{ "name": "example", "description": "example", "code": "example", "public": true, "lanes": 1, "owners": [ "example" ], "channels": [ "example" ], "userIds": [ "example" ], "userGroupIds": [ "example" ] }'

Answers 201 with the new rotation, its Location and its ETag. name and at least one entry in owners are required. The rotation starts enabled and manual, as one lane over one coverage window: give that window a cadence with PUT /v1/rotations/{rotationId}/coverage to put it on automatic. A retry carrying the same Idempotency-Key answers 200 with the rotation the first attempt made, having created nothing. A key restricted to particular rotations cannot create. Three things the plan decides answer 402, and create nothing: more than one lane, which is a paid feature, a workspace that already has every rotation its plan includes, and a userIds or userGroupIds list that would put more people in the workspace’s rotations than its plan allows. People are counted once each however many rotations they are in. All three leave the Idempotency-Key unspent, so the same request works after an upgrade. A person holds one place in a list: a userIds entry named twice answers 400 and creates nothing.

Idempotency-Key
required
string
<= 255 characters

A value you generate for this create attempt and reuse on every retry of it, so a retried request cannot make a second rotation. A UUID is the usual choice. Repeating the key within 24 hours answers 200 with the rotation it already created; after that the key is spent and the create is refused 409. Omitting the header is 428. https://docs.roundrobinbot.eu/api/conventions/#creating-things

The rotation POST /v1/rotations creates.

object
name

What the rotation is called. Required, and not unique: two rotations may share a name.

null | string
description

A sentence about what the rotation covers.

null | string
code

The short code the rotation answers to in Slack. Must be unique in the workspace, and a clash is refused rather than resolved.

null | string
public

Whether every member of the workspace can see the rotation. Defaults to false, which leaves it visible to its members, its owners and workspace admins.

null | boolean
lanes

How many lanes the rotation runs: one person holds each lane, so two lanes is two people on duty at once. Defaults to 1, and more than one lane is a paid-plan feature.

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

The Slack user ids that may administer the rotation. Required, and at least one.

Array<string> | null
channels

The Slack channel ids the rotation posts to. The app must already be in every channel named.

Array<string> | null
userIds

The Slack user ids in the rotation, in the order duty passes through them. A person holds one place in the list: an id named twice answers 400 and creates nothing.

Array<string> | null
userGroupIds

Slack user group ids whose members are in the rotation. The rotation follows the group: add or remove somebody in Slack and the turn order changes with it.

Array<string> | null
Examplegenerated
{
"name": "example",
"description": "example",
"code": "example",
"public": true,
"lanes": 1,
"owners": [
"example"
],
"channels": [
"example"
],
"userIds": [
"example"
],
"userGroupIds": [
"example"
]
}

OK

Media typeapplication/json

A rotation: the grid of duties it runs, who is in it, and who holds each place right now.

object
id
string
name
string
description
null | string
code

The short code the rotation answers to in Slack.

null | string
enabled
boolean
public

Whether every member of the workspace can see the rotation.

boolean
mode

manual, auto or external.

string
runningLanes

How many lanes are running, which is how many people hold the shift at once.

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

How many places on this rotation have somebody on call right now.

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

How many places on this rotation have nobody on them at all.

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

Whether this rotation has more than one place to be on duty at once.

boolean
doubleCover

Whether one person may hold two places at once when the list behind them is too short to fill both.

boolean
skipsAreOffTime

Whether days the cadence skips — weekends, national holidays — count as time the rotation is not covered. One switch for the whole rotation, read into every window’s own reading.

boolean
owners
Array<string>
channels

The channels this rotation posts in: the same facts as channelSettings, flattened to the ids that receive duty messages.

Array<string>
channelSettings

Every channel this rotation uses, each saying what Round Robin does in it. Write one with PUT /v1/rotations/{rotationId}/channels/{channelId}.

Array<object>

One Slack channel a rotation uses, and what Round Robin does in it.

object
channelId

The Slack channel id, for example C0123456789.

string
postsMessages

Whether hand-overs, reminders and mentions of this rotation are posted in this channel.

boolean
syncsTopic

Whether this channel’s topic is rewritten with whoever is on duty when duty changes hands. Reported as false on every channel when enableTopic is off, whatever the channel itself holds, so a reader never has to combine two fields to know whether a topic is written.

boolean
enableTopic

Whether the rotation keeps channel topics pointed at whoever is on duty. The switch for the whole rotation: with it off, no channel’s topic is written whatever its own syncsTopic says.

boolean
topicTemplate

The template the topic is written from, or null to use Round Robin’s own wording. Shares its placeholders with the message templates, which are not part of this version.

null | string
members

Everybody in the rotation: the union across every list it draws on.

object
users

Members added individually.

Array<object>

One member of a rotation.

object
userId
string
sequenceNumber

Where the member sits in the order the lists are built from.

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

Members that came in through a Slack user group, listed under the group they came from. Add or remove somebody in Slack’s group and the rotation follows.

Array<object>

A Slack user group in the rotation, and the people currently in it.

object
userGroupId
string
users
Array<object>

One member of a rotation.

object
userId
string
sequenceNumber

Where the member sits in the order the lists are built from.

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

The rows of the grid: the kinds of duty this rotation runs.

Array<object>

A row of the grid: a kind of duty, such as primary or backend.

object
id
string
name

What the customer calls this lane, and absent until they name it.

null | string
windows

The columns of the grid: covered hours, a time zone, and a handover cadence.

Array<object>

A column of the grid: when the rotation is covered, and how often duty changes hands there.

object
id
string
name
null | string
timeZone

The one zone this window’s covered hours are expressed in, as its own identifier (Europe/Rome). Never rewritten to the tzdb canonical name a linked city shares. Empty on a manual rotation, which has no cadence and so no zone to express one in.

string
coverage

The repeating week this window is open for, as stored. Empty means the whole week.

Array<object>

One stretch of the coverage week, which may run past midnight into the next day.

object
startDay

A weekday name, monday through sunday.

string
startTime

A time of day, HH:mm.

string
endDay
string
endTime
string
alwaysOpen

Whether the window restricts nothing: open the whole week, every week.

boolean
cadence

How often duty changes hands in this column.

object
type

One of dayOfWeek, daysOfWeekList, dayOfMonth, nthWeekdayOfMonth, daily, workday, manual or external. Fields that mean nothing for the type are absent rather than defaulted, so test for presence rather than for a zero.

string
timeZone

The time zone the cadence was configured with, as its own identifier (Europe/Rome). Never rewritten to the tzdb canonical name a linked city shares.

string
startsAt

When the cadence starts, in its own time zone.

string format: date-time
period

How many units of the cadence’s own kind sit between two hand-overs.

integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
skipWeekends
boolean
skipNationalHolidays
boolean
countryCodes

The countries whose weekends and holidays the skips are read from.

Array<string>
nextEvents

The next hand-overs this window has already scheduled. For anything further out, ask for a window from GET /v1/rotations/{rotationId}/schedule.

Array<string>
dayOfWeek

Set for dayOfWeek and nthWeekdayOfMonth.

null | string
daysOfWeek

Set for daysOfWeekList.

Array<string> | null
dayOfMonth

Set for dayOfMonth.

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

Set for nthWeekdayOfMonth: which occurrences in the month, 1 through 5.

Array<integer | string> | null
times

Set for daily: the times of day it hands over at.

Array<string> | null
partner

Set for external: which partner drives the rotation. One of pagerDuty, jsm for Jira Service Management, or none when the rotation is external but no partner is linked yet.

null | string
openNow

Whether the window is open at the moment of the response.

boolean
currentCoverage
One of:
null
nextCoverage
One of:
null
cells

Every lane crossed with every window, and whoever holds each one.

Array<object>

A lane crossed with a window: one place somebody can be on duty.

object
id
string
laneId
string
windowId
string
rosterId

Which list this place is dealt from, matching an entry in the rotation’s rosters.

null | string
stopped

Whether this place is defined but not running, because the workspace is not on a paid plan.

boolean
holder
One of:
null
rosters

The lists this rotation deals from, each in the order this rotation deals it.

Array<object>

A list this rotation deals from, in the order this rotation deals it.

object
rosterId

The list this order belongs to. Absent on a rotation whose people are not on a shared list yet.

null | string
order

The turn order: who is dealt, and in what order.

Array<object>

One position in a turn order.

object
userId
string
userGroupId

Set when the member is in the order by way of a user group.

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

The turn the next handover deals from.

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

The Slack user group kept in sync with whoever is on call.

null | string
onCall

The on-call resource, embedded, so the common question needs no second call.

object
externalAssignments
One of:
null
rotationId

Rotation ID.

string
onCall

Current mapped Slack assignments. An empty list does not establish a coverage gap: check externalAssignments or unmappedExternalDuty.

Array<object>

One current mapped Slack assignment. A user can appear more than once when they hold multiple assignments.

object
userId

Slack user ID of the person on duty.

string
turn

Position in the native turn order. External assignments have no native turn order.

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

Cell holding this assignment.

null | string
laneId

Lane containing the cell. Lanes for external assignments do not identify provider roles.

null | string
laneName

Lane name, when available.

null | string
windowId

Window containing the cell. External assignment windows do not define provider recurrence.

null | string
windowName

Window name, when available.

null | string
since

Assignment start in UTC, when known.

null | string format: date-time
until

Exclusive assignment end in UTC, when known.

null | string format: date-time
skippedForAbsence

Members skipped for absence when this assignment began.

Array<object>

A member skipped because of an absence at assignment time.

object
userId

Slack user ID of the skipped member.

string
from

Absence start in UTC, when known.

null | string format: date-time
until

Absence end in UTC, when known.

null | string format: date-time
source

Absence source: googleCalendar or internalAvailability. Null when the source is unknown.

null | string
unmappedExternalDuty
One of:
null
users

Directory records for current mapped Slack users and skipped members when ?expand=users is requested. Null otherwise. Expansion disables ETag validation.

Array<object> | null

A Slack user, as Round Robin knows them.

object
id

The Slack user id.

string
teamId

The workspace that owns this person’s directory entry, which under Enterprise Grid can differ from the calling key’s workspace.

string
displayName
null | string
realName
null | string
email

Null when the install was never granted users:read.email, not only when unknown.

null | string
isGuest
boolean
isExternal

A Slack Connect person: visible through a shared channel, never a member of the workspace.

boolean
isDeleted

Deactivated in Slack, or gone from the workspace. These people are still answered rather than hidden, so an id a rotation still names resolves to something you can render.

boolean
avatarUrl

The person’s Slack avatar, at 192px.

null | string
timeZone

The person’s Slack time zone, as an IANA identifier (Europe/Rome). Absent when Round Robin has not seen one for them.

null | string
createdAt
string format: date-time
updatedAt
string format: date-time
Examplegenerated
{
"id": "example",
"name": "example",
"description": "example",
"code": "example",
"enabled": true,
"public": true,
"mode": "example",
"runningLanes": 1,
"heldSeats": 1,
"openSeats": 1,
"isGrid": true,
"doubleCover": true,
"skipsAreOffTime": true,
"owners": [
"example"
],
"channels": [
"example"
],
"channelSettings": [
{
"channelId": "example",
"postsMessages": true,
"syncsTopic": true
}
],
"enableTopic": true,
"topicTemplate": "example",
"members": {
"users": [
{
"userId": "example",
"sequenceNumber": 1
}
],
"userGroups": [
{
"userGroupId": "example",
"users": [
{
"userId": "example",
"sequenceNumber": 1
}
]
}
]
},
"lanes": [
{
"id": "example",
"name": "example"
}
],
"windows": [
{
"id": "example",
"name": "example",
"timeZone": "example",
"coverage": [
{
"startDay": "example",
"startTime": "example",
"endDay": "example",
"endTime": "example"
}
],
"alwaysOpen": true,
"cadence": {
"type": "example",
"timeZone": "example",
"startsAt": "2026-04-15T12:00:00Z",
"period": 1,
"skipWeekends": true,
"skipNationalHolidays": true,
"countryCodes": [
"example"
],
"nextEvents": [
"2026-04-15T12:00:00Z"
],
"dayOfWeek": "example",
"daysOfWeek": [
"example"
],
"dayOfMonth": 1,
"weeksInMonth": [
1
],
"times": [
"example"
],
"partner": "example"
},
"openNow": true,
"currentCoverage": {
"startUtc": "2026-04-15T12:00:00Z",
"endUtc": "2026-04-15T12:00:00Z"
},
"nextCoverage": {
"startUtc": "2026-04-15T12:00:00Z",
"endUtc": "2026-04-15T12:00:00Z"
}
}
],
"cells": [
{
"id": "example",
"laneId": "example",
"windowId": "example",
"rosterId": "example",
"stopped": true,
"holder": {
"userId": "example",
"externalUser": {
"email": "example",
"name": "example"
},
"turn": 1,
"waiting": true,
"since": "2026-04-15T12:00:00Z",
"until": "2026-04-15T12:00:00Z",
"skippedForAbsence": [
{
"userId": "example",
"from": "2026-04-15T12:00:00Z",
"until": "2026-04-15T12:00:00Z",
"source": "example"
}
]
}
}
],
"rosters": [
{
"rosterId": "example",
"order": [
{
"userId": "example",
"userGroupId": "example",
"turn": 1
}
],
"nextTurn": 1
}
],
"onDutyUserGroupId": "example",
"onCall": {
"externalAssignments": {
"asOfUtc": "2026-04-15T12:00:00Z",
"state": "example",
"coverage": "example",
"lastSuccessfulReadUtc": "2026-04-15T12:00:00Z",
"lastReadFailureUtc": "2026-04-15T12:00:00Z",
"knownFromUtc": "2026-04-15T12:00:00Z",
"knownUntilUtc": "2026-04-15T12:00:00Z",
"current": [
{
"localAssignmentId": "example",
"cellId": "example",
"user": "example",
"externalName": "example",
"externalEmail": "example",
"sinceUtc": "2026-04-15T12:00:00Z",
"untilUtc": "2026-04-15T12:00:00Z",
"externalAssignment": {
"memberKind": "example",
"participantId": "example",
"sourceType": "example",
"rotationId": "example",
"shiftId": "example",
"overrideId": "example",
"originalStartUtc": "2026-04-15T12:00:00Z",
"originalEndUtc": "2026-04-15T12:00:00Z"
}
}
],
"requiredAssignmentCount": 1,
"assignedAssignmentCount": 1,
"lastReadFailure": "example",
"distinctResponderCount": 1,
"distinctParticipantCount": 1,
"unmappedAssignmentCount": 1,
"emptyAssignmentCount": 1,
"nextChange": {
"atUtc": "2026-04-15T12:00:00Z",
"starting": [
{
"localAssignmentId": "example",
"cellId": "example",
"user": "example",
"externalName": "example",
"externalEmail": "example",
"sinceUtc": "2026-04-15T12:00:00Z",
"untilUtc": "2026-04-15T12:00:00Z",
"externalAssignment": {
"memberKind": "example",
"participantId": "example",
"sourceType": "example",
"rotationId": "example",
"shiftId": "example",
"overrideId": "example",
"originalStartUtc": "2026-04-15T12:00:00Z",
"originalEndUtc": "2026-04-15T12:00:00Z"
}
}
],
"continuing": [
{
"localAssignmentId": "example",
"cellId": "example",
"user": "example",
"externalName": "example",
"externalEmail": "example",
"sinceUtc": "2026-04-15T12:00:00Z",
"untilUtc": "2026-04-15T12:00:00Z",
"externalAssignment": {
"memberKind": "example",
"participantId": "example",
"sourceType": "example",
"rotationId": "example",
"shiftId": "example",
"overrideId": "example",
"originalStartUtc": "2026-04-15T12:00:00Z",
"originalEndUtc": "2026-04-15T12:00:00Z"
}
}
],
"ending": [
{
"localAssignmentId": "example",
"cellId": "example",
"user": "example",
"externalName": "example",
"externalEmail": "example",
"sinceUtc": "2026-04-15T12:00:00Z",
"untilUtc": "2026-04-15T12:00:00Z",
"externalAssignment": {
"memberKind": "example",
"participantId": "example",
"sourceType": "example",
"rotationId": "example",
"shiftId": "example",
"overrideId": "example",
"originalStartUtc": "2026-04-15T12:00:00Z",
"originalEndUtc": "2026-04-15T12:00:00Z"
}
}
]
}
},
"rotationId": "example",
"onCall": [
{
"userId": "example",
"turn": 1,
"cellId": "example",
"laneId": "example",
"laneName": "example",
"windowId": "example",
"windowName": "example",
"since": "2026-04-15T12:00:00Z",
"until": "2026-04-15T12:00:00Z",
"skippedForAbsence": [
{
"userId": "example",
"from": "2026-04-15T12:00:00Z",
"until": "2026-04-15T12:00:00Z",
"source": "example"
}
]
}
],
"unmappedExternalDuty": {
"email": "example",
"name": "example",
"until": "2026-04-15T12:00:00Z"
},
"users": [
{
"id": "example",
"teamId": "example",
"displayName": "example",
"realName": "example",
"email": "example",
"isGuest": true,
"isExternal": true,
"isDeleted": true,
"avatarUrl": "example",
"timeZone": "example"
}
]
},
"createdAt": "2026-04-15T12:00:00Z",
"updatedAt": "2026-04-15T12:00:00Z"
}

Created

Media typeapplication/json

A rotation: the grid of duties it runs, who is in it, and who holds each place right now.

object
id
string
name
string
description
null | string
code

The short code the rotation answers to in Slack.

null | string
enabled
boolean
public

Whether every member of the workspace can see the rotation.

boolean
mode

manual, auto or external.

string
runningLanes

How many lanes are running, which is how many people hold the shift at once.

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

How many places on this rotation have somebody on call right now.

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

How many places on this rotation have nobody on them at all.

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

Whether this rotation has more than one place to be on duty at once.

boolean
doubleCover

Whether one person may hold two places at once when the list behind them is too short to fill both.

boolean
skipsAreOffTime

Whether days the cadence skips — weekends, national holidays — count as time the rotation is not covered. One switch for the whole rotation, read into every window’s own reading.

boolean
owners
Array<string>
channels

The channels this rotation posts in: the same facts as channelSettings, flattened to the ids that receive duty messages.

Array<string>
channelSettings

Every channel this rotation uses, each saying what Round Robin does in it. Write one with PUT /v1/rotations/{rotationId}/channels/{channelId}.

Array<object>

One Slack channel a rotation uses, and what Round Robin does in it.

object
channelId

The Slack channel id, for example C0123456789.

string
postsMessages

Whether hand-overs, reminders and mentions of this rotation are posted in this channel.

boolean
syncsTopic

Whether this channel’s topic is rewritten with whoever is on duty when duty changes hands. Reported as false on every channel when enableTopic is off, whatever the channel itself holds, so a reader never has to combine two fields to know whether a topic is written.

boolean
enableTopic

Whether the rotation keeps channel topics pointed at whoever is on duty. The switch for the whole rotation: with it off, no channel’s topic is written whatever its own syncsTopic says.

boolean
topicTemplate

The template the topic is written from, or null to use Round Robin’s own wording. Shares its placeholders with the message templates, which are not part of this version.

null | string
members

Everybody in the rotation: the union across every list it draws on.

object
users

Members added individually.

Array<object>

One member of a rotation.

object
userId
string
sequenceNumber

Where the member sits in the order the lists are built from.

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

Members that came in through a Slack user group, listed under the group they came from. Add or remove somebody in Slack’s group and the rotation follows.

Array<object>

A Slack user group in the rotation, and the people currently in it.

object
userGroupId
string
users
Array<object>

One member of a rotation.

object
userId
string
sequenceNumber

Where the member sits in the order the lists are built from.

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

The rows of the grid: the kinds of duty this rotation runs.

Array<object>

A row of the grid: a kind of duty, such as primary or backend.

object
id
string
name

What the customer calls this lane, and absent until they name it.

null | string
windows

The columns of the grid: covered hours, a time zone, and a handover cadence.

Array<object>

A column of the grid: when the rotation is covered, and how often duty changes hands there.

object
id
string
name
null | string
timeZone

The one zone this window’s covered hours are expressed in, as its own identifier (Europe/Rome). Never rewritten to the tzdb canonical name a linked city shares. Empty on a manual rotation, which has no cadence and so no zone to express one in.

string
coverage

The repeating week this window is open for, as stored. Empty means the whole week.

Array<object>

One stretch of the coverage week, which may run past midnight into the next day.

object
startDay

A weekday name, monday through sunday.

string
startTime

A time of day, HH:mm.

string
endDay
string
endTime
string
alwaysOpen

Whether the window restricts nothing: open the whole week, every week.

boolean
cadence

How often duty changes hands in this column.

object
type

One of dayOfWeek, daysOfWeekList, dayOfMonth, nthWeekdayOfMonth, daily, workday, manual or external. Fields that mean nothing for the type are absent rather than defaulted, so test for presence rather than for a zero.

string
timeZone

The time zone the cadence was configured with, as its own identifier (Europe/Rome). Never rewritten to the tzdb canonical name a linked city shares.

string
startsAt

When the cadence starts, in its own time zone.

string format: date-time
period

How many units of the cadence’s own kind sit between two hand-overs.

integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
skipWeekends
boolean
skipNationalHolidays
boolean
countryCodes

The countries whose weekends and holidays the skips are read from.

Array<string>
nextEvents

The next hand-overs this window has already scheduled. For anything further out, ask for a window from GET /v1/rotations/{rotationId}/schedule.

Array<string>
dayOfWeek

Set for dayOfWeek and nthWeekdayOfMonth.

null | string
daysOfWeek

Set for daysOfWeekList.

Array<string> | null
dayOfMonth

Set for dayOfMonth.

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

Set for nthWeekdayOfMonth: which occurrences in the month, 1 through 5.

Array<integer | string> | null
times

Set for daily: the times of day it hands over at.

Array<string> | null
partner

Set for external: which partner drives the rotation. One of pagerDuty, jsm for Jira Service Management, or none when the rotation is external but no partner is linked yet.

null | string
openNow

Whether the window is open at the moment of the response.

boolean
currentCoverage
One of:
null
nextCoverage
One of:
null
cells

Every lane crossed with every window, and whoever holds each one.

Array<object>

A lane crossed with a window: one place somebody can be on duty.

object
id
string
laneId
string
windowId
string
rosterId

Which list this place is dealt from, matching an entry in the rotation’s rosters.

null | string
stopped

Whether this place is defined but not running, because the workspace is not on a paid plan.

boolean
holder
One of:
null
rosters

The lists this rotation deals from, each in the order this rotation deals it.

Array<object>

A list this rotation deals from, in the order this rotation deals it.

object
rosterId

The list this order belongs to. Absent on a rotation whose people are not on a shared list yet.

null | string
order

The turn order: who is dealt, and in what order.

Array<object>

One position in a turn order.

object
userId
string
userGroupId

Set when the member is in the order by way of a user group.

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

The turn the next handover deals from.

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

The Slack user group kept in sync with whoever is on call.

null | string
onCall

The on-call resource, embedded, so the common question needs no second call.

object
externalAssignments
One of:
null
rotationId

Rotation ID.

string
onCall

Current mapped Slack assignments. An empty list does not establish a coverage gap: check externalAssignments or unmappedExternalDuty.

Array<object>

One current mapped Slack assignment. A user can appear more than once when they hold multiple assignments.

object
userId

Slack user ID of the person on duty.

string
turn

Position in the native turn order. External assignments have no native turn order.

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

Cell holding this assignment.

null | string
laneId

Lane containing the cell. Lanes for external assignments do not identify provider roles.

null | string
laneName

Lane name, when available.

null | string
windowId

Window containing the cell. External assignment windows do not define provider recurrence.

null | string
windowName

Window name, when available.

null | string
since

Assignment start in UTC, when known.

null | string format: date-time
until

Exclusive assignment end in UTC, when known.

null | string format: date-time
skippedForAbsence

Members skipped for absence when this assignment began.

Array<object>

A member skipped because of an absence at assignment time.

object
userId

Slack user ID of the skipped member.

string
from

Absence start in UTC, when known.

null | string format: date-time
until

Absence end in UTC, when known.

null | string format: date-time
source

Absence source: googleCalendar or internalAvailability. Null when the source is unknown.

null | string
unmappedExternalDuty
One of:
null
users

Directory records for current mapped Slack users and skipped members when ?expand=users is requested. Null otherwise. Expansion disables ETag validation.

Array<object> | null

A Slack user, as Round Robin knows them.

object
id

The Slack user id.

string
teamId

The workspace that owns this person’s directory entry, which under Enterprise Grid can differ from the calling key’s workspace.

string
displayName
null | string
realName
null | string
email

Null when the install was never granted users:read.email, not only when unknown.

null | string
isGuest
boolean
isExternal

A Slack Connect person: visible through a shared channel, never a member of the workspace.

boolean
isDeleted

Deactivated in Slack, or gone from the workspace. These people are still answered rather than hidden, so an id a rotation still names resolves to something you can render.

boolean
avatarUrl

The person’s Slack avatar, at 192px.

null | string
timeZone

The person’s Slack time zone, as an IANA identifier (Europe/Rome). Absent when Round Robin has not seen one for them.

null | string
createdAt
string format: date-time
updatedAt
string format: date-time
Examplegenerated
{
"id": "example",
"name": "example",
"description": "example",
"code": "example",
"enabled": true,
"public": true,
"mode": "example",
"runningLanes": 1,
"heldSeats": 1,
"openSeats": 1,
"isGrid": true,
"doubleCover": true,
"skipsAreOffTime": true,
"owners": [
"example"
],
"channels": [
"example"
],
"channelSettings": [
{
"channelId": "example",
"postsMessages": true,
"syncsTopic": true
}
],
"enableTopic": true,
"topicTemplate": "example",
"members": {
"users": [
{
"userId": "example",
"sequenceNumber": 1
}
],
"userGroups": [
{
"userGroupId": "example",
"users": [
{
"userId": "example",
"sequenceNumber": 1
}
]
}
]
},
"lanes": [
{
"id": "example",
"name": "example"
}
],
"windows": [
{
"id": "example",
"name": "example",
"timeZone": "example",
"coverage": [
{
"startDay": "example",
"startTime": "example",
"endDay": "example",
"endTime": "example"
}
],
"alwaysOpen": true,
"cadence": {
"type": "example",
"timeZone": "example",
"startsAt": "2026-04-15T12:00:00Z",
"period": 1,
"skipWeekends": true,
"skipNationalHolidays": true,
"countryCodes": [
"example"
],
"nextEvents": [
"2026-04-15T12:00:00Z"
],
"dayOfWeek": "example",
"daysOfWeek": [
"example"
],
"dayOfMonth": 1,
"weeksInMonth": [
1
],
"times": [
"example"
],
"partner": "example"
},
"openNow": true,
"currentCoverage": {
"startUtc": "2026-04-15T12:00:00Z",
"endUtc": "2026-04-15T12:00:00Z"
},
"nextCoverage": {
"startUtc": "2026-04-15T12:00:00Z",
"endUtc": "2026-04-15T12:00:00Z"
}
}
],
"cells": [
{
"id": "example",
"laneId": "example",
"windowId": "example",
"rosterId": "example",
"stopped": true,
"holder": {
"userId": "example",
"externalUser": {
"email": "example",
"name": "example"
},
"turn": 1,
"waiting": true,
"since": "2026-04-15T12:00:00Z",
"until": "2026-04-15T12:00:00Z",
"skippedForAbsence": [
{
"userId": "example",
"from": "2026-04-15T12:00:00Z",
"until": "2026-04-15T12:00:00Z",
"source": "example"
}
]
}
}
],
"rosters": [
{
"rosterId": "example",
"order": [
{
"userId": "example",
"userGroupId": "example",
"turn": 1
}
],
"nextTurn": 1
}
],
"onDutyUserGroupId": "example",
"onCall": {
"externalAssignments": {
"asOfUtc": "2026-04-15T12:00:00Z",
"state": "example",
"coverage": "example",
"lastSuccessfulReadUtc": "2026-04-15T12:00:00Z",
"lastReadFailureUtc": "2026-04-15T12:00:00Z",
"knownFromUtc": "2026-04-15T12:00:00Z",
"knownUntilUtc": "2026-04-15T12:00:00Z",
"current": [
{
"localAssignmentId": "example",
"cellId": "example",
"user": "example",
"externalName": "example",
"externalEmail": "example",
"sinceUtc": "2026-04-15T12:00:00Z",
"untilUtc": "2026-04-15T12:00:00Z",
"externalAssignment": {
"memberKind": "example",
"participantId": "example",
"sourceType": "example",
"rotationId": "example",
"shiftId": "example",
"overrideId": "example",
"originalStartUtc": "2026-04-15T12:00:00Z",
"originalEndUtc": "2026-04-15T12:00:00Z"
}
}
],
"requiredAssignmentCount": 1,
"assignedAssignmentCount": 1,
"lastReadFailure": "example",
"distinctResponderCount": 1,
"distinctParticipantCount": 1,
"unmappedAssignmentCount": 1,
"emptyAssignmentCount": 1,
"nextChange": {
"atUtc": "2026-04-15T12:00:00Z",
"starting": [
{
"localAssignmentId": "example",
"cellId": "example",
"user": "example",
"externalName": "example",
"externalEmail": "example",
"sinceUtc": "2026-04-15T12:00:00Z",
"untilUtc": "2026-04-15T12:00:00Z",
"externalAssignment": {
"memberKind": "example",
"participantId": "example",
"sourceType": "example",
"rotationId": "example",
"shiftId": "example",
"overrideId": "example",
"originalStartUtc": "2026-04-15T12:00:00Z",
"originalEndUtc": "2026-04-15T12:00:00Z"
}
}
],
"continuing": [
{
"localAssignmentId": "example",
"cellId": "example",
"user": "example",
"externalName": "example",
"externalEmail": "example",
"sinceUtc": "2026-04-15T12:00:00Z",
"untilUtc": "2026-04-15T12:00:00Z",
"externalAssignment": {
"memberKind": "example",
"participantId": "example",
"sourceType": "example",
"rotationId": "example",
"shiftId": "example",
"overrideId": "example",
"originalStartUtc": "2026-04-15T12:00:00Z",
"originalEndUtc": "2026-04-15T12:00:00Z"
}
}
],
"ending": [
{
"localAssignmentId": "example",
"cellId": "example",
"user": "example",
"externalName": "example",
"externalEmail": "example",
"sinceUtc": "2026-04-15T12:00:00Z",
"untilUtc": "2026-04-15T12:00:00Z",
"externalAssignment": {
"memberKind": "example",
"participantId": "example",
"sourceType": "example",
"rotationId": "example",
"shiftId": "example",
"overrideId": "example",
"originalStartUtc": "2026-04-15T12:00:00Z",
"originalEndUtc": "2026-04-15T12:00:00Z"
}
}
]
}
},
"rotationId": "example",
"onCall": [
{
"userId": "example",
"turn": 1,
"cellId": "example",
"laneId": "example",
"laneName": "example",
"windowId": "example",
"windowName": "example",
"since": "2026-04-15T12:00:00Z",
"until": "2026-04-15T12:00:00Z",
"skippedForAbsence": [
{
"userId": "example",
"from": "2026-04-15T12:00:00Z",
"until": "2026-04-15T12:00:00Z",
"source": "example"
}
]
}
],
"unmappedExternalDuty": {
"email": "example",
"name": "example",
"until": "2026-04-15T12:00:00Z"
},
"users": [
{
"id": "example",
"teamId": "example",
"displayName": "example",
"realName": "example",
"email": "example",
"isGuest": true,
"isExternal": true,
"isDeleted": true,
"avatarUrl": "example",
"timeZone": "example"
}
]
},
"createdAt": "2026-04-15T12:00:00Z",
"updatedAt": "2026-04-15T12:00:00Z"
}

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"
}

Payment Required

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"
}

Conflict

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"
}

Precondition Required

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"
}