Plan-Based Default Permissions
Date: 2026-04-27Status: Approved
Background
All users currently get role defaults fromDEFAULT_PERMISSIONS_BY_ROLE in permissions.ts, regardless of the clinic’s subscription plan. Admin can override per-user after the fact via /staff/:id/permissions, but there are no plan-gated defaults.
The ssh & Associates tenant (Pro+, active) serves as the golden standard. Their receptionist has a hand-tuned permission set that will become the canonical Pro+ receptionist default.
Goal
Auto-assign the right permission baseline at invite acceptance based on the clinic’s subscription plan. Admin can still override any individual user’s permissions afterward.Permission Sets
Pro+ (price_pro_plus)
| Role | Basis |
|---|---|
| admin | All permissions (unchanged) |
| doctor | Full current DEFAULT_PERMISSIONS_BY_ROLE.doctor (unchanged) |
| receptionist | ssh & Associates golden standard (see below) |
| patient | Current defaults (unchanged) |
inventory.create,inventory.edit,inventory.adjust_stock,inventory.manage_supplierssettings.signatures.view,settings.signatures.manage
bridge.view(clinical tool, not front-desk)comms.bulk.send(admin-level blast)reports.stats(reporting is admin concern)audit.activity.view(admin concern)
Pro (price_pro)
Same as Pro+ except:
Doctor on Pro — remove from Pro+ doctor set:
ai.dicom_analysis.view,ai.dicom_analysis.runai.revenue_forecast,ai.churn_risk,ai.monthly_summarybridge.view,bridge.capturereports.financial,reports.revenue(keepreports.stats)billing.insurance.view,billing.insurance.create,billing.insurance.edit,billing.insurance.manage_attachmentsclinical.ipd.view,clinical.ipd.admit,clinical.ipd.edit,clinical.ipd.discharge
inventory.create,inventory.edit,inventory.manage_suppliers(keepinventory.view,inventory.adjust_stock,inventory.view_alerts)settings.signatures.manage(keepsettings.signatures.view)ai.daily_brief(keepai.appointment_nudges,ai.payment_reminder)lab.laboratories.view(keeplab.cases.*andlab.services.view)
Fallback (trial / no plan / unknown plan)
Use Pro defaults. Gives new clinics a functional but conservative baseline until a plan is confirmed.Architecture
1. permissions.ts — new exports
Add two new constants:
price_pro_plus → PRO_PLUS_PERMISSIONS_BY_ROLE, price_pro → PRO_PERMISSIONS_BY_ROLE, anything else → PRO_PERMISSIONS_BY_ROLE (safe fallback).
Keep DEFAULT_PERMISSIONS_BY_ROLE intact for backwards compatibility — nothing removes it.
2. getEffectivePermissions — signature change
3. Callers — pass plan ID
Two call sites need updating:api.ts /me endpoint — already fetches the clinic record which includes subscriptionPlanId. Pass it through.
auth.ts invitation acceptance — already has clinicId; add a single extra DB select for the clinic’s subscriptionPlanId before calling getEffectivePermissions, or fetch it as part of the existing clinic lookup.
No other callers need changing; they pass undefined for subscriptionPlanId and get Pro defaults as the safe fallback.
Data Flow
No DB Migration Needed
Existing users withpermissions: {} in userClinicAssignments already rely on in-code defaults. Switching from DEFAULT_PERMISSIONS_BY_ROLE to plan-gated defaults is transparent — their empty override is merged on top of the new defaults just as before.
Existing users with explicit overrides (like the ssh & Associates receptionist) are unaffected — their stored permissions remain and continue to override the base.
Out of Scope
- UI for plan-based permission comparison (admin can see effective permissions via existing staff page)
- Automatic re-sync when clinic upgrades/downgrades plans (existing users keep their stored overrides; only new invites get new defaults)
- Enterprise plan permissions (add when needed, defaults to all-on like admin)

