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
Default mode for a non-open group when no assignedUserIds is sentEdits the specific slot onlyImplicit ALL_USERS — applies to every user in the group
createdShifts / deletedShiftIds in responseAlways emptyPopulated when users change
👍

Why V2's "implicit ALL_USERS" matters

In V2 you're addressing a base shift by base ID. When you update a title, time, location, or job, the API assumes you want the change to apply to every user in the group — because that's the natural mental model when you only see one object per group. You don't need to set isEditForAllUsers: true for property changes; it's the default. This is one of the main reasons V2 is easier to use for management dashboards and bulk operations.

Update a group's title in one call (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",
      "title": "Updated Title"
    }
  ]'

All users on the group see the new title.

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.

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 →


What’s Next

Continue