Skip to content

Patients

A patient is a real-world person receiving care. Their portable profile travels with them across clinics. Their clinical history stays private to each clinic.


What this enables

  • A patient registers once and brings their profile — demographics, blood type, allergies, insurance — to every clinic they visit on the platform, without re-entering anything
  • A daughter can book appointments and manage care for her elderly father who has no email or account
  • Each clinic has its own patient list with its own clinical history — appointments, forms, files, and treatment plans from Clinic A are never visible to Clinic B
  • Patient records are never deleted, only soft-deleted — preserving the clinical history required by HIPAA
  • Admins can temporarily act on behalf of a patient (impersonation) to provide support, such as helping fill out a form
  • Patients can be grouped dynamically into segments based on their profile data, form answers, and appointment history

How it works

The design separates two concerns:

patient_persons  (who the person is — owned by the patient, portable)
    └── patients @ Clinic A  (that they are a patient here — owned by the clinic)
    └── patients @ Clinic B  (that they are a patient here — owned by the clinic)

When a specialist at Clinic B looks up a patient, they see the person's name immediately. Once the patient signs the profile-sharing consent form, the specialist also sees date of birth, blood type, allergies, and insurance — without re-entry. They never see anything from Clinic A's appointments, forms, or files.

The portable profile

patient_persons is the patient's universal health identity. It holds:

  • Core identity: name
  • Demographics: date of birth, sex, phone, occupation, residence
  • Universal health facts: blood type, allergies, chronic conditions
  • Emergency contact: name and phone
  • Insurance: a list of policies (employer, private, state)

This data is owned by the patient, not the org. However, a clinic can only see the full profile after the patient consents — see Profile sharing consent below.

Family and dependent accounts

A user account (login) and a patient identity are separate things. One login can manage multiple patient identities — useful when a family member handles someone's healthcare:

Ana (user_id: 42, email: [email protected])
  ├── manages herself        → patient_persons id:1  (user_id: 42)
  └── manages her father     → patient_persons id:2  (user_id: NULL — no account)

When Ana logs in, she sees a "booking for" switcher. Appointments, forms, and treatment plans she manages for her father are linked to his identity (id:2), not hers. If her father later creates his own account, the link is set and all his history is preserved.

patients is a thin table that records that a person is a patient at a specific org. It holds nothing about the person — just the relationship and any external system references.

When a patient registers at a new clinic, the clinic can only see the patient's name — the minimum needed for scheduling. The rest of the portable profile (DOB, blood type, allergies, insurance, etc.) is hidden until the patient explicitly consents.

This is controlled by a profile_shared boolean on the patients table (the org-patient link):

profile_sharedWhat org staff sees
false (default)person.name only — enough to schedule appointments
trueFull portable profile — DOB, blood type, allergies, insurance, etc.

The patient themselves always sees their own full profile regardless of this flag.

How it works:

  1. Patient is onboarded at a new clinic → patients record created with profile_shared = false
  2. Automation rules create a "Profile Sharing Consent" disclaimer form (blocking)
  3. Patient signs the consent form
  4. System sets profile_shared = true on the patients record
  5. Clinic staff now sees the full portable profile

This consent is per-org — the patient must consent separately for each clinic. Signing at Clinic A does not grant Clinic B access.

Enforcement: RLS on patient_persons stays permissive (org staff can read the row for JOINs and name display). The field-level filtering is enforced at the application layer — the API strips extended fields when profile_shared = false.

What crosses clinic boundaries — and what doesn't

The portable profile and clinical records sit in two different legal categories:

DataCrosses clinics?Why
Portable profile (name, DOB, blood type, allergies, insurance)Yes — with consentThis is the patient's own identity data. They carry it themselves, like handing an insurance card to a new doctor.
Clinical records (appointments, forms, reports, treatment plans, files)No — neverThese are created by the clinic in the context of a care relationship. The clinic is a data controller for this information.

If a patient needs to share a report from Clinic A with Clinic B, they download the PDF from their portal and upload it at the new clinic — the same workflow used in traditional healthcare.

