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

6.1 KiB

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:

{
  "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:

{
  "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:

{
  "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:

{
  "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:

{
  "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:

{
  "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/*