> For the complete documentation index, see [llms.txt](https://docs.spike.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.spike.sh/spike-api-docs/on-call.md).

# On-call

Create, read and update on-call schedules, layers and shifts.

## Fetch on-calls

> Get list of on-call schedules for a team

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[],"TeamIdHeader":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls":{"get":{"operationId":"getOnCalls","summary":"Fetch on-calls","description":"Get list of on-call schedules for a team","tags":["On-call"],"responses":{"200":{"description":"An envelope of `{ oncalls, oncallReportsFeatureFlag }` — NOT a bare array.","content":{"application/json":{"schema":{"type":"object","properties":{"oncalls":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"},"teams":{"type":"array","items":{"type":"string"}},"users":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"role":{"type":"string"},"timezone":{"type":"string"}}}},"shifts":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"oncall":{"type":"string"},"index":{"type":"number"},"layerIndex":{"type":"number"},"layer":{"type":"string"},"org":{"type":"string"},"active":{"type":"boolean"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"_id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"role":{"type":"string"},"timezone":{"type":"string"}}},"isGap":{"type":"boolean"}}}},"active":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}}},"oncallReportsFeatureFlag":{"type":"boolean","description":"Whether this organisation's plan includes on-call report downloads."}}}}}},"400":{"description":"API-key callers must supply a valid `x-team-id` header — on-calls are listed per team. Undocumented until now.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in your Spike dashboard under Settings > API."},{"name":"x-team-id","in":"header","required":true,"schema":{"type":"string"},"description":"The team's `_id` — a 24-character hex ObjectId, NOT the short `uid` slug. Read it from `GET /teams/get-all-teams`."}]}}}}
```

## Create an on-call

> Create an on-call schedule.\
> \
> \*\*Common case:\*\* send \`users\` plus a top-level \`rotation\`. Spike creates one\
> primary layer and derives the shift order from the order of \`users\`.\
> \
> \*\*Advanced:\*\* send a \`layers\` array instead to create multiple escalation tiers\
> in one call.\
> \
> The \`x-team-id\` header is REQUIRED and sets which team the on-call belongs to.\
> \
> \---\
> \
> \*\*Custom slots (optional):\*\* restrict when this layer is on-call.\
> \
> \- \`customiseFor: "day"\` with \`fromTime\`/\`toTime\` for a single daily window.\
> \- \`customiseFor: "week"\` with a \`conditions\` array to pick \*\*specific days\*\* (one\
> &#x20; entry per day window — add as many as you need).\
> \
> Times are \`HH:mm\` (24-hour) in the schedule timezone; days are lowercase names.\
> \
> \`\`\`json\
> &#x20;       {\
> &#x20;         "customiseFor": "week",\
> &#x20;         "conditions": \[\
> &#x20;           { "fromDayOfWeek": "monday",    "fromTime": "00:00", "toDayOfWeek": "tuesday",   "toTime": "00:00" },\
> &#x20;           { "fromDayOfWeek": "tuesday",   "fromTime": "00:00", "toDayOfWeek": "wednesday", "toTime": "00:00" },\
> &#x20;           { "fromDayOfWeek": "wednesday", "fromTime": "00:00", "toDayOfWeek": "thursday",  "toTime": "00:00" }\
> &#x20;         ]\
> &#x20;       }\
> \`\`\`

````json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[],"TeamIdHeader":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls":{"post":{"operationId":"postOnCalls","summary":"Create an on-call","description":"Create an on-call schedule.\n\n**Common case:** send `users` plus a top-level `rotation`. Spike creates one\nprimary layer and derives the shift order from the order of `users`.\n\n**Advanced:** send a `layers` array instead to create multiple escalation tiers\nin one call.\n\nThe `x-team-id` header is REQUIRED and sets which team the on-call belongs to.\n\n---\n\n**Custom slots (optional):** restrict when this layer is on-call.\n\n- `customiseFor: \"day\"` with `fromTime`/`toTime` for a single daily window.\n- `customiseFor: \"week\"` with a `conditions` array to pick **specific days** (one\n  entry per day window — add as many as you need).\n\nTimes are `HH:mm` (24-hour) in the schedule timezone; days are lowercase names.\n\n```json\n        {\n          \"customiseFor\": \"week\",\n          \"conditions\": [\n            { \"fromDayOfWeek\": \"monday\",    \"fromTime\": \"00:00\", \"toDayOfWeek\": \"tuesday\",   \"toTime\": \"00:00\" },\n            { \"fromDayOfWeek\": \"tuesday\",   \"fromTime\": \"00:00\", \"toDayOfWeek\": \"wednesday\", \"toTime\": \"00:00\" },\n            { \"fromDayOfWeek\": \"wednesday\", \"fromTime\": \"00:00\", \"toDayOfWeek\": \"thursday\",  \"toTime\": \"00:00\" }\n          ]\n        }\n```","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in the Spike dashboard under Settings → API."},{"name":"x-team-id","in":"header","required":true,"schema":{"type":"string"},"description":"REQUIRED to create an on-call — the id of the team the on-call will belong to. Must be a team in your organisation."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"description":"Provide top-level `users` + `rotation` (one primary layer) OR a `layers` array (takes precedence when present).","properties":{"name":{"type":"string","description":"Display name for the on-call."},"users":{"type":"array","minItems":1,"description":"Rotation members for the primary layer, in rotation order.","items":{"type":"string","description":"A user ObjectId from your organisation."}},"rotation":{"type":"object","required":["length","unit"],"description":"How often the rotation moves to the next user.","properties":{"length":{"type":"integer","minimum":1,"description":"How many `unit`s each person is on call before handing off (e.g. 2 with unit days = a 2-day shift)."},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"],"description":"Time unit for the rotation length. Singular or plural and any case are accepted (day, Days, WEEK) and normalised."},"handoff":{"type":"string","pattern":"^\\d{2}:\\d{2}$","default":"00:00","description":"Time of day the shift hands off, 24-hour HH:mm, interpreted in the schedule timezone. Defaults to 00:00."},"handoffDay":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Day of the week the weekly handoff occurs. Only used when unit is weeks; ignored otherwise."}}},"timezone":{"type":"string","description":"IANA timezone for handoff times (e.g. America/New_York). Defaults to the owner timezone or Etc/UTC."},"desc":{"type":"string","description":"Optional description."},"layers":{"type":"array","description":"Optional explicit layers in priority order; each entry has its own users + rotation.","items":{"type":"object","required":["users","rotation"],"properties":{"users":{"type":"array","minItems":1,"description":"Rotation members for this layer, in order.","items":{"type":"string","description":"A user ObjectId from your organisation."}},"rotation":{"type":"object","required":["length","unit"],"description":"How often the rotation moves to the next user.","properties":{"length":{"type":"integer","minimum":1,"description":"How many `unit`s each person is on call before handing off (e.g. 2 with unit days = a 2-day shift)."},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"],"description":"Time unit for the rotation length. Singular or plural and any case are accepted (day, Days, WEEK) and normalised."},"handoff":{"type":"string","pattern":"^\\d{2}:\\d{2}$","default":"00:00","description":"Time of day the shift hands off, 24-hour HH:mm, interpreted in the schedule timezone. Defaults to 00:00."},"handoffDay":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Day of the week the weekly handoff occurs. Only used when unit is weeks; ignored otherwise."}}},"desc":{"type":"string","description":"Optional layer description."},"customTimings":{"type":["object","null"],"description":"Optional. Restrict WHEN this layer is on-call. Use customiseFor \"day\" for a single daily window (fromTime/toTime), or \"week\" for day-of-week windows (conditions[]). Times are \"HH:mm\" (24-hour) in the schedule timezone; day names are lowercase.","properties":{"customiseFor":{"type":"string","enum":["day","week"],"description":"\"day\" = one daily on-call window; \"week\" = per-day-of-week windows defined in conditions."},"fromTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Daily window start, HH:mm (used when customiseFor is \"day\")."},"toTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Daily window end, HH:mm (used when customiseFor is \"day\")."},"conditions":{"type":"array","description":"Weekly windows (used when customiseFor is \"week\").","items":{"type":"object","properties":{"fromDayOfWeek":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Window start day."},"fromTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Window start time, HH:mm."},"toDayOfWeek":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Window end day."},"toTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Window end time, HH:mm."}}}}}}}}},"customTimings":{"type":["object","null"],"description":"Optional. Restrict WHEN this layer is on-call. Use customiseFor \"day\" for a single daily window (fromTime/toTime), or \"week\" for day-of-week windows (conditions[]). Times are \"HH:mm\" (24-hour) in the schedule timezone; day names are lowercase.","properties":{"customiseFor":{"type":"string","enum":["day","week"],"description":"\"day\" = one daily on-call window; \"week\" = per-day-of-week windows defined in conditions."},"fromTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Daily window start, HH:mm (used when customiseFor is \"day\")."},"toTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Daily window end, HH:mm (used when customiseFor is \"day\")."},"conditions":{"type":"array","description":"Weekly windows (used when customiseFor is \"week\").","items":{"type":"object","properties":{"fromDayOfWeek":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Window start day."},"fromTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Window start time, HH:mm."},"toDayOfWeek":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Window end day."},"toTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Window end time, HH:mm."}}}}}}}}}}},"responses":{"201":{"description":"On-call created; the full on-call is returned","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"},"teams":{"type":"array","items":{"type":"string"}},"layers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Layer `_id`."},"index":{"type":"number"},"rotation":{"type":"object","properties":{"length":{"type":"number"},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"]},"handoff":{"type":"string"},"handoffDay":{"type":["string","null"]}}},"users":{"type":"array","items":{"type":"string"}},"customTimings":{"type":["object","null"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Validation failed, or `x-team-id` was missing/invalid (required for create).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"No owner/team could be resolved for this organisation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````

## Fetch an on-call

> Get details about a specific on-call including layers, shifts and users

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[],"TeamIdHeader":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/{oncallId}":{"get":{"operationId":"getOnCallsByOncallId","summary":"Fetch an on-call","description":"Get details about a specific on-call including layers, shifts and users","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in your Spike dashboard under Settings > API."},{"name":"x-team-id","in":"header","required":true,"schema":{"type":"string"},"description":"The team's `_id` — a 24-character hex ObjectId, NOT the short `uid` slug. Read it from `GET /teams/get-all-teams`."},{"name":"oncallId","in":"path","required":true,"schema":{"type":"string"},"description":"oncallId parameter"},{"name":"from","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"API-key callers get `{ oncall }`. A missing or non-owned schedule still answers `200` with `{\"oncall\": {}}` rather than `404` — treat an empty object as not-found. An `{oncallId}` that is not a valid ObjectId answers `400` with an empty body.","content":{"application/json":{"schema":{"type":"object","properties":{"oncall":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"},"teams":{"type":"array","items":{"type":"string"}},"users":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"role":{"type":"string"},"timezone":{"type":"string"}}}},"activeShift":{"type":["object","null"]},"renderedShifts":{"type":"array","items":{"type":"object"}}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Returned only for session callers. API-key callers receive `200 {\"oncall\": {}}`.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}}}}}}}}}}}
```

## Edit an on-call

> Update an on-call's top-level properties (name and/or team). Layers are not changed here — use the layer endpoints to edit rotations. Send only the fields you want to change.

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/{oncallId}":{"put":{"operationId":"putOnCallsByOncallId","summary":"Edit an on-call","description":"Update an on-call's top-level properties (name and/or team). Layers are not changed here — use the layer endpoints to edit rotations. Send only the fields you want to change.","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in the Spike dashboard under Settings → API."},{"name":"x-team-id","in":"header","required":false,"schema":{"type":"string"},"description":"Optional. Team to scope the request to. Defaults to your organisation's default team when omitted."},{"name":"oncallId","in":"path","required":true,"schema":{"type":"string"},"description":"The on-call ObjectId."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"properties":{"name":{"type":"string","description":"New display name."},"teamId":{"type":"string","description":"Move the on-call to this team (ObjectId)."}}}}}},"responses":{"200":{"description":"On-call updated; the full on-call is returned","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"},"teams":{"type":"array","items":{"type":"string"}},"layers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Layer `_id`."},"index":{"type":"number"},"rotation":{"type":"object","properties":{"length":{"type":"number"},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"]},"handoff":{"type":"string"},"handoffDay":{"type":["string","null"]}}},"users":{"type":"array","items":{"type":"string"}},"customTimings":{"type":["object","null"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"On-call not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Add a layer

> Add a new layer (rotation) to an on-call.\
> \
> It's appended after the existing layers as the next escalation tier; shifts are\
> derived from the order of \`users\`. Returns the full updated on-call.\
> \
> \---\
> \
> \*\*Custom timings (optional):\*\* restrict when this layer is on-call.\
> \
> \- \`customiseFor: "day"\` with \`fromTime\`/\`toTime\` for a single daily window.\
> \- \`customiseFor: "week"\` with a \`conditions\` array to pick \*\*specific days\*\* (one\
> &#x20; entry per day window — add as many as you need).\
> \
> Times are \`HH:mm\` (24-hour) in the schedule timezone; days are lowercase names.\
> \
> \`\`\`json\
> &#x20;       {\
> &#x20;         "customiseFor": "week",\
> &#x20;         "conditions": \[\
> &#x20;           { "fromDayOfWeek": "monday",    "fromTime": "00:00", "toDayOfWeek": "tuesday",   "toTime": "00:00" },\
> &#x20;           { "fromDayOfWeek": "tuesday",   "fromTime": "00:00", "toDayOfWeek": "wednesday", "toTime": "00:00" },\
> &#x20;           { "fromDayOfWeek": "wednesday", "fromTime": "00:00", "toDayOfWeek": "thursday",  "toTime": "00:00" }\
> &#x20;         ]\
> &#x20;       }\
> \`\`\`

````json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/{oncallId}/layers":{"post":{"operationId":"postOnCallsByOncallIdLayers","summary":"Add a layer","description":"Add a new layer (rotation) to an on-call.\n\nIt's appended after the existing layers as the next escalation tier; shifts are\nderived from the order of `users`. Returns the full updated on-call.\n\n---\n\n**Custom timings (optional):** restrict when this layer is on-call.\n\n- `customiseFor: \"day\"` with `fromTime`/`toTime` for a single daily window.\n- `customiseFor: \"week\"` with a `conditions` array to pick **specific days** (one\n  entry per day window — add as many as you need).\n\nTimes are `HH:mm` (24-hour) in the schedule timezone; days are lowercase names.\n\n```json\n        {\n          \"customiseFor\": \"week\",\n          \"conditions\": [\n            { \"fromDayOfWeek\": \"monday\",    \"fromTime\": \"00:00\", \"toDayOfWeek\": \"tuesday\",   \"toTime\": \"00:00\" },\n            { \"fromDayOfWeek\": \"tuesday\",   \"fromTime\": \"00:00\", \"toDayOfWeek\": \"wednesday\", \"toTime\": \"00:00\" },\n            { \"fromDayOfWeek\": \"wednesday\", \"fromTime\": \"00:00\", \"toDayOfWeek\": \"thursday\",  \"toTime\": \"00:00\" }\n          ]\n        }\n```","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in the Spike dashboard under Settings → API."},{"name":"x-team-id","in":"header","required":false,"schema":{"type":"string"},"description":"Optional. Team to scope the request to. Defaults to your organisation's default team when omitted."},{"name":"oncallId","in":"path","required":true,"schema":{"type":"string"},"description":"The on-call ObjectId."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["users","rotation"],"properties":{"users":{"type":"array","minItems":1,"description":"Rotation members for this layer, in order.","items":{"type":"string","description":"A user ObjectId from your organisation."}},"rotation":{"type":"object","required":["length","unit"],"description":"How often the rotation moves to the next user.","properties":{"length":{"type":"integer","minimum":1,"description":"How many `unit`s each person is on call before handing off (e.g. 2 with unit days = a 2-day shift)."},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"],"description":"Time unit for the rotation length. Singular or plural and any case are accepted (day, Days, WEEK) and normalised."},"handoff":{"type":"string","pattern":"^\\d{2}:\\d{2}$","default":"00:00","description":"Time of day the shift hands off, 24-hour HH:mm, interpreted in the schedule timezone. Defaults to 00:00."},"handoffDay":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Day of the week the weekly handoff occurs. Only used when unit is weeks; ignored otherwise."}}},"desc":{"type":"string","description":"Optional layer description."},"customTimings":{"type":["object","null"],"description":"Optional. Restrict WHEN this layer is on-call. Use customiseFor \"day\" for a single daily window (fromTime/toTime), or \"week\" for day-of-week windows (conditions[]). Times are \"HH:mm\" (24-hour) in the schedule timezone; day names are lowercase.","properties":{"customiseFor":{"type":"string","enum":["day","week"],"description":"\"day\" = one daily on-call window; \"week\" = per-day-of-week windows defined in conditions."},"fromTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Daily window start, HH:mm (used when customiseFor is \"day\")."},"toTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Daily window end, HH:mm (used when customiseFor is \"day\")."},"conditions":{"type":"array","description":"Weekly windows (used when customiseFor is \"week\").","items":{"type":"object","properties":{"fromDayOfWeek":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Window start day."},"fromTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Window start time, HH:mm."},"toDayOfWeek":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Window end day."},"toTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Window end time, HH:mm."}}}}}}}}}}},"responses":{"201":{"description":"Layer added; the full on-call is returned","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"},"teams":{"type":"array","items":{"type":"string"}},"layers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Layer `_id`."},"index":{"type":"number"},"rotation":{"type":"object","properties":{"length":{"type":"number"},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"]},"handoff":{"type":"string"},"handoffDay":{"type":["string","null"]}}},"users":{"type":"array","items":{"type":"string"}},"customTimings":{"type":["object","null"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"On-call not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````

## Edit a layer

> Send any subset of \`users\`, \`rotation\`, \`desc\`; omitted fields keep their\
> current values. When you send \`rotation\`, its fields merge onto the current\
> cadence (e.g. send only \`{ "rotation": { "length": 2 } }\` to change just the\
> length). Returns the full updated on-call.\
> \
> \---\
> \
> \*\*Custom slots (optional):\*\* restrict when this layer is on-call.\
> \
> \- \`customiseFor: "day"\` with \`fromTime\`/\`toTime\` for a single daily window.\
> \- \`customiseFor: "week"\` with a \`conditions\` array to pick \*\*specific days\*\* (one\
> &#x20; entry per day window — add as many as you need).\
> \
> Times are \`HH:mm\` (24-hour) in the schedule timezone; days are lowercase names.\
> \
> \`\`\`json\
> &#x20;       {\
> &#x20;         "customiseFor": "week",\
> &#x20;         "conditions": \[\
> &#x20;           { "fromDayOfWeek": "monday",    "fromTime": "00:00", "toDayOfWeek": "tuesday",   "toTime": "00:00" },\
> &#x20;           { "fromDayOfWeek": "tuesday",   "fromTime": "00:00", "toDayOfWeek": "wednesday", "toTime": "00:00" },\
> &#x20;           { "fromDayOfWeek": "wednesday", "fromTime": "00:00", "toDayOfWeek": "thursday",  "toTime": "00:00" }\
> &#x20;         ]\
> &#x20;       }\
> \`\`\`

````json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/{oncallId}/layers/{layerId}":{"put":{"operationId":"putOnCallsByOncallIdLayersByLayerId","summary":"Edit a layer","description":"Send any subset of `users`, `rotation`, `desc`; omitted fields keep their\ncurrent values. When you send `rotation`, its fields merge onto the current\ncadence (e.g. send only `{ \"rotation\": { \"length\": 2 } }` to change just the\nlength). Returns the full updated on-call.\n\n---\n\n**Custom slots (optional):** restrict when this layer is on-call.\n\n- `customiseFor: \"day\"` with `fromTime`/`toTime` for a single daily window.\n- `customiseFor: \"week\"` with a `conditions` array to pick **specific days** (one\n  entry per day window — add as many as you need).\n\nTimes are `HH:mm` (24-hour) in the schedule timezone; days are lowercase names.\n\n```json\n        {\n          \"customiseFor\": \"week\",\n          \"conditions\": [\n            { \"fromDayOfWeek\": \"monday\",    \"fromTime\": \"00:00\", \"toDayOfWeek\": \"tuesday\",   \"toTime\": \"00:00\" },\n            { \"fromDayOfWeek\": \"tuesday\",   \"fromTime\": \"00:00\", \"toDayOfWeek\": \"wednesday\", \"toTime\": \"00:00\" },\n            { \"fromDayOfWeek\": \"wednesday\", \"fromTime\": \"00:00\", \"toDayOfWeek\": \"thursday\",  \"toTime\": \"00:00\" }\n          ]\n        }\n```","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in the Spike dashboard under Settings → API."},{"name":"x-team-id","in":"header","required":false,"schema":{"type":"string"},"description":"Optional. Team to scope the request to. Defaults to your organisation's default team when omitted."},{"name":"oncallId","in":"path","required":true,"schema":{"type":"string"},"description":"The on-call ObjectId."},{"name":"layerId","in":"path","required":true,"schema":{"type":"string"},"description":"The layer ObjectId (from layers[].id)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"properties":{"users":{"type":"array","minItems":1,"description":"Replacement rotation members, in order (replaces the existing list).","items":{"type":"string","description":"A user ObjectId from your organisation."}},"rotation":{"type":"object","description":"How often the rotation moves to the next user.","properties":{"length":{"type":"integer","minimum":1,"description":"How many `unit`s each person is on call before handing off (e.g. 2 with unit days = a 2-day shift)."},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"],"description":"Time unit for the rotation length. Singular or plural and any case are accepted (day, Days, WEEK) and normalised."},"handoff":{"type":"string","pattern":"^\\d{2}:\\d{2}$","default":"00:00","description":"Time of day the shift hands off, 24-hour HH:mm, interpreted in the schedule timezone. Defaults to 00:00."},"handoffDay":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Day of the week the weekly handoff occurs. Only used when unit is weeks; ignored otherwise."}}},"desc":{"type":"string","description":"New layer description."},"customTimings":{"type":["object","null"],"description":"Optional. Restrict WHEN this layer is on-call. Use customiseFor \"day\" for a single daily window (fromTime/toTime), or \"week\" for day-of-week windows (conditions[]). Times are \"HH:mm\" (24-hour) in the schedule timezone; day names are lowercase.","properties":{"customiseFor":{"type":"string","enum":["day","week"],"description":"\"day\" = one daily on-call window; \"week\" = per-day-of-week windows defined in conditions."},"fromTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Daily window start, HH:mm (used when customiseFor is \"day\")."},"toTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Daily window end, HH:mm (used when customiseFor is \"day\")."},"conditions":{"type":"array","description":"Weekly windows (used when customiseFor is \"week\").","items":{"type":"object","properties":{"fromDayOfWeek":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Window start day."},"fromTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Window start time, HH:mm."},"toDayOfWeek":{"type":"string","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"description":"Window end day."},"toTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Window end time, HH:mm."}}}}}}}}}}},"responses":{"200":{"description":"Layer updated; the full on-call is returned","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"},"teams":{"type":"array","items":{"type":"string"}},"layers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Layer `_id`."},"index":{"type":"number"},"rotation":{"type":"object","properties":{"length":{"type":"number"},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"]},"handoff":{"type":"string"},"handoffDay":{"type":["string","null"]}}},"users":{"type":"array","items":{"type":"string"}},"customTimings":{"type":["object","null"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"On-call or layer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````

## Delete a layer

> Delete a layer from an on-call. An on-call must always keep at least one layer, so deleting the last remaining layer returns 409. Returns the full updated on-call.

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/{oncallId}/layers/{layerId}":{"delete":{"operationId":"deleteOnCallsByOncallIdLayersByLayerId","summary":"Delete a layer","description":"Delete a layer from an on-call. An on-call must always keep at least one layer, so deleting the last remaining layer returns 409. Returns the full updated on-call.","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in the Spike dashboard under Settings → API."},{"name":"x-team-id","in":"header","required":false,"schema":{"type":"string"},"description":"Optional. Team to scope the request to. Defaults to your organisation's default team when omitted."},{"name":"oncallId","in":"path","required":true,"schema":{"type":"string"},"description":"The on-call ObjectId."},{"name":"layerId","in":"path","required":true,"schema":{"type":"string"},"description":"The layer ObjectId (from layers[].id)."}],"responses":{"200":{"description":"Layer deleted; the full on-call is returned","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"},"teams":{"type":"array","items":{"type":"string"}},"layers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Layer `_id`."},"index":{"type":"number"},"rotation":{"type":"object","properties":{"length":{"type":"number"},"unit":{"type":"string","enum":["minutes","hours","days","weeks","months"]},"handoff":{"type":"string"},"handoffDay":{"type":["string","null"]}}},"users":{"type":"array","items":{"type":"string"}},"customTimings":{"type":["object","null"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"On-call or layer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"An on-call must always keep at least one layer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Who is on-call next

> Returns the next scheduled shift for an on-call, looking up to 7 days ahead of now (or of the schedule's \`scheduledStart\`, whichever is later). Returns an empty object \`{}\` when the on-call has no future shift in that window or the on-call does not exist. Responds \`400\` with an empty body when \`oncallId\` is not a valid ObjectId.

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/{oncallId}/who-is-on-call-next":{"get":{"operationId":"getOnCallsByOncallIdWhoIsOnCallNext","summary":"Who is on-call next","description":"Returns the next scheduled shift for an on-call, looking up to 7 days ahead of now (or of the schedule's `scheduledStart`, whichever is later). Returns an empty object `{}` when the on-call has no future shift in that window or the on-call does not exist. Responds `400` with an empty body when `oncallId` is not a valid ObjectId.","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in your Spike dashboard under Settings > API."},{"name":"x-team-id","in":"header","required":false,"schema":{"type":"string"},"description":"Optional. The team's `_id` — a 24-character hex ObjectId, NOT the short `uid` slug. Read it from `GET /teams/get-all-teams`."},{"name":"oncallId","in":"path","required":true,"schema":{"type":"string"},"description":"The on-call schedule's `_id` (24-char hex ObjectId)."}],"responses":{"200":{"description":"The next shift, or `{}` when there is none.","content":{"application/json":{"schema":{"type":"object","properties":{"nextShift":{"type":"object","description":"Absent when no future shift exists in the lookahead window.","properties":{"userId":{"type":"string"},"startTimeInUTC":{"type":"string","format":"date-time"},"endTimeInUTC":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"`oncallId` is not a valid ObjectId. The body is empty."},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Fetch the active shift

> Returns the shift that is currently active for an on-call schedule. \`activeShift\` is \`null\` when nobody is on-call right now.

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/{oncallId}/active-shift":{"get":{"operationId":"getOnCallsByOncallIdActiveShift","summary":"Fetch the active shift","description":"Returns the shift that is currently active for an on-call schedule. `activeShift` is `null` when nobody is on-call right now.","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in your Spike dashboard under Settings > API."},{"name":"x-team-id","in":"header","required":false,"schema":{"type":"string"},"description":"Optional. The team's `_id` — a 24-character hex ObjectId, NOT the short `uid` slug. Read it from `GET /teams/get-all-teams`."},{"name":"oncallId","in":"path","required":true,"schema":{"type":"string"},"description":"The on-call schedule's `_id` (24-char hex ObjectId)."}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"activeShift":{"type":["object","null"],"properties":{"_id":{"type":"string"},"oncall":{"type":"string"},"user":{"type":"object"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"active":{"type":"boolean"}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Is a user on-call?

> Check whether a personal is currently oncall or not

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[],"TeamIdHeader":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/am-i-on-call":{"get":{"operationId":"getOnCallsAmIOnCall","summary":"Is a user on-call?","description":"Check whether a personal is currently oncall or not","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in your Spike dashboard under Settings > API."},{"name":"x-team-id","in":"header","required":true,"schema":{"type":"string"},"description":"The team's `_id` — a 24-character hex ObjectId, NOT the short `uid` slug. Read it from `GET /teams/get-all-teams`."},{"name":"id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Requires the `?id=<userId>` query parameter. Returns `{}` when the user id is unknown to the caller's organisation.","content":{"application/json":{"schema":{"type":"object","properties":{"isCurrentlyOncall":{"type":"boolean"},"activeShift":{"type":["object","null"]},"shiftEnds":{"type":"string","description":"A DISPLAY string formatted with moment `llll` in the user's timezone (e.g. `Mon, Mar 16, 2026 10:00 AM`) — not an ISO 8601 timestamp."},"partOfMultipleOncalls":{"type":"boolean"},"activeOncallShifts":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"oncall":{"type":"string"},"index":{"type":"number"},"layerIndex":{"type":"number"},"layer":{"type":"string"},"org":{"type":"string"},"active":{"type":"boolean"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"_id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"role":{"type":"string"},"timezone":{"type":"string"}}},"isGap":{"type":"boolean"}}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Fetch upcoming shifts from multiple schedules

> Fetch upcoming shifts from multiple schedules

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[],"TeamIdHeader":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/upcoming-shifts":{"get":{"operationId":"getOnCallsUpcomingShifts","summary":"Fetch upcoming shifts from multiple schedules","description":"Fetch upcoming shifts from multiple schedules","tags":["On-call"],"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in your Spike dashboard under Settings > API."},{"name":"x-team-id","in":"header","required":true,"schema":{"type":"string"},"description":"The team's `_id` — a 24-character hex ObjectId, NOT the short `uid` slug. Read it from `GET /teams/get-all-teams`."},{"name":"id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Requires the `?id=<userId>` query parameter. Returns the user's shifts from the start of the current week to one month ahead.","content":{"application/json":{"schema":{"type":"object","properties":{"spectrum":{"type":"array","items":{"type":"object"}},"handoff":{"type":"object"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Fetch all currently active on-call shifts

> Get a list of users who are currently on call

```json
{"openapi":"3.1.0","info":{"title":"Spike API","version":"1.0.0"},"tags":[{"name":"On-call","description":"Create, read and update on-call schedules, layers and shifts."}],"servers":[{"url":"https://api.spike.sh","description":"API server"}],"security":[{"ApiKeyAuth":[],"TeamIdHeader":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your organisation's API key. Create one in the Spike dashboard under Settings > API. Sent on every request as the `x-api-key` header."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","description":"Error message describing the problem"},"error":{"type":"string","description":"Error type or code"},"success":{"type":"boolean","description":"Present on the endpoints whose handlers include it (for example the API-key filter and the public on-call routes). Always `false` on an error."},"valid_api":{"type":"boolean","description":"Present on `401`s produced by the session/API-key filter. `false` means the supplied `x-api-key` was not recognised."},"statusCode":{"type":"number","description":"Echo of the HTTP status, present on a few permission errors."}}}}},"paths":{"/on-calls/all-active-on-call-shifts":{"get":{"operationId":"getOnCallsAllActiveOnCallShifts","summary":"Fetch all currently active on-call shifts","description":"Get a list of users who are currently on call","tags":["On-call"],"responses":{"200":{"description":"A bare JSON array of every currently-active shift in the organisation — not wrapped in an envelope.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string"},"oncall":{"type":"string"},"index":{"type":"number"},"layerIndex":{"type":"number"},"layer":{"type":"string"},"org":{"type":"string"},"active":{"type":"boolean"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"_id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"role":{"type":"string"},"timezone":{"type":"string"}}},"isGap":{"type":"boolean"}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key. Find it in your Spike dashboard under Settings > API."},{"name":"x-team-id","in":"header","required":true,"schema":{"type":"string"},"description":"The team's `_id` — a 24-character hex ObjectId, NOT the short `uid` slug. Read it from `GET /teams/get-all-teams`."}]}}}}
```
