Skip to main content

How UnitOps works

A short tour of the pieces behind the product. You do not need this to use UnitOps, but it helps when you are planning an integration or debugging an environment.

Components

ComponentTechnologyResponsibility
Web appNext.js (App Router) with TypeScript, Tailwind, and shadcn/uiEvery page you see, server-rendered
APIFastAPI on Python 3.12 with SQLAlchemy 2 asyncBusiness logic, KPI queries, AI orchestration, notification dispatch
Metadata databasePostgres with pgvectorChats, saved views, audit log, embeddings, forecast metadata
WarehouseSnowflake, Databricks, BigQuery, and othersPOS, labor, review, and weather source data, see Data stores
SchedulerAPScheduler in the API, or an external schedulerEvaluates notification rules on their cadence
AIA platform-managed provider (OpenAI, Anthropic, Google, or Vertex AI), set by the deployment operatorChat, summaries, and embeddings

Request flow

Browser
-> Next.js server components and server actions
-> FastAPI (JWT validated, tenant and location scoped)
-> Postgres metadata (row-level security per organization)
-> Warehouse (read-only, validated SQL)
-> AI provider (grounded with KPI and document context)

Forecasting model

  1. Historical actuals are read at the finest grain available.
  2. The selected projection model produces a value for each 15-minute slot across seven days.
  3. Manual adjustments are stored separately from the projection, so accuracy reporting can distinguish the model from the operator.
  4. Publishing freezes the week as the official plan.
  5. The labor matrix maps each period's projected volume to headcount per position, producing hours and cost.
  6. Actuals are compared to the published plan to produce variance and rolling accuracy (MAPE and bias) that alert rules read.

Notification pipeline

Rule evaluates (schedule or event)
-> org override decides whether the rule runs
-> recipients resolved from role defaults and user subscriptions
-> thresholds, quiet hours, mute, and cooldown filter deliveries
-> channel provider sends (in-app, email, push, Slack, Teams, SMS)
-> permanent failures land in the dead-letter queue for replay

Multi-tenancy

Every record carries an organization identifier, and many carry a project identifier too. Isolation is enforced in three layers: API middleware sets the tenant context from the validated token, repositories require the organization identifier as a parameter, and Postgres row-level security filters rows in the database itself. Generated warehouse SQL is statically checked for the tenant predicate before it executes.