---
updatedAt: 2026-06-29T09:15:20.000Z
---

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

# Migration Playbook and FAQ

This page is the recipe for moving a working V1 integration over to V2 on a JS Vision-migrated company, plus answers to the common questions.

> 👍 You can migrate incrementally
>
> V1 and V2 run side-by-side against the same data. You can migrate one caller at a time — there is no big-bang requirement. Pick V2 for new code, keep V1 for anything you don't want to touch. There is no deprecation pressure on V1.

***

## If you're staying on V1, here's what you still need to do

Even if you have **no intention** of migrating to V2, the underlying company switch to JS Vision changes one thing in V1 responses: **the shape of newly-issued shift IDs**.

| Before migration                                       | After migration                                                                            |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| Shift IDs are 24-char BSON: `507f1f77bcf86cd799439011` | New shifts return slot IDs (\~61 chars, with `:`): `507f1f77bcf86cd799439011:7a4e8a18-...` |

Your existing stored IDs **continue to work** on V1 — the server keeps a `legacyShiftId` mapping and resolves them automatically. But your **storage** has to be ready for the new format:

* **Widen any 24-char ID column** to `VARCHAR(64)` or `TEXT` (or remove the BSON type constraint) before the company is migrated. Otherwise new IDs silently truncate.
* **Don't mix legacy and slot IDs in a single bulk request.** V1 returns `400 Bad Request` (`"must be all new format ids ... or all legacy ids"`) on mixed lists. Batch them by type.

That's the entire V1 maintenance burden on a migrated company. The rest of this page is only relevant if you're moving callers from V1 to V2.

***

## Before you start (V2 migration)

1. **Confirm the company is migrated.** Call any V2 endpoint:

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

   If you get a `403` with `"V2 endpoints require JS Vision migration. Contact support to migrate."`, stop here and contact Connecteam Support.
2. **Decide your scope.** Identify which callers to migrate first. Read-only list-shifts callers are usually the lowest-risk starting point.

***

## Step 1 — Update your shift ID storage

Any shift IDs your system has saved from V1 (slot IDs, with a `:`) need to be converted to base IDs before calling V2. To convert, strip everything from the first `:` onward:

```python
base_id = slot_id.split(":")[0] if ":" in slot_id else slot_id
```

> ⚠️ Watch out for key collisions
>
> Slot IDs from a single base shift all share the same prefix. A slot-ID-keyed table that tracks group members will collapse to fewer rows when keyed by base ID. Plan for that before the data migration so you don't lose per-user state.

***

## Step 2 — Update list-shifts callers

`GET /v2/.../shifts` returns one row per base shift, not one row per user. Expect:

* Fewer rows for the same query (one per group, not one per user).
* A non-empty `assignedUserIds` array on group shifts — loop over it instead of treating each row as a separate user shift.
* Pagination semantics changed: `limit=10` no longer means "10 user-shifts", it now means "10 base shifts". If your code does "fetch a page, count users on the page" — re-check that math.

***

## Step 3 — Update create callers

You now have new capabilities on `POST /v2/.../shifts`:

* Multiple `assignedUserIds` in a single create — one request body item creates one group shift with everyone on it.
* `openSpots > 1` on open shifts (up to 100 per shift).

Existing single-user creates work without changes once you call the V2 path.

***

## Step 4 — Update update callers

> 🚧 This is the section that breaks most often — read carefully

* **Update fields for everyone (keep all members):** send `isEditForAllUsers: true` with the fields you're changing; leave `assignedUserIds` out. Do **not** send a bare update (no `isEditForAllUsers`, no `assignedUserIds`) on a multi-user shift - it currently keeps only one user and removes the rest.
* **Change who's on the shift (add/remove people):** send `isEditForAllUsers: true` with the complete `assignedUserIds` list you want.
* **Give one or a few people different details:** send `isEditForAllUsers: false` with just those `assignedUserIds` and the fields — they're **split onto their own shift**, and everyone else stays put.
* **Gotcha:** sending `assignedUserIds` together with `isEditForAllUsers: false` does **not** edit those people in place - it splits them off. To change the whole group's time or title, use `isEditForAllUsers: true` (not a bare update).
* You can now bulk-toggle `openSpots` directly in `PUT`.
* The response can now contain `createdShifts` and `deletedShiftIds` arrays when `assignedUserIds` changes. Handle both: store the new slot IDs (or base IDs, depending on how you key your storage) and drop references to the deleted ones.

