# People And Scheduling Migration

This page covers v1-to-v2 migration notes for users, absences, shifts, and shift day notes.

:::info{title="Where to find exact schemas"}

For the v2-native endpoint behavior, start with
[People And Scheduling](/docs/guides/resource-guides/people-and-scheduling). Use the [Users API reference](/api/platform/users),
[Absences API reference](/api/platform/absences), and [Shifts API reference](/api/platform/shifts) for exact schemas,
examples, status codes, and field-level validation.

:::

## Endpoint Mapping

| v1 usage pattern | v2 direction |
|------------------|--------------|
| `GET .../users/all` | Use `GET /api/v2/users`; parse the shared pagination response shape. |
| `GET .../users/email/{email}` | No public v2 endpoint is exposed. List users via `GET /api/v2/users` (paginated), match on email client-side, and persist the v2 user ID. |
| `GET .../absence/{id}` | Use `GET /api/v2/absences/{id}`. |
| `GET .../absence/external/{externalAbsenceId}` | Use `GET /api/v2/absences/external-id/{externalId}`. |
| `POST/PUT/PATCH/DELETE .../absence...` | Use the plural `/api/v2/absences` paths and v2 request models. |
| `GET/PUT/PATCH .../shift/{id}` | Use `GET/PUT/PATCH /api/v2/shifts/{id}` and map `label` to `name`. |
| `.../shift/external/{externalShiftId}/...` | Use `/api/v2/shifts/external-id/{externalId}/...` for shift reads and shift-day-note operations. |
| `.../shift/{shiftId}/note/{date}` | Use `/api/v2/shifts/{shiftId}/notes/{date}`. |

## Users

Users are read-only through the public API.

Client-impacting changes:

- stop parsing `GET .../users/all` as a bare array.
- use `includeExternal=false` when only internal users should be returned; the default includes
  external users.
- use `includeGroups=true` when the response must include group references.
- replace v1 `departmentIds` parsing with v2 `groups` when groups are requested.
- replace v1 `extUserId` or `externalUserId` fields with v2 `externalId`.
- do not migrate email lookup calls to `/api/v2/users/email/{email}`; that path is not part of the
  public v2 contract.

## Absences

Absence request and response models are more structured in v2.

| v1 field | v2 field |
|----------|----------|
| `externalAbsenceId` | `externalId` |
| `userEmail` | `user.email` |
| `supplierId` | `user.supplierId` |
| `externalUserId` | `user.externalId` |
| `period.fromDate`, `period.fromTime`, `period.toDate`, `period.toTime` | same names under `period` |
| numeric `type` | `type.code` in requests, `type.code` plus `type.name` in responses |
| `note` | `note` |

Important migration points:

- `POST /api/v2/absences` returns `201 Created`.
- `PUT` is full replacement and requires `period` and `type`.
- `PATCH` is presence-aware; omitted fields remain unchanged.
- `externalId: null` and `note: null` clear those fields where documented.
- delete operations return `204 No Content`.
- absence lists are paginated and support filters such as `fromDate`, `toDate`, `userId`,
  `userEmail`, and `type`.

## Shifts

Shifts are configuration data for work schedules. v2 keeps public shift updates narrow:

- shift create is not exposed through public v2 REST.
- shift delete is not exposed through public v2 REST.
- shift writes by external ID are not exposed for the shift resource itself.
- `PUT/PATCH /api/v2/shifts/{id}` update only `name` and `externalId`.

Field mapping:

| v1 field | v2 field |
|----------|----------|
| `label` | `name` |
| `externalId` | `externalId` |
| response only had basic shift fields | response can also include `schedule`, `startTime`, `endTime`, `timeZone`, and `availability` |

:::caution{title="External-ID clearing convention changed"}

External-ID clearing is documented as JSON `null` in v2. An exactly empty string is also normalized
to `null` and clears the field; whitespace-only external IDs are invalid.

:::

## Shift Day Notes

Shift day notes moved from singular `/note` paths to plural `/notes` paths.

| Operation | v2 path |
|-----------|---------|
| Read by shift ID and date | `GET /api/v2/shifts/{shiftId}/notes/{date}` |
| Read by external shift ID and date | `GET /api/v2/shifts/external-id/{externalId}/notes/{date}` |
| Create by shift ID | `POST /api/v2/shifts/{shiftId}/notes` |
| Create by external shift ID | `POST /api/v2/shifts/external-id/{externalId}/notes` |
| Replace by shift ID and date | `PUT /api/v2/shifts/{shiftId}/notes/{date}` |
| Replace by external shift ID and date | `PUT /api/v2/shifts/external-id/{externalId}/notes/{date}` |

Client-impacting changes:

- handle `201 Created` from shift-day-note create.
- handle `409 Conflict` when a note already exists for the addressed shift/date.
- handle `400 Bad Request` when the create date is not part of the shift schedule.
- body `date` must match the path date on replace.
- prefer persisted shift IDs when possible. External shift IDs are integration identifiers; use the
  v2 shift ID to target a specific shift.

## Related Guides

- [People And Scheduling](/docs/guides/resource-guides/people-and-scheduling)
- [Write Semantics](/docs/guides/api-conventions/write-semantics)
- [Querying Lists](/docs/guides/api-conventions/querying-lists)
- [Error Handling](/docs/guides/api-conventions/error-handling)
