---
updatedAt: 2026-07-08T08:42:06.000Z
---

Fetch the complete documentation index at: https://developer.connecteam.com/llms.txt. Use this file to discover all available pages before exploring further.

# 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

|                                | V1                                  | V2                     |
| ------------------------------ | ----------------------------------- | ---------------------- |
| Result shape                   | One row per user assignment (slot)  | One row per base shift |
| `assignedUserIds` length       | 0 or 1                              | 0, 1, or more          |
| `shiftId` query filter accepts | Slot ID, or pre-migration legacy ID | Base ID                |
| Pagination unit                | Slot                                | Base shift             |
| `limit` semantics              | Up to N slots                       | Up 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:**

```bash
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):**

```bash
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

|                 | V1                                  | V2                                          |
| --------------- | ----------------------------------- | ------------------------------------------- |
| Path ID accepts | Slot ID, or pre-migration legacy ID | Base ID only — slot ID returns `400`        |
| Response        | The specific slot the ID points to  | Aggregated base shift (all slots collapsed) |

**V1 request (slot ID in path):**

```bash
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):**

```bash
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

|                                  | V1                             | V2                                         |
| -------------------------------- | ------------------------------ | ------------------------------------------ |
| `assignedUserIds` length         | 0 or 1 (more → `400`)          | Any                                        |
| `openSpots > 1` on an open shift | `400`                          | Allowed (`1 ≤ openSpots ≤ 100`)            |
| Group-shift creation in one call | Not possible                   | One object with multiple `assignedUserIds` |
| Created group-shift response     | One object per user (slot IDs) | One aggregated object (base ID)            |

**V2: create a group shift with three users:**

```bash
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:**

```bash
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

|                                                           | V1                                                        | V2                                                                                                                      |
| --------------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `shiftId` in body accepts                                 | Slot ID, or pre-migration legacy ID                       | Base ID only                                                                                                            |
| `isEditForAllUsers: true`                                 | `400` ("not supported in V1")                             | Allowed                                                                                                                 |
| `openSpots` in body                                       | `400` ("not supported in V1 update")                      | Allowed                                                                                                                 |
| Multiple `assignedUserIds`                                | `400` ("V1 supports at most one assigned user per shift") | Allowed                                                                                                                 |
| Update fields for all users in a group                    | Edits the specific slot only                              | Send `isEditForAllUsers: true`. A bare update (no `isEditForAllUsers`, no `assignedUserIds`) applies to the whole group |
| Targeted single/partial edit (`isEditForAllUsers: false`) | Not applicable                                            | Requires `assignedUserIds`; omitting it returns `400` (see below)                                                       |
| `createdShifts` / `deletedShiftIds` in response           | Always empty                                              | Populated 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).

> 🚫 `isEditForAllUsers: false` requires `assignedUserIds`
>
> On V2, if you explicitly send `isEditForAllUsers: false` on a group / base shift **without** `assignedUserIds`, the request is rejected:
>
> ```json
> { "detail": "Invalid shift update request. For targeted V2 Vision base/group edits, assignedUserIds must be provided when isEditForAllUsers is explicitly false." }
> ```
>
> There is no way to know which user to target. To edit the whole group, use `isEditForAllUsers: true`; to peel specific users off, include their `assignedUserIds`.

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

```bash
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:

```bash
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.

```bash
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:

```json
{
  "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}`

|                   | V1                                    | V2                                           |
| ----------------- | ------------------------------------- | -------------------------------------------- |
| IDs accepted      | Slot IDs, or pre-migration legacy IDs | Base IDs only                                |
| Delete by base ID | Not accepted                          | Removes **every slot** under that base shift |
| Delete by slot ID | Removes that single slot only         | `400 Bad Request`                            |
| Response IDs      | Slot IDs that were removed            | Base IDs that were removed                   |

**V2: delete an entire group in one call:**

```bash
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:**

```bash
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 / situation                                   | V1 rule                                | Error code                                                 |
| --------------------------------------------------- | -------------------------------------- | ---------------------------------------------------------- |
| `assignedUserIds` length > 1                        | Rejected                               | `400` ("V1 supports at most one assigned user per shift")  |
| `openSpots != 1` on create                          | Rejected                               | `400`                                                      |
| `openSpots` present on update                       | Rejected                               | `400` ("not supported in V1 update")                       |
| `isEditForAllUsers: true`                           | Rejected                               | `400` ("not supported in V1")                              |
| Plain BSON `shiftId` (no `:`) on a migrated company | Rejected unless it's a known legacy ID | `400` ("shift id is invalid")                              |
| Mixed slot/legacy IDs in one request                | Rejected                               | `400` ("must be all new format ids ... or all legacy ids") |

> 🚧 V2-only rejections

| Field / situation                                                          | V2 rule  | Error code                                                                                                           |
| -------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| Slot-shaped `shiftId` (contains `:`)                                       | Rejected | `400` ("shift id is invalid")                                                                                        |
| `isEditForAllUsers: false` without `assignedUserIds` on a group/base shift | Rejected | `400` ("Invalid shift update request … assignedUserIds must be provided when isEditForAllUsers is explicitly false") |
| Any V2 endpoint on a non-Vision company                                    | Rejected | `403`                                                                                                                |

***

## 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.

```text
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.

```text
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 →](https://developer.connecteam.com/docs/scheduler-shifts-vision-migration)