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 incrementallyV1 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)orTEXT(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)
-
Confirm the company is migrated. Call any V2 endpoint:
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
403with"V2 endpoints require JS Vision migration. Contact support to migrate.", stop here and contact Connecteam Support. -
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:
base_id = slot_id.split(":")[0] if ":" in slot_id else slot_id
Watch out for key collisionsSlot 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
assignedUserIdsarray on group shifts — loop over it instead of treating each row as a separate user shift. - Pagination semantics changed:
limit=10no 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
assignedUserIdsin a single create — one request body item creates one group shift with everyone on it. openSpots > 1on 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
- A simple property change (title, time, location, etc.) on a group base ID applies to every user in the group by default. You do not need to set
isEditForAllUsers: true— that's the V2 default for non-open group shifts when noassignedUserIdsis provided. - To edit a single user inside a group, set
isEditForAllUsers: falseand passassignedUserIdswith the user you want to change. - You can now bulk-toggle
openSpotsdirectly inPUT. - The response can now contain
createdShiftsanddeletedShiftIdsarrays whenassignedUserIdschanges. 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}orDELETE /v2/.../shiftswith 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 V2PUTwith the remaining users, OR use V1DELETEwith the specific slot ID.
Step 6 — Test and roll out
Sanity-check checklistRun these four checks against your real (low-traffic) scheduler before flipping production traffic to V2:
- Create a 3-user group shift via V2 and verify the response has one shift with
assignedUserIds.length === 3.- Update its title via V2 with only
{ shiftId, title }(noisEditForAllUsers). ThenGETit back and verify all three users still see the new title.- Update assigned users via V2 by swapping one user. Verify the response includes the expected entries in
createdShiftsanddeletedShiftIds.- Delete the base ID via V2. Verify all three slots are gone (a follow-up
GETreturns404).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 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
/shiftsand the relatedauto-assignendpoints 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
legacyShiftIdlookup. If you pass a base ID of a shift that was created after migration, V1 returns404 Not Foundbecause there's nolegacyShiftIdmatch. To address post-migration shifts on V1, use the slot ID (bson:uuid) you got back when the shift was created.
See also
Updated about 19 hours ago
