Endpoint-by-Endpoint Reference

This page covers, endpoint by endpoint, what changes between V1 and V2 on a JS Vision-migrated company. All paths are relative to https://api.connecteam.com/scheduler/{v1|v2}/schedulers/{schedulerId}.

📘

Reminder

On non-Vision companies, every V2 endpoint returns 403. The tables and examples below describe behavior on a migrated company.


GET /shifts — list shifts

V1V2
Result shapeOne row per user assignment (slot)One row per base shift
assignedUserIds length0 or 10, 1, or more
shiftId query filter acceptsSlot ID, or pre-migration legacy IDBase ID
Pagination unitSlotBase shift
limit semanticsUp to N slotsUp to N base shifts

startTime, endTime, isOpenShift, isPublished, isRequireAdminApproval, jobId, assignedUserIds, title, sort, order, limit, and offset are identical between V1 and V2.

V1 request:

curl --request GET \
  --url 'https://api.connecteam.com/scheduler/v1/schedulers/{schedulerId}/shifts?startTime=1736899200&endTime=1737504000&limit=10' \
  --header 'X-API-KEY: YOUR_API_KEY'

V2 request (same params, different path):

curl --request GET \
  --url 'https://api.connecteam.com/scheduler/v2/schedulers/{schedulerId}/shifts?startTime=1736899200&endTime=1737504000&limit=10' \
  --header 'X-API-KEY: YOUR_API_KEY'
📘

Pagination caveat

If you have 100 base shifts each with 3 users:

  • V1 with limit=10 returns the first 10 slots, which may cover only 3–4 distinct base shifts.
  • V2 with limit=10 returns the first 10 base shifts (10 aggregated objects).

GET /shifts/{shiftId} — single shift

V1V2
Path ID acceptsSlot ID, or pre-migration legacy IDBase ID only — slot ID returns 400
ResponseThe specific slot the ID points toAggregated base shift (all slots collapsed)

V1 request (slot ID in path):

curl --request GET \
  --url 'https://api.connecteam.com/scheduler/v1/schedulers/{schedulerId}/shifts/6784dacb3c07733b0a849f49:7a4e8a18-...' \
  --header 'X-API-KEY: YOUR_API_KEY'

V2 request (base ID in path):

curl --request GET \
  --url 'https://api.connecteam.com/scheduler/v2/schedulers/{schedulerId}/shifts/6784dacb3c07733b0a849f49' \
  --header 'X-API-KEY: YOUR_API_KEY'

POST /shifts — create shifts

V1V2
assignedUserIds length0 or 1 (more → 400)Any
openSpots > 1 on an open shift400Allowed (1 ≤ openSpots ≤ 100)
Group-shift creation in one callNot possibleOne object with multiple assignedUserIds
Created group-shift responseOne object per user (slot IDs)One aggregated object (base ID)

V2: create a group shift with three users:

curl --request POST \
  --url 'https://api.connecteam.com/scheduler/v2/schedulers/{schedulerId}/shifts' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '[
    {
      "startTime": 1736924400,
      "endTime":   1736953200,
      "title": "Morning Shift",
      "assignedUserIds": [9170357, 9170358, 9170359],
      "isPublished": true
    }
  ]'

V2: create an open shift with 5 spots:

curl --request POST \
  --url 'https://api.connecteam.com/scheduler/v2/schedulers/{schedulerId}/shifts' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '[
    {
      "startTime": 1736924400,
      "endTime":   1736953200,
      "title": "Event Coverage",
      "isOpenShift": true,
      "openSpots": 5,
      "isPublished": true
    }
  ]'
🚧

V1 cannot create multi-user or multi-spot shifts

On V1, assignedUserIds of length > 1 returns 400 ("V1 supports at most one assigned user per shift") and openSpots != 1 also returns 400. To create a multi-user or multi-spot shift on a migrated company, you must use V2.


PUT /shifts — update shifts

V1V2
shiftId in body acceptsSlot ID, or pre-migration legacy IDBase ID only
isEditForAllUsers: true400 ("not supported in V1")Allowed
openSpots in body400 ("not supported in V1 update")Allowed
Multiple assignedUserIds400 ("V1 supports at most one assigned user per shift")Allowed
Update fields for all users in a groupEdits the specific slot onlySend isEditForAllUsers: true. A bare update (no isEditForAllUsers, no assignedUserIds) currently keeps only one user - see the warning below
createdShifts / deletedShiftIds in responseAlways emptyPopulated when users change
📘

The mental model: one shift with a list of people

On V2 a group shift is one object with a list of people on it (assignedUserIds). When you PUT, the API decides what you meant from what you send:

  • Update fields for everyone (keep all members) -> send isEditForAllUsers: true with the fields you're changing, and leave assignedUserIds out. The change applies to every user and nobody is removed.
  • Change who is on the shift (add or remove people) -> send isEditForAllUsers: true and the complete assignedUserIds list you want. Anyone missing from the list is removed; anyone new is added.
  • Give one or a few people something different -> send isEditForAllUsers: false and just those assignedUserIds, plus the fields. Those people are split onto their own shift; everyone else stays on the original.
🚧

Important: on a multi-user shift, always set isEditForAllUsers

There is a known issue with bare group updates. If you PUT a multi-user shift without isEditForAllUsers and without assignedUserIds (for example, changing only startTime), the shift currently keeps only one user and silently removes the rest - and the response shows an empty deletedShiftIds, so it is not obvious. Always send isEditForAllUsers: true when a field change should apply to the whole group.

