Skip to main content

API Endpoints

The Pivot backend ships its own auto-generated, always-in-sync OpenAPI spec. Don't duplicate it here; link to it.

The live API reference

The docs module (functions/modules/docs/) builds an OpenAPI 3.1 spec at boot from every Zod DTO registered in functions/modules/docs/logic/registry.ts. Served two ways:

  • Interactive Scalar UI/api-reference (embedded in this site; gated by @pivotapp.ca OAuth)
  • Raw JSON/api/openapi.json (committed snapshot at pivot-docs/static/api/openapi.json, refresh via npm run fetch-openapi-spec)

The upstream cloudfunctions.net/docs endpoint requires a Firebase Bearer token; the embedded viewer above is the way engineers should access the spec.

The router lives at functions/modules/docs/endpoints/apis/docs.router.ts:24-51. Spec is cached in-process and regenerated only on cold-start. If you add a new route to a module, register it in registry.ts or it won't appear in /openapi.json.

Modules covered by the OpenAPI spec

All 17 modules are registered. The OpenAPI tags list at the bottom of registry.ts is the source of truth.

ModuleNotable routes
salesGET /sales
integrationsGET /integrations, GET /integrations/status, GET /integrations/available[/:category], GET /integrations/:category/:slug
companiesGET/PATCH /companies/:id, GET/PATCH /companies/:id/settings, GET/PATCH /companies/:id/policies, GET/POST/PATCH/DELETE /roles*, AI-tips-rules (/companies/:id/ai-tips-rules*), AI usage + config (/companies/:id/ai-usage, /ai-config/*)
scheduleGET /shifts, POST/PUT/DELETE /shifts*, GET/PATCH /companies/:id/schedule-template
employeesGET/POST /employees, GET/PATCH/DELETE /employees/:id, plus availability / days-off / rates / positions / integration-ids sub-resources
attendanceGET /attendance*, POST /attendance/punches[/batch], PATCH/DELETE /attendance/punches/:id
payrollGET/PATCH /payroll/settings, GET /payroll/tip-outs, GET /payroll/issues
applicantsGET /applicants, GET/PATCH /applicants/:id
postsGET /posts, GET/POST/PATCH/DELETE /posts/:id
chatGET /chats, GET/POST /chats/:chatId/messages, PATCH/DELETE /chats/:chatId/messages/:messageId
ai-memoryGET/PATCH /ai-memory/company-profile, GET/POST/PATCH/DELETE /ai-memory/user-rules*, GET/POST/PATCH/DELETE /ai-memory/cron-tasks*
documentsGET /employees/:id/documents, POST /employees/:id/documents/drafts, POST /employees/:id/documents/upload, finalize / delete
calculatorPOST /calculate — deterministic arithmetic (used by the AI's calculate tool).
requestsGET /requests, POST /requests, PUT /requests/:id — shift-request CRUD.
pos-syncPOST /sync-pos-employees, POST /load-cached-pos-employees, POST /link-pos-employee, POST /create-bulk-pos-employees, POST /get-pivot-employees.
onboardingGET /onboarding/session/:userId, POST /onboarding/session/:userId/complete — auth via x-user-token header, not the standard Authorization: Bearer.
docsGET /openapi.json, GET /docs, GET / — the spec server itself.
authPOST /send-reset-link, /verify-authorization-code, /accept-{head-office,employee}-invitation (public) plus /init-session, /signup, /setup-password, /send-{head-office,employee}-invitation, /revoke-head-office-invitation, /admin/impersonate. Lives at functions/systems/auth/, outside functions/modules/.
integration-enginePOST /connection/{connect,disconnect,reconnect}, GET /connection/status/:companyId, POST /sync, POST /integration-id/:companyId, POST /partner-ingest/:provider, OpenTable partner flow. Distinct from the integrations catalog tag. Lives at functions/integration-engine/.

Note: schemas for calculator, pos-sync, onboarding, and integration-engine are mirrored inline in registry.ts rather than imported from common/dtos/. Keep them in sync when their runtime schemas change. auth and requests properly import from common/dtos/. Consolidating the inline schemas is open work.

Cloud Run service (pivotAiAgent)

Separately deployed Express app, not part of the Functions monolith. Source: functions/pivotAiAgent/index.ts. Local port: :3457.

MethodPathPurpose
POST/api/chatStreaming AI chat (SSE) — see Normal Conversations
POST/api/chat-asyncAlias for /api/chat
POST/api/widget-logFrontend widget-interaction telemetry
POST/uploadMultipart upload (CSV / image / sheet) for tool use
GET/download/:fileIdDownload generated Excel / PDF

Not currently in the OpenAPI spec. Auth is via firebaseToken in the request body (the SSE response means the EventSource API can't set custom headers, so the token rides in the payload).

Outside the OpenAPI spec (legacy / non-HTTP surfaces)

The repo has substantial code outside functions/modules/ that hasn't been migrated. These aren't part of the OpenAPI doc and most aren't MIA-relevant, but they exist:

  • Legacy onRequest endpoints (~35) — functions/http/** (clock in/out, posts, notifications, scheduled posts), functions/cron/** (Pub/Sub-fired cron jobs like saveYearlySalaryAttendance, autoClosePayroll, fetchWeatherForecast), functions/dashboard/** (labor cost, sales, SPLH metrics), integration webhooks (Clover, MYR, Givex, MaîtreD).
  • onCall (Firebase Callable) (~30) — functions/on-call/** (labor, sales, messages, legal/EULA, print PDF, copy company, etc.), functions/integrations/** (POS / payroll integration connect/disconnect/sync flows). These are invoked from the frontend via the Firebase SDK, not as REST endpoints.
  • Pub/Sub and DB triggers (40+) — functions/db/**, scheduled triggers across modules. RTDB write listeners that propagate changes (e.g., onPublishedSchedulePgSync, onMessageCreate).

If you need to wire MIA to one of these, see if a module-based equivalent exists first. If you do call legacy, use the same auth contract (Firebase ID token in Authorization header).

Shared middleware

Located at functions/shared/middleware/. Every Hono router in a module mounts these:

MiddlewareWhat it does
authMiddleware()Verifies Authorization: Bearer <token>, sets c.set('uid', ...) and c.set('token', ...). Throws 401 on missing/invalid.
companyScopeMiddleware({ from, key? })Reads companyId from query / param / body, validates against token.currentCompanyId. Allows cross-tenant for admin and isMiaAgent callers. Throws 403 on mismatch.
requireClaim(name)Generic "must have custom claim name set to true" gate. Used for isHeadOffice and similar.
adminMiddleware()Shorthand for requireClaim('admin').
requestCacheMiddlewareIn-memory cache layer (used by the requests module).
traceMiddleware()Attaches a trace ID to each request.
loggerMiddleware(moduleName)Structured JSON request logging, tagged with the module.
errorHandlerGlobal error-to-JSON formatter — produces consistent status + body shapes.

Note: the AI-usage / AI-config endpoints in the companies module don't use companyScopeMiddleware (admins on /stats aren't company members). They gate with an inline isAdmin() check on the admin custom claim — see functions/modules/companies/endpoints/apis/ai-usage.router.ts:91.

See Security Model for how custom claims and ownership overrides work.