Entity Profiles
Entity profiles are the aggregated view of all field values for a specific entity instance. They serve as the single source of truth for entity attributes and enable form auto-fill.
Patient profiles are split across two sources: the portable profile (patient_persons) and org-specific custom field values (custom_field_values). Other entity types (specialist, appointment, organization) use only custom field values.
Patient profiles
A patient's profile has two layers:
Patient #123 profile
├── Portable profile (patient_persons — shared across all orgs)
│ ├── name: "Ana Pop"
│ ├── date_of_birth: "1985-03-12"
│ ├── sex: "Female"
│ ├── blood_type: "A+"
│ ├── allergies: ["Penicillin", "Latex"]
│ ├── chronic_conditions: ["Hypertension"]
│ ├── occupation: "Engineer"
│ ├── residence: "Bucharest"
│ └── insurance_entries: [{"provider": "AXA", "number": "AXA-123", "type": "private"}]
│
└── Org-specific profile (custom_field_values — scoped to this org)
├── referral_source: "Physiotherapist"
└── vip_status: "true"API endpoint for the full combined patient profile:
GET /v1/patients/{id}/profileReturns both portable profile fields and org-specific custom field values, merged into a single response.
Reading the portable profile
SELECT pp.date_of_birth, pp.sex, pp.blood_type, pp.allergies, pp.chronic_conditions,
pp.occupation, pp.residence, pp.insurance_entries
FROM patients p
JOIN patient_persons pp ON pp.id = p.patient_person_id
WHERE p.id = ? AND p.organization_id = current_app_org_id()Reading org-specific profile values
SELECT cf.key, cf.label, cfv.value, cf.field_type, cf.options, cf.is_private
FROM custom_field_values cfv
JOIN custom_fields cf ON cf.id = cfv.custom_field_id
WHERE cfv.entity_type = 'patient'
AND cfv.entity_id = ?
AND cfv.organization_id = current_app_org_id()
ORDER BY cf.sort_order;Updating the patient profile
Updates to portable profile fields write to patient_persons. Updates to org-specific fields write to custom_field_values. The PUT /v1/patients/{id}/profile endpoint handles both in a single request by routing each key to the correct destination.
Example:
PUT /v1/patients/123/profile
{
"date_of_birth": "1985-03-12",
"blood_type": "A+",
"referral_source": "Physiotherapist"
}date_of_birth→ writes topatient_persons.date_of_birthblood_type→ writes topatient_persons.blood_typereferral_source→ upsertscustom_field_valuesfor the org'sreferral_sourcefield
Profile data flow (forms)
When a patient submits a form:
Form field with profile_field_key: "date_of_birth"
→ writes to patient_persons.date_of_birth
Form field with custom_field_id: 7 (referral_source)
→ upserts custom_field_values (entity_type='patient', entity_id=patients.id, custom_field_id=7)When a new form is created for the same patient at any org:
Form field with profile_field_key: "date_of_birth"
→ pre-filled from patient_persons.date_of_birth (same value, cross-org)
Form field with custom_field_id: 7 (referral_source)
→ pre-filled from custom_field_values (org-scoped — only pre-fills in the same org)See Form Auto-Fill → for the full mechanics.
Specialist profiles
Purpose: Provider credentials, specializations, certifications — all org-scoped.
Common fields:
license_number— Medical license IDspecialization— Primary specialtycertifications— Additional certificationslanguages— Spoken languages
API endpoints:
GET /v1/specialists/{id}/profile → Read profile
PUT /v1/specialists/{id}/profile → Update profileAccess control: Specialist can edit their own profile. Admin can edit all in org.
All specialist profile data lives in custom_field_values — there is no portable profile for specialists.
Appointment profiles
Purpose: Appointment-specific metadata, internal notes — all org-scoped.
Common fields:
referral_source— How patient found the clinic (if tracked per appointment)internal_notes— Staff notes (private)billing_code— Insurance billing coderoom_number— Physical location
API endpoints:
GET /v1/appointments/{id}/profile → Read profile
PUT /v1/appointments/{id}/profile → Update profileAccess control: Staff only (specialists, admins, customer support).
Organization profiles
Purpose: Org-level settings and configuration not modelled in the core organizations table.
API endpoints:
GET /v1/organizations/{id}/profile → Read profile
PUT /v1/organizations/{id}/profile → Update profileAccess control: Admin only.
Profile querying for segments
Portable profile fields on patient_persons are queryable via direct column access:
-- Find all patients with blood type A+
SELECT p.id AS patient_id, pp.name
FROM patients p
JOIN patient_persons pp ON pp.id = p.patient_person_id
WHERE p.organization_id = current_app_org_id()
AND p.deleted_at IS NULL
AND pp.blood_type = 'A+';
-- Find all patients with a Penicillin allergy
SELECT p.id AS patient_id, pp.name
FROM patients p
JOIN patient_persons pp ON pp.id = p.patient_person_id
WHERE p.organization_id = current_app_org_id()
AND p.deleted_at IS NULL
AND 'Penicillin' = ANY(pp.allergies);Org-specific custom field values are queryable via custom_field_values:
-- Find all patients with referral_source = "Physiotherapist"
SELECT cfv.entity_id AS patient_id, pp.name
FROM custom_field_values cfv
JOIN custom_fields cf ON cf.id = cfv.custom_field_id
JOIN patients p ON p.id = cfv.entity_id
JOIN patient_persons pp ON pp.id = p.patient_person_id
WHERE cf.organization_id = current_app_org_id()
AND cf.entity_type = 'patient'
AND cf.key = 'referral_source'
AND cfv.value = 'Physiotherapist';See Segments → for segment rule evaluation details.
Profile visibility and privacy
Fields with is_private = true on a custom field are:
- Visible in profile API responses (staff can see them)
- Visible during form filling (patients can fill them)
- Hidden from patient-facing PDFs and reports
Portable profile fields on patient_persons do not have an is_private flag — they are always visible to both staff and the patient themselves. Sensitivity is handled by the PHI encryption layer (phone_encrypted, emergency_contact_phone_encrypted).
Related documentation
- Patient Profile → — Portable profile model and data ownership
- Custom Fields → — Org-specific field library
- System Fields → — Stable field identifiers for PDFs and integrations
- Form Auto-Fill → — How forms read and write profile data
- Segments → — Querying profile data for patient cohorts