Files
Krow-workspace/docs/BACKEND/API_GUIDES/V2/staff-shifts.md

286 lines
6.1 KiB
Markdown

# Staff Shifts V2
This document is the frontend handoff for the `staff/shifts/*` routes on the unified v2 API.
Base URL:
- `https://krow-api-v2-e3g6witsvq-uc.a.run.app`
## Read routes
- `GET /staff/orders/available`
- `GET /staff/orders/:orderId`
- `GET /staff/shifts/assigned`
- `GET /staff/shifts/open`
- `GET /staff/shifts/pending`
- `GET /staff/shifts/cancelled`
- `GET /staff/shifts/completed`
- `GET /staff/shifts/:shiftId`
## Write routes
- `POST /staff/orders/:orderId/book`
- `POST /staff/shifts/:shiftId/apply`
- `POST /staff/shifts/:shiftId/accept`
- `POST /staff/shifts/:shiftId/decline`
- `POST /staff/shifts/:shiftId/request-swap`
- `POST /staff/shifts/:shiftId/submit-for-approval`
All write routes require:
- `Authorization: Bearer <firebase-id-token>`
- `Idempotency-Key: <unique-per-action>`
## Shift lifecycle
### Find work by order
`GET /staff/orders/available`
- use this for grouped recurring or permanent work cards
- each item represents one order plus one role
- this feed is already filtered to the current worker context
- `schedule` gives the preview for the whole booking window
Example response:
```json
{
"orderId": "uuid",
"orderType": "RECURRING",
"roleId": "uuid",
"roleCode": "BARISTA",
"roleName": "Barista",
"clientName": "Google Mountain View Cafes",
"location": "Google MV Cafe Clock Point",
"locationAddress": "1600 Amphitheatre Pkwy, Mountain View, CA",
"hourlyRateCents": 2300,
"hourlyRate": 23,
"requiredWorkerCount": 1,
"filledCount": 0,
"instantBook": false,
"dispatchTeam": "CORE",
"dispatchPriority": 1,
"schedule": {
"totalShifts": 3,
"startDate": "2026-03-24",
"endDate": "2026-03-28",
"daysOfWeek": ["WED", "FRI"],
"startTime": "09:00",
"endTime": "15:00",
"timezone": "America/Los_Angeles",
"firstShiftStartsAt": "2026-03-25T16:00:00.000Z",
"lastShiftEndsAt": "2026-03-27T22:00:00.000Z"
}
}
```
`POST /staff/orders/:orderId/book`
- use this when the worker books the full order instead of one shift
- booking is atomic across the future shifts in that order for the selected role
- backend returns `PENDING` when the booking is reserved but not instant-booked
- backend returns `CONFIRMED` when every future shift in that booking path is instant-booked
- backend returns `422 UNPROCESSABLE_ENTITY` when the worker is not eligible to book that order
Example request:
```json
{
"roleId": "uuid"
}
```
Important:
- `GET /staff/orders/:orderId` is now the source of truth for the order detail screen before booking
- `roleId` for the order-booking flow is the role catalog id returned by `GET /staff/orders/:orderId`
- it is not the same thing as the per-shift `shift_roles.id`
- when booking is rejected, use `details.blockers` from the error response to explain why
### Order detail
`GET /staff/orders/:orderId`
Use this as the source of truth for the worker order-review page before calling `POST /staff/orders/:orderId/book`.
Response shape includes:
- `orderId`
- `orderType`
- `roleId`
- `roleCode`
- `roleName`
- `clientName`
- `businessId`
- `instantBook`
- `dispatchTeam`
- `dispatchPriority`
- `jobDescription`
- `instructions`
- `status`
- `schedule`
- `location`
- `pay`
- `staffing`
- `managers`
- `eligibility`
Frontend rules:
- call this endpoint after a worker taps an order card from `GET /staff/orders/available`
- use the returned `roleId` when calling `POST /staff/orders/:orderId/book`
- if `eligibility.isEligible` is `false`, show the blocker messages and disable booking
### Find shifts
`GET /staff/shifts/open`
- use this for the worker marketplace feed
- the worker applies to a concrete shift role
- send the `roleId` returned by the open-shifts response
- `roleId` here means `shift_roles.id`, not the role catalog id
Apply request example:
```json
{
"roleId": "uuid",
"instantBook": false
}
```
### Pending shifts
`GET /staff/shifts/pending`
- use `POST /staff/shifts/:shiftId/accept` to accept
- use `POST /staff/shifts/:shiftId/decline` to decline
### Assigned shifts
`GET /staff/shifts/assigned`
Each item now includes:
- `clientName`
- `hourlyRate`
- `totalRate`
- `startTime`
- `endTime`
### Shift detail
`GET /staff/shifts/:shiftId`
Each detail response now includes:
- `clientName`
- `latitude`
- `longitude`
- `hourlyRate`
- `totalRate`
Use this as the source of truth for the shift detail screen.
### Request swap
`POST /staff/shifts/:shiftId/request-swap`
Example:
```json
{
"reason": "Need coverage for a family emergency"
}
```
Current backend behavior:
- marks the assignment as `SWAP_REQUESTED`
- stores the reason
- emits `SHIFT_SWAP_REQUESTED`
- exposes the shift in the replacement pool
- starts the swap-expiry window used by backend auto-cancellation
Manager/ops review happens through:
- `GET /client/coverage/swap-requests`
- `GET /client/coverage/dispatch-candidates`
- `POST /client/coverage/swap-requests/:swapRequestId/resolve`
- `POST /client/coverage/swap-requests/:swapRequestId/cancel`
If the swap request expires without coverage, backend auto-cancels it and alerts the manager path plus the original worker.
### Submit completed shift for approval
`POST /staff/shifts/:shiftId/submit-for-approval`
Use this after the worker has clocked out.
Example:
```json
{
"note": "Worked full shift and all tasks were completed"
}
```
Current backend behavior:
- only allows shifts in `CHECKED_OUT` or `COMPLETED`
- creates or updates the assignment timesheet
- sets the timesheet to `SUBMITTED` unless it is already `APPROVED` or `PAID`
- emits `TIMESHEET_SUBMITTED_FOR_APPROVAL`
Example response:
```json
{
"assignmentId": "uuid",
"shiftId": "uuid",
"timesheetId": "uuid",
"status": "SUBMITTED",
"submitted": true
}
```
## Completed shifts
`GET /staff/shifts/completed`
Each item now includes:
- `date`
- `clientName`
- `startTime`
- `endTime`
- `hourlyRate`
- `totalRate`
- `timesheetStatus`
- `paymentStatus`
## Clock-in support fields
`GET /staff/clock-in/shifts/today`
Each item now includes:
- `clientName`
- `hourlyRate`
- `totalRate`
- `latitude`
- `longitude`
- `clockInMode`
- `allowClockInOverride`
## Frontend rule
Use the unified routes only.
Do not build new mobile work on:
- `/query/*`
- `/commands/*`
- `/core/*`