⚠️

The other gotcha: assignedUserIds + isEditForAllUsers: false does not edit those people "in place"

It splits them off into a separate shift. Only include assignedUserIds when you actually want to change membership (isEditForAllUsers: true + full list) or peel specific people off (isEditForAllUsers: false + those users).

Update a group in one call - applies to all users (V2)

curl --request PUT \
  --url 'https://api.connecteam.com/scheduler/v2/schedulers/{schedulerId}/shifts' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '[
    {
      "shiftId": "6784dacb3c07733b0a849f49",
      "isEditForAllUsers": true,
      "title": "Updated Title"
    }
  ]'

With isEditForAllUsers: true (and no assignedUserIds), the change applies to every user on the shift and nobody is removed.

Edit one user out of a group (V2)

Set isEditForAllUsers: false and pass assignedUserIds with the single user you want to change:

curl --request PUT \
  --url 'https://api.connecteam.com/scheduler/v2/schedulers/{schedulerId}/shifts' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '[
    {
      "shiftId": "6784dacb3c07733b0a849f49",
      "isEditForAllUsers": false,
      "assignedUserIds": [9170357],
      "title": "Updated only for this user"
    }
  ]'

The API detaches that user's slot from the group and applies your edits only to them; the other users' slots stay untouched. Think of it as "peel this person off, with changes" — not "edit this person in place." The original group keeps everyone else.

Replace the whole group's users (V2)

Pass isEditForAllUsers: true with the full desired assignedUserIds list. Users not in the list are removed; users not already on the shift are added — all under the same base ID.

curl --request PUT \
  --url 'https://api.connecteam.com/scheduler/v2/schedulers/{schedulerId}/shifts' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '[
    {
      "shiftId": "6784dacb3c07733b0a849f49",
      "assignedUserIds": [9170357, 9170359],
      "isEditForAllUsers": true
    }
  ]'

The removed user's slot is reported in deletedShiftIds, and any added user's slot in createdShifts.

createdShifts and deletedShiftIds in the response

When a V2 PUT changes the user set on a group (assignedUserIds), the API may need to add or remove slots under the same base shift. These show up in the response alongside the edited shifts:

{
  "data": {
    "shifts":           [ /* shifts whose properties changed */ ],
    "createdShifts":    [ /* slots added because users were added to the group */ ],
    "deletedShiftIds":  [ /* slot IDs removed because users were removed from the group */ ]
  }
}
📘

V1 never returns non-empty createdShifts / deletedShiftIds

V1 never restructures a group from a single PUT, so these arrays are always empty on V1. If your code reads them, it will only ever see content on V2.


DELETE /shifts and DELETE /shifts/{shiftId}

V1V2
IDs acceptedSlot IDs, or pre-migration legacy IDsBase IDs only
Delete by base IDNot acceptedRemoves every slot under that base shift
Delete by slot IDRemoves that single slot only400 Bad Request
Response IDsSlot IDs that were removedBase IDs that were removed

V2: delete an entire group in one call:

curl --request DELETE \
  --url 'https://api.connecteam.com/scheduler/v2/schedulers/{schedulerId}/shifts/6784dacb3c07733b0a849f49' \
  --header 'X-API-KEY: YOUR_API_KEY'

V1: delete only one user's slot from a group:

curl --request DELETE \
  --url 'https://api.connecteam.com/scheduler/v1/schedulers/{schedulerId}/shifts/6784dacb3c07733b0a849f49:7a4e8a18-...' \
  --header 'X-API-KEY: YOUR_API_KEY'

The group stays intact for the other users.

📘

Removing one user from a group on V2

V2 has no direct "delete one user" operation. Use PUT instead, with assignedUserIds set to the users who should remain. The API will detach the removed user's slot and list it in deletedShiftIds.


Validation rules that differ between V1 and V2

Even though the request schema is the same, V1 validators are stricter to preserve legacy behavior.

🚧

V1-only rejections on a migrated company

Field / situationV1 ruleError code
assignedUserIds length > 1Rejected400 ("V1 supports at most one assigned user per shift")
openSpots != 1 on createRejected400
openSpots present on updateRejected400 ("not supported in V1 update")
isEditForAllUsers: trueRejected400 ("not supported in V1")
Plain BSON shiftId (no :) on a migrated companyRejected unless it's a known legacy ID400 ("shift id is invalid")
Mixed slot/legacy IDs in one requestRejected400 ("must be all new format ids ... or all legacy ids")
🚧

V2-only rejections

Field / situationV2 ruleError code
Slot-shaped shiftId (contains :)Rejected400 ("shift id is invalid")
Any V2 endpoint on a non-Vision companyRejected403

Pagination on a migrated company

This is the most common pitfall when an existing V1 integration starts using V2.

V1 + Vision paginates by slot, with a deterministic secondary sort on the slot ID. If you have base shifts with multiple users, limit controls how many user-rows come back — not how many distinct shifts.

limit=10  →  up to 10 rows, each row = one user assignment
              ↑ this may include several rows that share a base ID prefix

V2 + Vision paginates by base shift. Each row in the response is one aggregated shift.

limit=10  →  up to 10 aggregated shifts, each with assignedUserIds: [...]
👍

Pagination loop pattern (identical for V1 and V2)

Pass paging.offset from each response back as the offset query parameter on the next request. Stop when the response returns fewer items than limit.


Next: Migration Playbook and FAQ →