Skip to content

RTM Billing System

Overview

The RTM (Remote Therapeutic Monitoring) billing system tracks patient engagement and therapist management time, generating CPT billing codes for reimbursement. The system consists of two complementary billing tracks:

Track Codes Based On Cycle
Workout-based 98975, 98985, 98977 Patient workout completion 30-day rolling windows
Time-based 98979, 98980, 98981 Therapist management time Calendar month

Both tracks share the same tenant feature flag, Zustand store (rtmBilling.ts), and dashboard UI (RtmBillingTabs).


CPT Codes Quick Reference

Workout-Based Codes (Patient Activity)

Code Trigger Description
98975 Patient invited to system Initial setup code
98985 2-15 distinct workout days in 30-day window Low-tier engagement
98977 16+ distinct workout days in 30-day window High-tier engagement

A "workout day" is any local calendar day with at least one completed workout. Sessions completed via the dashboard quick-finish ("Mark as finished") count the same as fully tracked sessions; they carry a quickCompleted flag for reporting.

Time-Based Codes (Therapist Activity)

Code Trigger Description
98979 10-19 minutes of management time in a calendar month First threshold
98980 20+ minutes of management time in a calendar month Base 20+ code
98981 Each additional 20-minute increment beyond 20 min Stacking add-on

Dashboard Structure

RTM Billing Tab
├── Billable Codes
│   └── Table of billing records (workout + time codes) with status management
├── In Progress
│   ├── [Sub-tab] Workout Progress — Kanban board
│   │   ├── Never Logged In
│   │   ├── Needs Engagement
│   │   └── On Track
│   └── [Sub-tab] Time Tracking — Table view
│       └── All patients with current month's accumulated time + progress bars
└── Insights
    └── Analytics: trends, code distribution, counts

Key UI Patterns

  • "My Patients" / "All Patients" toggle — Available across all tabs, filters by therapist connection
  • 5-minute client-side cache — All data endpoints use the same caching pattern in the Zustand store
  • Independent data fetching — Each view (kanban, time table, billable codes, insights) fetches from its own endpoint; switching tabs doesn't block on unrelated data
  • Optimistic updates — Settings changes update the UI immediately before server confirmation

Architecture

Clinician time on web and mobile

Clinician time from the web dashboard and the mobile app adds to the same patient/month record. Both record silently: patient-specific review, home-program work and eligible patient conversations count while the clinician is active, and recording pauses when the page or app is not in front and after 30 seconds without input. When the same clinician works on the web and in the app at once, each second is counted once.

Time recorded without a connection stays on the device and is sent later under the same clinician. Sample patients never accrue billing time.

Billing review and the live-communication attestation stay in the web dashboard, and any clinician in the clinic can review the clinic's time records (the My Patients switch narrows the list). Time that arrives after a month is marked billed adds to its total without changing its status. A timer or a projected code is not itself a billing approval. In a clinic that bills from the claims portal, a pending month of management time also has Live call: the therapist attests the live conversation without marking the month billed, and the clinic sends the claim, which marks it billed.

See the clinician time audit for screen coverage and verification.

Backend

All API endpoints live under /v2/rtm-billing and require authentication.

Endpoint Purpose Docs
GET /feature-access Check tenant feature flag Workout docs
GET / Workout billing records Workout docs
PATCH /:id/status Update workout billing status Workout docs
POST /recalculate Recalculate workout billing Workout docs
GET /in-progress Kanban data (workout progress) Workout docs
GET /time-progress Time tracking progress table Time docs
POST /time-sessions Record time sessions Time docs
POST /time-sessions/beacon Record time (beacon) Time docs
GET /time-billing Time billing records Time docs
PATCH /time-billing/:id/status Update time billing status Time docs
POST /time-billing/recalculate Recalculate time aggregates Time docs
GET /time-entries/:patientId/:calendarMonth Audit entries Time docs
GET /settings Therapist reimbursement rates Workout docs
PATCH /settings Update reimbursement rates Workout docs
POST /nudge Send nudge email Workout docs
GET /insights Analytics/trends Workout docs

Frontend Store

The main Zustand store (lib/stores/rtmBilling.ts) manages state for the entire RTM billing dashboard. A separate store (lib/stores/rtmTimeTracking.ts) manages the pending entry queue for the time tracking hook.

Database Models

Model Purpose Docs
RtmBillingRecord Workout-based billing records Workout docs
RtmBillingRecordEvent Audit log for workout status changes Workout docs
RtmTimeEntry Granular time tracking audit log Time docs
RtmTimeBillingRecord Monthly time aggregates Time docs
TherapistSettings Per-code reimbursement rates Workout docs

Enabling the Feature

-- Enable RTM billing (all features) for a tenant:
UPDATE "TenantSettings"
SET features = jsonb_set(COALESCE(features, '{}'::jsonb), '{rtmBillingDashboard}', 'true')
WHERE "tenantId" = <TENANT_ID>;

A single feature flag controls the entire RTM billing system (both workout-based and time-based).

Sample patients (internal/sample-patient.md) are never billed: billing and time-tracking code skips them, and the database refuses RTM rows that reference one.


Detailed Documentation

  • RTM CPT Billing Dashboard — Workout-based billing (98975, 98985, 98977): billing records, kanban board, insights, nudge emails, projected codes calculator
  • RTM Time-Based Billing — Time tracking system (98979, 98980, 98981): time tracking hook, heartbeat model, multi-tab coordination, beacon delivery, time progress table