On-Call History
Fetch the record of who was on call for one of your schedules, span by span, after all overrides have been applied. Use it to calculate hours for payroll, audits, or any system that tracks who was responsible during an incident window.
Request
GET https://api.oppex.ai/client/oncall/public/history
Send your API key in the X-API-KEY header.
curl -G "https://api.oppex.ai/client/oncall/public/history" \
-H "X-API-KEY: <your-api-key>" \
--data-urlencode "scheduleId=42" \
--data-urlencode "startTime=2026-10-01T00:00:00Z" \
--data-urlencode "endTime=2026-10-31T00:00:00Z"
To get history for a single user only, add the userId parameter:
curl -G "https://api.oppex.ai/client/oncall/public/history" \
-H "X-API-KEY: <your-api-key>" \
--data-urlencode "scheduleId=42" \
--data-urlencode "startTime=2026-10-01T00:00:00Z" \
--data-urlencode "endTime=2026-10-31T00:00:00Z" \
--data-urlencode "userId=1001"
Query parameters
| Parameter | Required | Description |
|---|---|---|
scheduleId | Yes | The numeric id of the schedule. Use List On-Call Schedules to find it. |
startTime | Yes | Start of the range (inclusive), as an ISO-8601 timestamp with a timezone offset, such as 2026-10-01T00:00:00Z or 2026-10-01T05:30:00+05:30. A space is read as +, so URL-encode the offset. |
endTime | Yes | End of the range (exclusive), same format as startTime. Must be after startTime. |
userId | No | Numeric user id. When given, only spans where that user is on call are returned. Omit to get all spans. |
The range must not exceed 93 days. Request a shorter window and repeat if you need more.
Response
{
"success": true,
"statusCode": 200,
"message": "On-call history retrieved successfully",
"data": {
"items": [
{
"startTime": "2026-10-01T00:00:00Z",
"endTime": "2026-10-08T00:00:00Z",
"userId": 1001,
"userName": "Alice Chen",
"onCallUsers": [
{ "userId": 1001, "userName": "Alice Chen" }
],
"assignmentMode": "SCHEDULE_ROTATION",
"overrideActive": false,
"shortSpan": false
},
{
"startTime": "2026-10-08T00:00:00Z",
"endTime": "2026-10-15T00:00:00Z",
"userId": 1002,
"userName": "Raj Patel",
"onCallUsers": [
{ "userId": 1002, "userName": "Raj Patel" }
],
"assignmentMode": "SCHEDULE_ROTATION",
"overrideActive": false,
"shortSpan": false
}
],
"truncated": false
}
}
Response fields
| Field | Description |
|---|---|
items | Ordered list of on-call spans from earliest to latest. |
items[].startTime | Start of the span, UTC, whole seconds. |
items[].endTime | End of the span, UTC, exclusive. One span ends exactly where the next begins. |
items[].userId | The nominal on-call user. Null when nobody is on call (a gap between shifts). |
items[].userName | Display name of the nominal on-call user. |
items[].onCallUsers | Everyone on call during this span. Contains more than one entry only when the schedule layer uses Notify All mode, where the entire roster is on call at once. |
items[].assignmentMode | SCHEDULE_ROTATION, NOTIFY_ALL, or DISTRIBUTED_EVENLY. |
items[].overrideActive | true when an override changed who is on call for this span. |
items[].shortSpan | true when the span is under a minute. This is real on-call time — flagged rather than dropped, typically caused by a schedule edit at a handover boundary. |
truncated | true when the server stopped before the end of the range. Split the request into smaller windows and repeat. |
Calculating on-call hours
Sum endTime − startTime per userId across onCallUsers. When a span has truncated: true (i.e. the parent response is truncated), that span's duration is incomplete — request the remainder of the range to complete the calculation.
# Total seconds on call for each user in October 2026
curl -s -G "https://api.oppex.ai/client/oncall/public/history" \
-H "X-API-KEY: <your-api-key>" \
--data-urlencode "scheduleId=42" \
--data-urlencode "startTime=2026-10-01T00:00:00Z" \
--data-urlencode "endTime=2026-11-01T00:00:00Z" \
| jq '[
.data.items[] | . as $span | .onCallUsers[]
| {userId, userName, start: $span.startTime, end: $span.endTime}
]
| group_by(.userId)
| map({
userId: .[0].userId,
userName: .[0].userName,
totalSeconds: (map(
((.end | fromdateiso8601) - (.start | fromdateiso8601))
) | add)
})'
Because onCallUsers at the span level doesn't carry startTime/endTime, use the parent span's times when computing per-user totals for Notify All schedules — every user in onCallUsers was on call for the whole span.
Errors
| Status | When |
|---|---|
400 | A parameter is invalid — scheduleId missing, times missing or malformed, range exceeds 93 days, or startTime is not before endTime. The message says which. |
401 | The API key is missing, wrong, or disabled. See API Keys. |
404 | The schedule doesn't exist, or the API key doesn't have access to it. |
500 | Something went wrong in Oppex. Retry. If it keeps happening, contact support with the message. |