The platform does not broker cross-clinic document sharing. Under HIPAA, cross-provider sharing requires formal written patient authorization (45 CFR § 164.508) — a higher bar than the consent gate used for profile sharing. Under GDPR, it constitutes a cross-controller data transfer. Facilitating this would classify RestartiX as a Health Information Exchange (HIE), bringing significantly heavier regulation.

Org-specific custom fields

Beyond the portable profile, clinics can define their own patient fields — referral source, preferred training surface, VIP status, etc. These are org-scoped and only visible within that clinic. See Custom Fields →.


Technical Reference

Everything below is intended for developers.

Data model

users (auth identity — managed by Clerk)
    └── patient_persons (portable profile — patient-owned, no org_id)
            ├── patient_person_managers (who can manage this person)
            └── patients (org-patient link — one per org per person)
                    └── organization_id (tenant isolation)

patient_persons — portable profile columns

ColumnTypeNotes
idbigserialInternal identity ID
user_idbigint, nullable, uniqueLink to users table — NULL if no account
nametextFull name
date_of_birthdate
sextextMale / Female / Other / Prefer not to say
phone_encryptedbyteaAES-256-GCM encrypted
occupationtext
residencetext
blood_typetextA+, A-, B+, B-, O+, O-, AB+, AB-
allergiestext[]Array of allergy names
chronic_conditionstext[]Array of condition names
emergency_contact_nametext
emergency_contact_phone_encryptedbyteaAES-256-GCM encrypted
insurance_entriesjsonbArray of {provider, number, type} objects
ColumnTypeNotes
idbigserialInternal patient ID
organization_idbigintTenant isolation
patient_person_idbigintLink to patient_persons
profile_sharedbooleanfalse = org sees name only; true = full profile visible
consumer_idtextExternal system identifier (legacy migration)
deleted_attimestamptzSoft delete — records are never hard-deleted (HIPAA)

patient_person_managers

ColumnTypeNotes
patient_person_idbigintThe person being managed
manager_user_idbigintThe user managing them
relationshiptextself / parent / child / spouse / sibling / caregiver / other

Access control (RLS policies)

patient_persons (no org_id — user-based RLS):

  • Superadmins — all records
  • The person themselves or anyone managing them — their own record via current_user_patient_person_ids()
  • Org staff — can read patient_persons for anyone registered at their org (joined through patients)

patients (org-scoped):

  • Superadmins — all patients across all orgs
  • Admin / Specialist / Customer Support — all patients in their org
  • Patient / manager — their own and managed records via current_user_patient_person_ids()

RLS helper function:

sql
-- Returns all patient_person IDs the current user can act on behalf of:
-- their own identity + any dependents they manage
current_user_patient_person_ids() → SETOF BIGINT

This function is used in RLS on patients, appointments, forms, and any table that references patient_person_id.

Org-specific profile data

For data that is genuinely org-specific (not part of the universal profile), clinics use custom fields:

custom_field: {id: 7, entity_type: "patient", key: "referral_source", org_id: X}
   └── custom_field_value: {entity_type: "patient", entity_id: patients.id, custom_field_id: 7, value: "Physiotherapist"}

These values are org-scoped and not shared with other clinics. See Custom Fields →.

What each clinic sees

When a patient registers at a new clinic:

DataBefore consentAfter consentNotes
NameAlways visible (minimum for scheduling)
DOB, sex, phone, occupation, residenceRequires profile_shared = true
Blood type, allergies, chronic conditionsRequires profile_shared = true
Emergency contact, insuranceRequires profile_shared = true
Appointments at other clinicsOrg-scoped, never shared
Forms filled at other clinicsOrg-scoped, never shared
Files uploaded at other clinicsOrg-scoped, never shared
Treatment plans from other clinicsOrg-scoped, never shared
Custom field values from other clinicsOrg-scoped, never shared

Segments

Patients can be grouped by rules across three data sources:

  • Portable profile fields (patient_persons columns)
  • Org-specific custom field values
  • Form responses
  • Appointment history

Example: patients where blood_type = 'A+' AND forms.values.pain_level = 'high' AND appointments.count >= 2.

See Segments → for implementation details.