Skip to main content

Integrations & External Systems

Every external system PIVOT talks to, with the file paths to start reading. Newer providers live behind the abstraction layer at ../../functions/integration-engine/; older ones live as standalone folders under ../../functions/integrations/.


1. Point of Sale (POS)

POS providers feed daily sales and employee data into the pos-sync and sales modules.

Newer providers — registered in integration-engine:

ProviderFolderAuth
Toast../../functions/integration-engine/domains/pos/providers/toast/

Legacy providers — standalone folders under functions/integrations/:

ProviderFolderAuthNotes
Lightspeed../../functions/integrations/lightspeed/OAuthHourly sales cron, employee sync
Clover (OAuth)../../functions/integrations/clover/OAuthWebhook handler, hourly sales cron
Clover (API key)../../functions/integrations/clover-api-key/API keyNon-OAuth fallback
Cluster../../functions/integrations/cluster/API key (posApiKey)Hourly sales
Myr../../functions/integrations/myr/API keyDaily sales, 9am health check
Givex../../functions/integrations/givex/API key (via defineSecret)Hourly sales, webhook re-poll, employee search
Maitre'D../../functions/integrations/maitreD/Validated nightly via cron check-maitred-data.ts
Veloce../../functions/integrations/veloce/Sales import

Provider abstraction layer

The unified contract for new POS / reservation providers lives in ../../functions/integration-engine/:

  • bootstrap.ts — provider registry + plug-in init
  • core/BaseProvider, BaseClient, BaseManager, BaseProcessor, BaseOAuthAuth
  • domains/pos, reservation (one sub-folder per domain, with provider implementations inside)
  • endpoints/ — generic OAuth callback / webhook endpoints
  • observability/ — health monitoring, metrics (state in Firestore — see decisions.md → data-stores)
  • services/ — sync-job planner, pipeline factory

When adding a new provider, extend the engine. The legacy folders above predate it.


2. Payroll & HR

Payroll has two halves: export generation (web, client-side) and HR sync (backend cron).

Export formats — src/routes/PayrollNew/export-formats/

All under ../../src/routes/PayrollNew/export-formats/:

HR sync (backend)

ProviderFolderNotes
Nethris / EmployerD../../functions/integrations/nethris-employerD/Daily sync at 5am (0 5 * * *); init sync flow on CO_NUMBER creation
ADPn/a12 secrets defined in get-secret.ts (Connector + SSO + Subscription + certs) but no implementation in this repo

3. Reservations

ProviderFolderNotes
OpenTable../../functions/integration-engine/domains/reservation/providers/opentable/OAuth with token refresh; credentials read from process.env.OPENTABLE_CLIENT_ID/SECRET (legacy env-var pattern, not defineSecret)
Libro../../functions/integrations/libro/OAuth (client credentials). Reservation poll every 5 minutes; expected-revenue calc hourly. Standalone — not registered with the integration-engine

4. Billing — Stripe

  • Backend: ../../functions/systems/stripe/
    • handlers/stripe-webhook-handler.ts — webhook entry point; sub-handlers for customer.updated, subscription created/updated/deleted, invoice events
    • utils/fetch-stripe-subscriptions.ts — subscription fetch
    • utils/company-matching.ts — matches Stripe customers to companies
    • api/ — Payment Methods (list / attach), Setup Intents (create), Invoices (get), Billing info updates
  • Web UI: ../../src/routes/Billing/ — uses @stripe/react-stripe-js (Elements, PaymentElement, useStripe, useElements)
  • Stripe init: ../../src/routes/Billing/utils/stripe.tsloadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY)
  • API client: ../../src/routes/Billing/utils/api.ts — uses Firebase httpsCallable (not the shared ky client)

No Stripe Connect, Customer Portal, or Checkout. Subscription + invoice + payment-method flows only.


5. Communications

ChannelHow
EmailNodemailer + MJML templates. Service at ../../functions/services/mailer/; templates under ../../functions/services/mailer/templates/. SMTP creds via SMTP_* / NODEMAILER_* secrets
Push (FCM)Backend: firebase-admin getMessaging() — see ../../functions/services/notification/providers/push/push.transport.ts. Mobile: @react-native-firebase/messaging. iOS deliveries route through Firebase's APNS integration; the APNS_* secrets exist but are not used directly in code
Slack../../functions/utils/send-slack-notification.ts — error & integration alerts, uses SLACK_BOT_TOKEN

NotificationChannel.SMS and .EMAIL are declared in the notification-core constants but currently have no provider implementations — only push is wired.


6. Monitoring & Observability

SystemWhere
Rollbar — backend../../functions/utils/rollbar.ts
Rollbar — webConfigured in ../../src/index.tsx (RollbarProvider) and ../../src/config.ts; used via ../../src/utils/useHandleError.ts
Rollbar — mobilePIVOT-Mobile/src/utils/rollbar.ts
Logger (backend)../../functions/shared/infrastructure/logger.ts — wraps Firebase Functions' logger and enriches log lines with module. A loggerStore (AsyncLocalStorage) is set up for richer context but is not yet read at log time

7. Other External Services

