Therapist-Patient Assignment¶
Overview¶
The therapist-patient assignment feature allows clinics to assign multiple therapists to a single patient. Previously, a patient was connected only to the therapist who invited them. With this feature enabled, any therapist in the clinic can be assigned to any patient — both at invite time and after onboarding.
The feature is gated by the therapistPatientAssignment feature flag (per-tenant). See docs/feature-flags.md for how flags work.
Assignment Flows¶
Flow 1: At Invite Time¶
When inviting a new patient via the "Invite New Patient" modal, the inviting therapist can optionally select additional therapists to assign:
- Therapist opens the invite modal and fills in patient details
- If the feature flag is enabled and the clinic has other therapists, an "Also assign to" section appears below the form fields
- For clinics with 5 or fewer other therapists, toggle-style pill buttons are shown inline
- For clinics with more than 5 therapists, a searchable multi-select dropdown is shown instead
- On submit, the backend creates the primary doctor-patient connection as usual, then creates additional connections for each selected therapist
The additional therapist IDs are sent as additionalTherapistIds in the invite payload. The backend validates each therapist (active, approved, same tenant, type DOCTOR) before creating the connection.
Flow 2: From the Dashboard (Post-Onboarding)¶
Therapists can manage assignments from two places on the dashboard:
All Active Patients Table¶
When viewing the "All Active Patients" tab (requires allActivePatientsDashboard flag), a "Therapists" column appears showing:
- Avatar bubbles with therapist initials (current user highlighted in blue, others in gray)
- A "+N" badge when there are multiple therapists
- Quick-action buttons:
- Assign to Me — appears if the current therapist is not yet connected to that patient
- Add therapist — opens the assign modal to pick a different therapist
Since 2026-10-06 a search in the My Active Patients table also reaches the clinic's other therapists' patients (with allActivePatientsDashboard): matches outside the list appear under a Not your patients heading with the patient's therapists and Assign to Me (with therapistPatientAssignment), and matches among archived patients under Archived (opening one restores them, as in the archived view). In All Active Patients a search adds the Archived heading. The search takes the name in any word order, the email and phone digits. The app's directory search does the same under the same headings (see mobile-doctor-dashboard.md), and the add-patient form's existing-patient check offers Assign to Me for a colleague's patient on the web (see internal/patient-onboarding.md).
Patient Management Modal¶
When opening a patient's management modal, a "Therapist Management" section shows:
- A list of all connected therapists (current user marked with "(You)")
- Remove buttons beside therapists when more than one therapist is connected
- Assign to Me button — if the current therapist is not connected
- Add therapist button — opens the assign therapist modal
Mobile Patient Detail¶
On the mobile doctor patient detail screen, tenants with the feature enabled can manage the same care-team assignment from the patient's Care team section:
- Current connected therapists are listed with
(you)next to the viewer - Inline remove buttons appear when more than one therapist is connected
- Assign to me appears when the viewer is browsing a patient outside their caseload
- Add therapist opens a searchable mobile picker that excludes therapists already assigned to the patient
Assign Therapist Modal¶
A dedicated modal for assigning a patient to a specific therapist:
- Shows the patient name being assigned
- Searchable list of available therapists (excludes already-connected ones)
- Single-select a therapist, then confirm
- On success, the store updates optimistically (no page reload needed)
API Endpoints¶
| Endpoint | Method | Purpose |
|---|---|---|
/v2/doctor/patients/tenant-doctors |
GET | Lists active, approved therapists in the tenant (excludes the caller). Requires therapistPatientAssignment flag. |
/v2/doctor/patients/assign |
POST | Assigns a patient to a target therapist. Body: { patientId, targetDoctorId? }. If targetDoctorId is omitted, assigns to the caller (self-assign). Validates the target doctor and patient belong to the same tenant. Reactivates archived connections if one exists. |
/v2/doctor/patients/unassign |
POST | Removes a therapist from a patient by soft-archiving the active connection. Body: { patientId, targetDoctorId? }. If targetDoctorId is omitted, removes the caller. The backend rejects removal of the last active therapist. |
All assignment endpoints are guarded by the therapistPatientAssignment feature flag and return 403 if the flag is not enabled.
The existing getAllTenantPatients endpoint was also modified to return connectedTherapists (array of { id, firstName, lastName }) and isConnectedToMe (boolean) for each patient. The getPatientsSummary ("My Patients") endpoint conditionally includes the same fields when the therapistPatientAssignment flag is enabled for the tenant. Since 2026-10-06 every route under /v2/doctor requires the DOCTOR role (requireDoctor on each route of src/routes/doctor/index.ts), and so do the legacy GET /active-patients (the patient-search page's list) and GET /doctor/patients/:patientId/workouts; a patient account is refused with 403. Before, several of these (the patient summary, workout sessions, analysis, HEP management, AI settings, notes, plan creation and editing, assign and unassign, tenant doctors) checked only the patient-access helper, which in a clinic-wide tenant lets any signed-in caller through. Workout recordings (GET /doctor/recordings/:recordingId/url, GET /workout-recording/:workoutId, GET /workout-recordings/patient/:patientId) are for the recording's patient and for a therapist with access to that patient (a workout without a patient: its own therapist); anyone else gets 403 or 404. POST /workout-recording/upload-url follows the same rule and takes only an exercise of the given workout.
Frontend Components¶
| Component | Location | Purpose |
|---|---|---|
AssignTherapistModal |
components/doctor/dashboardV2/AssignTherapistModal.tsx |
Modal with searchable therapist list for single-select assignment |
TherapistBubbles |
components/ui/TherapistBubbles.tsx |
Compact avatar bubbles showing assigned therapists with initials |
TherapistMultiSelect |
components/ui/TherapistMultiSelect.tsx |
Searchable multi-select dropdown used during invite (>5 therapists) |
Store Actions¶
The doctorDashboard Zustand store (lib/stores/doctorDashboard.ts) has two new actions:
fetchTenantDoctors()— callsGET /v2/doctor/patients/tenant-doctorsand caches the result intenantDoctorsassignPatientToDoctor(patientId, targetDoctorId?)— callsPOST /v2/doctor/patients/assignand performs an optimistic update ofconnectedTherapistson the patient in bothpatientsandallTenantPatientsarraysunassignTherapistFromPatient(patientId, targetDoctorId?)— callsPOST /v2/doctor/patients/unassignand removes the therapist from cachedconnectedTherapists; self-removal also clearsisConnectedToMe
Removal uses the same soft-archive fields as patient archiving. A patient must always retain at least one active therapist connection, so the server returns 400 if the request would remove the last therapist.
Types¶
TherapistInfo (lib/types/patient.ts):
PatientSummaryV2 gained two optional fields:
connectedTherapists?: TherapistInfo[]isConnectedToMe?: boolean
Key Files¶
| File | Role |
|---|---|
OktaPT-API/src/routes/doctor/controller.ts |
getTenantDoctors, assignPatientToDoctor controllers; modified getAllTenantPatients |
OktaPT-API/src/routes/doctor/index.ts |
Route definitions for new endpoints |
OktaPT-API/src/routes/user/onboarding.controllers.ts |
Modified invitePatientWithMagicLink for additionalTherapistIds |
OktaPT-FE/components/doctor/dashboardV2/AssignTherapistModal.tsx |
Assign therapist modal |
Okta-Mobile/components/doctor/AssignTherapistModal.tsx |
Mobile assign therapist modal |
OktaPT-FE/components/ui/TherapistBubbles.tsx |
Therapist avatar bubbles |
OktaPT-FE/components/ui/TherapistMultiSelect.tsx |
Searchable multi-select |
OktaPT-FE/components/InviteNewPatientModal.tsx |
Modified invite modal |
OktaPT-FE/components/doctor/dashboardV2/PatientManagementModal.tsx |
Modified management modal |
OktaPT-FE/components/doctor/dashboardV2/PatientManagementTable.tsx |
Modified patient table |
OktaPT-FE/lib/stores/doctorDashboard.ts |
Store with fetchTenantDoctors, assignPatientToDoctor |
OktaPT-FE/lib/types/patient.ts |
TherapistInfo type, extended PatientSummaryV2 |
OktaPT-FE/pages/doctor/dashboard.tsx |
Dashboard wiring for assign handlers |
Okta-Mobile/app/(doctor)/patient/[id].tsx |
Mobile patient detail care-team assignment |
Okta-Mobile/services/doctorPatientsService.ts |
Mobile patient assignment API wrapper |