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.ts — loadStripe(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.