Skip to main content
ORbit’s data model is built on PostgreSQL (via Supabase) with 70+ tables, 85 RPC functions, 25 triggers, and row-level security on every table.
This is the complete data model reference. For focused deep-dives, see the dedicated pages on the Milestone system, Scoring methodology, and Flag engine.

Core table groups

Identity and access

TableDescription
facilitiesMulti-tenant facility definitions (name, city, state, timezone)
usersAll platform users with facility scoping and access levels
user_rolesRole assignments (surgeon, anesthesiologist, nurse, tech, admin)

Cases

TableDescription
casesSurgical cases — the central entity. Has 8 triggers.
case_milestonesOne row per expected milestone per case (FK → facility_milestones)
case_completion_statsDenormalized stats cache with 41 columns (timing, financial, sequencing)
case_flagsFlag instances detected on cases
case_staffStaff assignments per case
case_device_companiesDevice company assignments with tray tracking
case_statusesStatus lookup (Draft, Scheduled, In Progress, etc.)

Milestone system (v2.0)

TableDescription
milestone_typesGlobal milestone definitions (read-only reference)
facility_milestonesPer-facility instances (FK → milestone_types via source_milestone_type_id)
milestone_templatesTemplate containers for grouping milestones
milestone_template_itemsIndividual items within templates (milestone + phase + sequence)
facility_phasesPhase definitions per facility
surgeon_template_overridesSurgeon-specific template assignments
milestone_type_id was dropped from case_milestones. All code must use facility_milestone_id. To get global type info, join through: case_milestonesfacility_milestonesmilestone_types.

Analytics

TableDescription
surgeon_scorecardsCached ORbit Score results (refreshed nightly)
flag_rulesConfigurable flag detection rules per facility
financial_targetsMonthly profit targets per facility

Materialized views

ViewDescription
surgeon_procedure_statsPer surgeon, per procedure type aggregation
facility_procedure_statsPer facility, per procedure type aggregation
surgeon_overall_statsPer surgeon across all procedures
These are refreshed when cases are validated.

Scheduling

TableDescription
or_roomsOperating rooms per facility
block_schedulesSurgeon block time reservations
facility_closuresFacility closure dates
facility_holidaysHoliday definitions

Financial

TableDescription
cost_categoriesExpense and revenue categories (debit/credit)
payersInsurance carriers
procedure_pricingPer-procedure cost items and reimbursement rates
surgeon_cost_overridesSurgeon-specific cost adjustments

Patient flow

TableDescription
patient_checkinsCheck-in records with checklist responses (JSONB)
patient_statusesStatus lookup for check-in workflow
escort_status_linksSecure family status tracking links

Soft deletes

20+ tables use the sync_soft_delete_columns() trigger. The rule: never hard delete — set is_active = false instead. The trigger enforces consistency:
  • Setting deleted_at → automatically sets is_active = false
  • Clearing deleted_at → automatically sets is_active = true
  • Setting is_active = false → automatically sets deleted_at = NOW()
  • Setting is_active = true → automatically clears deleted_at
Tables with soft deletes include: body_regions, cancellation_reasons, complexities, cost_categories, delay_types, facility_milestones, implant_companies, milestone_types, or_rooms, patients, payers, preop_checklist_fields, procedure_categories, procedure_types, users, and others.

Trigger system

The cases table has 8 triggers that handle:
  • Status transition validation
  • Stats pipeline (record_case_stats) — populates 41 columns in case_completion_stats
  • Flag detection invocation
  • Materialized view refresh
  • Audit logging
Test any changes to case INSERT/UPDATE logic carefully. The trigger cascade is complex and order-dependent.

Row-level security

Every table has RLS policies enforcing facility-scoped access:
  • Users can only see data within their assigned facility
  • Global admins bypass facility filters via the is_global_admin() helper
  • Device reps have restricted access to their assigned cases only
When writing new queries, always include .eq('facility_id', facilityId) and .eq('is_active', true) for soft-delete tables. RLS enforces facility scoping at the database level, but explicit filtering prevents accidental cross-facility data exposure at the application level.

Common patterns

Create a migration with the table definition, add RLS policies (use existing tables as reference), and add the sync_soft_delete_columns() trigger if the table should support soft deletes. Update the TypeScript types in types/.
Create the function in a migration. Use SECURITY DEFINER sparingly — prefer SECURITY INVOKER so RLS policies apply. Add TypeScript types for the function parameters and return type.
Be extremely careful — the cases table has 8 triggers that fire on INSERT/UPDATE. Test changes thoroughly, especially anything that modifies the validation flow or status transitions.

Next steps