OHMOHM Studio

Tools Portal

The non-clinical user portal at tools.ohm.doctor — medical coders, billing staff, and future analytics tools. Generic role with per-user feature flags so admins can grant exactly the tools each user needs.

View as Markdown

The Tools Portal at tools.ohm.doctor is a separate frontend for non-clinical users — medical coders, billing staff, and future analytics or audit tools. It shares the same backend, auth, and organisation scoping as the doctor app, but is completely sealed off from PHI (patients, visits, notes, audio recordings).

Currently ships with one tool — the Codes Tool (AI ICD-10 / LOINC / SNOMED extraction + search + crosswalks + favourites + lists). New tools land here as new feature flags without touching the role system.

The model

Role.OTHER  →  defines the access boundary
              (can log in, cannot see patient / visit / note data)

User.enabledFeatures  →  defines what they can do inside that boundary
                          (e.g. ["codes"]). Toggled per user by the
                          org admin.

Organization.features →  the paywall layer
                          (e.g. features.codes = true means the org has
                          been granted this tool — sales / billing gate).

A request is allowed when all three are true:

  1. JWT identifies a valid, active user in an active org.
  2. The org has the feature enabled (Organization.features.codes === true).
  3. The user has the feature in their list (User.enabledFeatures.includes("codes")).

Plus a global belt-and-braces interceptor (OtherRoleGuard) that rejects any sole-OTHER request to a path outside the codes-tool allowlist — defense in depth in case a new PHI route is added without its role decorator.

Architecture at a glance

┌──────────────────────────────────────────────────────────────────┐
│                       apps/api  (NestJS)                          │
│                                                                   │
│  ┌────────────────────┐   ┌──────────────────────────────────┐   │
│  │ JwtAuthGuard       │   │ OtherRoleGuard  (global)         │   │
│  └─────────┬──────────┘   │ Reject sole-OTHER outside        │   │
│            │              │ allowlist (/api/codes/*, /auth/*)│   │
│            ▼              └──────────────────────────────────┘   │
│  ┌────────────────────┐   ┌──────────────────────────────────┐   │
│  │ RolesGuard         │   │ RequiresFeatureGuard             │   │
│  │ (per-controller)   │   │ Two-layer: org features +        │   │
│  │ @ClinicalStaff(),  │   │ user.enabledFeatures             │   │
│  │ @Roles(OTHER, ...) │   └──────────────────────────────────┘   │
│  └────────────────────┘                                          │
└──────────────────────────────────────────────────────────────────┘
              │                                  │
              │                                  │
   app.ohm.doctor                       tools.ohm.doctor
   (doctors, nurses)                    (medical coders, billers)

Where it lives in the codebase

ConcernLocation
Schema (Role.OTHER, enabledFeatures, CodeFavorite, CodeList)apps/api/prisma/schema.prisma
Codes Tool moduleapps/api/src/codes-tool/
Curated crosswalk datasetapps/api/src/fhir/codes/crosswalks.json
RequiresFeature decorator + guardapps/api/src/auth/decorators/requires-feature.decorator.ts, apps/api/src/auth/guards/requires-feature.guard.ts
Global PHI boundaryapps/api/src/auth/guards/other-role.guard.ts
Frontend portalapps/tools/
Admin invitation UIapps/admin/src/pages/admin/InvitationsPage.tsx
Admin per-user featuresapps/admin/src/pages/admin/UsersManagement.tsx