ServiceUsed forWhere
WeatherAPI (api.weatherapi.com)Hourly forecast per company location, written into RTDB../../functions/cron/fetch-weather-forecast.ts, ../../functions/cron/write-weather-forecast-for-companies.ts
Temporal CloudWorkflow scheduler used by the AI memory module../../functions/modules/ai-memory/ (via @temporalio/client)
Pivot AI Agent (external Cloud Run)LLM-backed company/employee suggestions. The Anthropic SDK lives in functions/package.json but the actual Claude calls run in a separate Cloud Run service, not this repo. ai-memory is the RTDB + Temporal persistence layer that feeds itfunctions/modules/ai-memory/
MapboxMap rendering on mobile WeatherRadar@rnmapbox/maps in PIVOT-Mobile (src/routes/WeatherRadar/)

8. Cron / Scheduled Jobs

Legacy crons at ../../functions/cron/

Mostly v1 functions.pubsub.schedule(); one exception flagged below.

FilePurpose
auto-close-payroll.tsAuto-locks past payroll periods
check-maitred-data.tsValidates Maitre'D imports (1 11 * * *)
disable-inactive-integrations.tsMarks stale integrations as disabled
fetch-weather-forecast.tsPulls WeatherAPI forecasts
late-for-shift-notification.tsPushes notification if employee is late
mass-notification.tsBulk notification sender
process-scheduled-posts.tsv2 onTaskDispatched (not Pub/Sub) — publishes scheduled social posts
remove-old-drafts.tsDeletes stale schedule drafts
remove-unsent-chat-attachments.tsCleans up failed uploads
save-yearly-salary-attendance.jsBackfills annual data
update-availabilities.jsRecalculates employee availability
update-salary.jsRefreshes salary data
write-weather-forecast-for-companies.tsWrites forecasts to per-company paths

Module-scoped (Cloud Scheduler v2)

Under each module's endpoints/scheduled/. Example:

POS / integration crons (inside each provider folder)

ProviderScheduleFile
Lightspeed saleshourly (0 * * * *)functions/integrations/lightspeed/cron-lightspeed-daily-sales.ts
Clover saleshourly (0 * * * *)functions/integrations/clover/cron-clover-daily-sales.ts
Cluster saleshourly (0 * * * *)functions/integrations/cluster/cron-cluster-daily-sales.ts
Givex saleshourly (0 * * * *)functions/integrations/givex/cron-get-hourly-sales.ts
Myr health check9am daily (0 9 * * *)functions/integrations/myr/check-myr-integrations.ts
Nethris/EmployerD sync5am daily (0 5 * * *)functions/integrations/nethris-employerD/index.ts
Libro reservation pollevery 5 minutesfunctions/integrations/libro/cron/fetch-libro-reservations.ts
Libro expected revenuehourly (0 */1 * * *)functions/integrations/libro/cron/calculate-libro-expected.ts

Filenames containing "daily" are historical — most actually run hourly.


9. Secrets

Two patterns coexist:

Legacy: typed accessor — ../../functions/get-secret.ts

  • ALL_SECRETS is a const-asserted string array; SecretName is the derived union type (effectively type-safe, though not an enum).
  • 56 entries at last count, fetched from Google Secret Manager at cold start via onInit() and cached. Falls back to process.env[name] in dev.
  • Categories present: URL, RTDB_URL, Stripe (2), Lightspeed (4), Libro (4), Cluster (2), Clover (2), Myr (1), ADP (10), SMTP/Nodemailer (9), AppEncryption, APNS (3), Rollbar, Weather (2), Slack, env copy (3), DB (7).

New: defineSecret() from firebase-functions/params

Used by newer integrations. Examples:

For new code prefer defineSecret(). Don't expand the ALL_SECRETS list.

Rule

Never read process.env directly outside dev migrations. A small number of legitimate exceptions exist (e.g. FUNCTIONS_EMULATOR checks).


10. Frontend HTTP to Backend

Three patterns are in active use on web. Mobile mostly mirrors the ky pattern.

A. ky HTTP client — src/services/http.client.ts

  • Auto-injects Firebase ID token as Authorization: Bearer <token>.
  • Base URL chosen by REACT_APP_USE_EMULATOR:
    • emulator: http://localhost:5001/{projectId}/us-central1
    • prod: https://us-central1-{projectId}.cloudfunctions.net
  • Mobile equivalent: PIVOT-Mobile/src/services/httpClient.ts (same pattern, same URL shapes).
  • Also wrapped in a per-feature client: ../../src/modules/integrations/services/integration-client/integration.client.ts.

B. Firebase Callable Functions — httpsCallable

Used heavily, primarily in ../../src/utils/api.ts (e.g. findCompanyById, onCallEmployeeBreakToggler, getVeloceSearchedEmployees, acceptEula, printPdf, getLibroCompanies, …) and in Billing's api.ts for all Stripe operations. Different routing path than ky — these hit *.cloudfunctions.net/functionName directly with Firebase's callable envelope.

C. Axios (legacy)

Still used for a handful of endpoints (setEmployeeClockInV2, setEmployeeClockOutV2) and in some action creators (../../src/actions/notifications.ts, ../../src/actions/integrations.ts).

Direct RTDB / Firestore subscriptions

Bypass HTTP entirely. Authorization comes from the security rules in ../../database.rules.json and ../../firestore.rules.