289 lines
8.9 KiB
Markdown
289 lines
8.9 KiB
Markdown
# Unified API V2
|
|
|
|
Frontend should use this service as the single base URL:
|
|
|
|
- `https://krow-api-v2-933560802882.us-central1.run.app`
|
|
|
|
The gateway keeps backend services separate internally, but frontend should treat it as one API.
|
|
|
|
## 1) Auth routes
|
|
|
|
Full auth behavior, including staff phone flow and refresh rules, is documented in [Authentication](./authentication.md).
|
|
|
|
### Client auth
|
|
|
|
- `POST /auth/client/sign-in`
|
|
- `POST /auth/client/sign-up`
|
|
- `POST /auth/client/sign-out`
|
|
|
|
### Staff auth
|
|
|
|
- `POST /auth/staff/phone/start`
|
|
- `POST /auth/staff/phone/verify`
|
|
- `POST /auth/staff/sign-out`
|
|
|
|
### Shared auth
|
|
|
|
- `GET /auth/session`
|
|
- `POST /auth/sign-out`
|
|
|
|
## 2) Client routes
|
|
|
|
### Client reads
|
|
|
|
- `GET /client/session`
|
|
- `GET /client/dashboard`
|
|
- `GET /client/reorders`
|
|
- `GET /client/billing/accounts`
|
|
- `GET /client/billing/invoices/pending`
|
|
- `GET /client/billing/invoices/history`
|
|
- `GET /client/billing/current-bill`
|
|
- `GET /client/billing/savings`
|
|
- `GET /client/billing/spend-breakdown`
|
|
- `GET /client/coverage`
|
|
- `GET /client/coverage/stats`
|
|
- `GET /client/coverage/core-team`
|
|
- `GET /client/coverage/incidents`
|
|
- `GET /client/hubs`
|
|
- `GET /client/cost-centers`
|
|
- `GET /client/vendors`
|
|
- `GET /client/vendors/:vendorId/roles`
|
|
- `GET /client/hubs/:hubId/managers`
|
|
- `GET /client/team-members`
|
|
- `GET /client/orders/view`
|
|
- `GET /client/orders/:orderId/reorder-preview`
|
|
- `GET /client/reports/summary`
|
|
- `GET /client/reports/daily-ops`
|
|
- `GET /client/reports/spend`
|
|
- `GET /client/reports/coverage`
|
|
- `GET /client/reports/forecast`
|
|
- `GET /client/reports/performance`
|
|
- `GET /client/reports/no-show`
|
|
|
|
### Client writes
|
|
|
|
- `POST /client/devices/push-tokens`
|
|
- `DELETE /client/devices/push-tokens`
|
|
- `POST /client/orders/one-time`
|
|
- `POST /client/orders/recurring`
|
|
- `POST /client/orders/permanent`
|
|
- `POST /client/orders/:orderId/edit`
|
|
- `POST /client/orders/:orderId/cancel`
|
|
- `POST /client/hubs`
|
|
- `PUT /client/hubs/:hubId`
|
|
- `DELETE /client/hubs/:hubId`
|
|
- `POST /client/hubs/:hubId/assign-nfc`
|
|
- `POST /client/hubs/:hubId/managers`
|
|
- `POST /client/billing/invoices/:invoiceId/approve`
|
|
- `POST /client/billing/invoices/:invoiceId/dispute`
|
|
- `POST /client/coverage/reviews`
|
|
- `POST /client/coverage/late-workers/:assignmentId/cancel`
|
|
|
|
## 3) Staff routes
|
|
|
|
### Staff reads
|
|
|
|
- `GET /staff/session`
|
|
- `GET /staff/dashboard`
|
|
- `GET /staff/profile-completion`
|
|
- `GET /staff/availability`
|
|
- `GET /staff/clock-in/shifts/today`
|
|
- `GET /staff/clock-in/status`
|
|
- `GET /staff/payments/summary`
|
|
- `GET /staff/payments/history`
|
|
- `GET /staff/payments/chart`
|
|
- `GET /staff/shifts/assigned`
|
|
- `GET /staff/shifts/open`
|
|
- `GET /staff/shifts/pending`
|
|
- `GET /staff/shifts/cancelled`
|
|
- `GET /staff/shifts/completed`
|
|
- `GET /staff/shifts/:shiftId`
|
|
- `GET /staff/profile/sections`
|
|
- `GET /staff/profile/personal-info`
|
|
- `GET /staff/profile/industries`
|
|
- `GET /staff/profile/skills`
|
|
- `GET /staff/profile/documents`
|
|
- `GET /staff/profile/attire`
|
|
- `GET /staff/profile/tax-forms`
|
|
- `GET /staff/profile/emergency-contacts`
|
|
- `GET /staff/profile/certificates`
|
|
- `GET /staff/profile/bank-accounts`
|
|
- `GET /staff/profile/benefits`
|
|
- `GET /staff/profile/time-card`
|
|
- `GET /staff/profile/privacy`
|
|
- `GET /staff/faqs`
|
|
- `GET /staff/faqs/search`
|
|
|
|
### Staff writes
|
|
|
|
- `POST /staff/profile/setup`
|
|
- `POST /staff/devices/push-tokens`
|
|
- `DELETE /staff/devices/push-tokens`
|
|
- `POST /staff/clock-in`
|
|
- `POST /staff/clock-out`
|
|
- `POST /staff/location-streams`
|
|
- `PUT /staff/availability`
|
|
- `POST /staff/availability/quick-set`
|
|
- `POST /staff/shifts/:shiftId/apply`
|
|
- `POST /staff/shifts/:shiftId/accept`
|
|
- `POST /staff/shifts/:shiftId/decline`
|
|
- `POST /staff/shifts/:shiftId/request-swap`
|
|
- `PUT /staff/profile/personal-info`
|
|
- `PUT /staff/profile/experience`
|
|
- `PUT /staff/profile/locations`
|
|
- `POST /staff/profile/emergency-contacts`
|
|
- `PUT /staff/profile/emergency-contacts/:contactId`
|
|
- `PUT /staff/profile/tax-forms/:formType`
|
|
- `POST /staff/profile/tax-forms/:formType/submit`
|
|
- `POST /staff/profile/bank-accounts`
|
|
- `PUT /staff/profile/privacy`
|
|
|
|
## 4) Upload and verification routes
|
|
|
|
These are exposed as direct unified aliases even though they are backed by `core-api-v2`.
|
|
|
|
### Generic core aliases
|
|
|
|
- `POST /upload-file`
|
|
- `POST /create-signed-url`
|
|
- `POST /invoke-llm`
|
|
- `POST /rapid-orders/transcribe`
|
|
- `POST /rapid-orders/parse`
|
|
- `POST /verifications`
|
|
- `GET /verifications/:verificationId`
|
|
- `POST /verifications/:verificationId/review`
|
|
- `POST /verifications/:verificationId/retry`
|
|
|
|
### Staff upload aliases
|
|
|
|
- `POST /staff/profile/photo`
|
|
- `POST /staff/profile/documents/:documentId/upload`
|
|
- `POST /staff/profile/attire/:documentId/upload`
|
|
- `POST /staff/profile/certificates`
|
|
- `DELETE /staff/profile/certificates/:certificateId`
|
|
|
|
## 5) Notes that matter for frontend
|
|
|
|
- `roleId` on `POST /staff/shifts/:shiftId/apply` is the concrete `shift_roles.id` for that shift, not the catalog role definition id.
|
|
- `accountType` on `POST /staff/profile/bank-accounts` accepts either lowercase or uppercase and is normalized by the backend.
|
|
- File upload routes return a storage path plus a signed URL. Frontend uploads the file directly to storage using that URL.
|
|
- Verification upload and review routes are live and were validated through document, attire, and certificate flows. Do not rely on long-lived verification history durability until the dedicated persistence slice is landed in `core-api-v2`.
|
|
- Attendance policy is explicit. Reads now expose `clockInMode` and `allowClockInOverride`.
|
|
- `clockInMode` values are:
|
|
- `NFC_REQUIRED`
|
|
- `GEO_REQUIRED`
|
|
- `EITHER`
|
|
- For `POST /staff/clock-in` and `POST /staff/clock-out`:
|
|
- send `nfcTagId` when clocking with NFC
|
|
- send `latitude`, `longitude`, and `accuracyMeters` when clocking with geolocation
|
|
- send `proofNonce` and `proofTimestamp` for attendance-proof logging; these are most important on NFC paths
|
|
- send `attestationProvider` and `attestationToken` only when the device has a real attestation result to forward
|
|
- send `overrideReason` only when the worker is bypassing a geofence failure and the shift/hub allows overrides
|
|
- `POST /staff/location-streams` is for the background tracking loop after a worker is already clocked in.
|
|
- `GET /client/coverage/incidents` is the review feed for geofence breaches, missing-location batches, and clock-in overrides.
|
|
- `POST /client/coverage/late-workers/:assignmentId/cancel` is the client-side recovery action when lateness is confirmed by incident evidence or elapsed grace time.
|
|
- Raw location stream payloads are stored in the private v2 bucket; SQL only stores the summary and incident index.
|
|
- Push delivery is backed by:
|
|
- SQL token registry in `device_push_tokens`
|
|
- durable queue in `notification_outbox`
|
|
- per-attempt delivery records in `notification_deliveries`
|
|
- private Cloud Run worker service `krow-notification-worker-v2`
|
|
- Cloud Scheduler job `krow-notification-dispatch-v2`
|
|
|
|
### Push token request example
|
|
|
|
```json
|
|
{
|
|
"provider": "FCM",
|
|
"platform": "IOS",
|
|
"pushToken": "expo-or-fcm-device-token",
|
|
"deviceId": "iphone-15-pro-max",
|
|
"appVersion": "2.0.0",
|
|
"appBuild": "2000",
|
|
"locale": "en-US",
|
|
"timezone": "America/Los_Angeles"
|
|
}
|
|
```
|
|
|
|
Push-token delete requests may send `tokenId` or `pushToken` either:
|
|
|
|
- as JSON in the request body
|
|
- or as query params on the `DELETE` URL
|
|
|
|
Using query params is safer when the client stack or proxy is inconsistent about forwarding `DELETE` bodies.
|
|
|
|
### Clock-in request example
|
|
|
|
```json
|
|
{
|
|
"shiftId": "uuid",
|
|
"sourceType": "GEO",
|
|
"deviceId": "iphone-15-pro",
|
|
"latitude": 37.4221,
|
|
"longitude": -122.0841,
|
|
"accuracyMeters": 12,
|
|
"proofNonce": "nonce-generated-on-device",
|
|
"proofTimestamp": "2026-03-16T09:00:00.000Z",
|
|
"overrideReason": "Parking garage entrance is outside the marked hub geofence",
|
|
"capturedAt": "2026-03-16T09:00:00.000Z"
|
|
}
|
|
```
|
|
|
|
### Location-stream batch example
|
|
|
|
```json
|
|
{
|
|
"shiftId": "uuid",
|
|
"sourceType": "GEO",
|
|
"deviceId": "iphone-15-pro",
|
|
"points": [
|
|
{
|
|
"capturedAt": "2026-03-16T09:15:00.000Z",
|
|
"latitude": 37.4221,
|
|
"longitude": -122.0841,
|
|
"accuracyMeters": 12
|
|
},
|
|
{
|
|
"capturedAt": "2026-03-16T09:30:00.000Z",
|
|
"latitude": 37.4301,
|
|
"longitude": -122.0761,
|
|
"accuracyMeters": 20
|
|
}
|
|
],
|
|
"metadata": {
|
|
"source": "background-workmanager"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Coverage incidents response shape
|
|
|
|
```json
|
|
{
|
|
"items": [
|
|
{
|
|
"incidentId": "uuid",
|
|
"assignmentId": "uuid",
|
|
"shiftId": "uuid",
|
|
"staffName": "Ana Barista",
|
|
"incidentType": "OUTSIDE_GEOFENCE",
|
|
"severity": "CRITICAL",
|
|
"status": "OPEN",
|
|
"clockInMode": "GEO_REQUIRED",
|
|
"overrideReason": null,
|
|
"message": "Worker drifted outside hub geofence during active monitoring",
|
|
"distanceToClockPointMeters": 910,
|
|
"withinGeofence": false,
|
|
"occurredAt": "2026-03-16T09:30:00.000Z"
|
|
}
|
|
],
|
|
"requestId": "uuid"
|
|
}
|
|
```
|
|
|
|
## 6) Why this shape
|
|
|
|
- frontend gets one host
|
|
- backend keeps reads, writes, and service helpers separated
|
|
- routing can change internally later without forcing frontend rewrites
|