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