***

## Step 5 — Update delete callers

* Pass **base IDs** to V2 delete (`DELETE /v2/.../shifts/{baseId}` or `DELETE /v2/.../shifts` with a base ID array).
* Deleting by base ID removes the **entire group** — every user's slot is gone.
* "Delete one user from a group" cannot be done with V2 `DELETE`. Use V2 `PUT` with the remaining users, OR use V1 `DELETE` with the specific slot ID.

***

## Step 6 — Test and roll out

> 👍 Sanity-check checklist
>
> Run these four checks against your real (low-traffic) scheduler before flipping production traffic to V2:
>
> 1. Create a 3-user group shift via V2 and verify the response has **one** shift with `assignedUserIds.length === 3`.
> 2. Update its title via V2 with only `{ shiftId, title }` (no `isEditForAllUsers`). Then `GET` it back and verify all three users still see the new title.
> 3. Update assigned users via V2 by swapping one user. Verify the response includes the expected entries in `createdShifts` and `deletedShiftIds`.
> 4. Delete the base ID via V2. Verify all three slots are gone (a follow-up `GET` returns `404`).
>
> If all four pass, the rest is mechanical.

***

## FAQ

> 📘 What about webhooks?
>
> Outbound webhooks have their own V1/V2 versioning, configured separately when you set up the integration. The REST API V1/V2 split documented in this section does **not** affect which webhook payload version you receive. See the existing [Webhooks guides](https://developer.connecteam.com/docs/webhooks-overview) for details.

> 📘 Can I use V1 and V2 simultaneously?
>
> Yes. They share the same data. An ID returned by one path can be converted to the format the other expects (`slot_id.split(":")[0]` to go from V1 to V2). The two paths are not mutually exclusive — pick V2 for new code, keep V1 for anything you don't want to touch.

> 📘 Do repeating shifts behave differently?
>
> Both V1 and V2 currently reject API edits to repeating shifts. This is a known limitation and not specific to Vision.

> 📘 What's the "root open shift with multiple open spots" error?
>
> This V1-only error appears when you try to edit an open shift that has multiple unclaimed spots. V1's data model cannot express "edit all spots of a multi-spot open shift" — use V2 to do this safely.

> 📘 Will V1 ever be deprecated on Vision companies?
>
> Not in the foreseeable future. V1 is the stable backward-compatibility surface and will keep working as-is. V2 is the recommended path for new integrations, especially those that need group shifts or multi-spot open shifts. We will give explicit notice well in advance of any deprecation.

> 📘 Are there V2 endpoints for things other than shifts?
>
> Currently only `/shifts` and the related `auto-assign` endpoints are versioned. Scheduler listing, unavailabilities, shift layers, custom fields, and user-unavailability remain V1-only. You can call them under `/scheduler/v1/...` regardless of whether the rest of your integration is on V2.

> 📘 Do my pre-migration shift IDs still work on V1?
>
> Yes. Every 24-char BSON shift ID issued **before** the company was migrated is still accepted by V1 — the server keeps a mapping (`legacyShiftId`) from the old ID to the new slot it lives in, and resolves it automatically. You don't need to convert or re-fetch anything for existing references.
>
> The only thing that changed: **new** shifts (created after migration) come back from V1 as slot IDs (`bson:uuid`, \~61 chars). So as you create new shifts, your storage starts receiving the longer format alongside any legacy IDs you already have.

> 📘 Why does V1 reject a base ID (no `:`) of a post-migration shift?
>
> A bare BSON ID is ambiguous on Vision: it could be a base shift ID (which maps to a group of multiple slots) or a pre-migration legacy ID (which maps to one specific slot). Rather than guessing and potentially returning the wrong data, V1 only resolves bare BSON IDs via the `legacyShiftId` lookup. If you pass a base ID of a shift that was created **after** migration, V1 returns `404 Not Found` because there's no `legacyShiftId` match. To address post-migration shifts on V1, use the slot ID (`bson:uuid`) you got back when the shift was created.

***

## See also

* [Scheduler API Overview](https://developer.connecteam.com/docs/scheduler-overview)
* [Shifts Overview](https://developer.connecteam.com/docs/scheduler-shifts)
* [Webhooks Overview](https://developer.connecteam.com/docs/webhooks-overview)
* [API Reference](https://developer.connecteam.com/reference/)