Data Ownership Model

Patient = User Identity

Core principle: Every Patient is a User. PatientID = UserID. There is no separate OrganizationID on the User or Patient record — these entities are global across the entire platform.

A person exists once in the system. Organizations see the patient through org-scoped relationships (encounters, appointments, claims) but never "own" the patient record itself.

User AMI Schema

FieldTypeNotes
UserIDShortGUID (PK)Primary key, Base62 22-char
EmailEmailUnique, verified
PhonePhoneE.164 format
FirstNameString
LastNameString
DateOfBirthDate
IsActiveBoolSoft-delete flag
CreatedDateTimeUTCDateTimeAudit
ModifiedDateTimeUTCDateTimeAudit
ModifiedByUserIDShortGUIDAudit — FK to User

Patient AMI Schema

FieldTypeNotes
PatientIDShortGUID (PK)FK → User.UserID (same value)
PreferredNameStringDisplay name / nickname
SexStringAdministrative sex (M/F/O/U)
GenderIdentityStringUSCDI v4 value set
PreferredLanguageStringBCP-47 code
RaceStringOMB categories
EthnicityStringOMB categories
IsDeceasedBool
CreatedDateTimeUTCDateTimeAudit
ModifiedDateTimeUTCDateTimeAudit
ModifiedByUserIDShortGUIDAudit — FK to User

Short GUID Convention

All primary keys use a Base62-encoded Short GUID: 22 characters, URL-safe, case-sensitive.

PropertyValue
EncodingBase62 (0-9A-Za-z)
Length22 characters (fixed, zero-padded)
SourceStandard UUID v4 (128-bit)
AlgorithmUUID → strip hyphens → parse as 128-bit integer → repeatedly divide by 62 → map remainders to Base62 alphabet → pad to 22 chars

Worked Example

UUID: fb1e9c50-3f1c-4b8e-9a31-2b7c0e2d4a18
Short GUID: 7nKxC2Lh3vQrX8P4MsB1aF

JavaScript Reference Implementation

const BASE62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

function uuidToShortGuid(uuid) {
  const hex = uuid.replace(/-/g, '');
  let num = BigInt('0x' + hex);
  let result = '';
  while (num > 0n) {
    result = BASE62[Number(num % 62n)] + result;
    num = num / 62n;
  }
  return result.padStart(22, '0');
}

function shortGuidToUuid(short) {
  let num = 0n;
  for (const ch of short) {
    num = num * 62n + BigInt(BASE62.indexOf(ch));
  }
  const hex = num.toString(16).padStart(32, '0');
  return [hex.slice(0,8), hex.slice(8,12), hex.slice(12,16),
          hex.slice(16,20), hex.slice(20)].join('-');
}

Data Ownership Classification

Every entity is classified as Global (patient-centric, no org owner) or Org-scoped (belongs to a specific organization).

Global Entities (Patient-Owned)

EntityScopeOrganizationID?PatientID Partition?SourceOrganizationID?
UserGlobalNoN/ANo
PatientGlobalNoYes (PK)No
AllergyGlobalNoYesYes
MedicationGlobalNoYesYes
ConditionGlobalNoYesYes
VitalGlobalNoYesYes
LabResultGlobalNoYesYes
ImagingGlobalNoYesYes
ImmunizationGlobalNoYesYes
CarePlanGlobalNoYesYes
FamilyHistoryGlobalNoYesNo
SurgeryGlobalNoYesYes
ProblemGlobalNoYesYes
ProblemListGlobalNoYesNo
DiagnosisGlobalNoYesYes
ProcedureGlobalNoYesYes
PrescriptionGlobalNoYesYes
PatientDocumentGlobalNoYesYes
SensitiveClassGatingGlobalNoYesNo

Org-Scoped Entities

EntityScopeOrganizationID?PatientID Partition?SourceOrganizationID?
EncounterOrgYesYesNo
CareRelationshipOrgYesYesNo
PatientOrgMembershipOrgYesYesNo
AppointmentOrgYesYesNo
ClaimOrgYesYesNo
ClaimLineOrgYesYesNo
RemittanceOrgYesYesNo
DenialOrgYesYesNo
PatientStatementOrgYesYesNo
CoverageOrgYesYesNo
TaskOrgYesOptionalNo
ReferralOrgYesYesNo
ReferralResponseOrgYesYesNo
ScheduleOrgYesNoNo
SlotOrgYesNoNo
ResourceOrgYesNoNo
VisitTypeOrgYesNoNo
VisitStageOrgYesNoNo
WaitlistEntryOrgYesYesNo
InsurancePlanOrgYesNoNo
EligibilityCheckOrgYesYesNo
BenefitDetailOrgYesYesNo
PriorAuthOrgYesYesNo
ControlledSubstanceAuthOrgYesYesNo
PdmpQueryOrgYesYesNo
FormularyCheckOrgYesYesNo
OrderSetOrgYesNoNo
CdsRuleOrgYesNoNo
CdsHookEventOrgYesYesNo
PortalMessageOrgYesYesNo
PaymentOrgYesYesNo
WorkTaskOrgYesOptionalNo
PayerRuleOrgYesNoNo

Billing Nuance

CPT codes and procedure definitions are shareable clinical facts — they travel with the patient. Payments, claims, and remittances are private to the organization that generated them. Org B cannot see Org A's claim details even for the same patient.

Read-Access Audit Trail

Every read of clinical data generates an audit entry recording who, what, when, how, and why.

Authorization Chain Types

Chain TypeDescription
CareOrgMemberStaff at the patient's care organization (active CareRelationship)
FamilyProxyFamily member with patient-granted proxy access
ExternalSpecialistProvider at a referred-to organization (active Referral)
TEFCACross-network query via eHealth Exchange / TEFCA QHIN
BreakTheGlassEmergency override — requires justification, triggers alert
ResearcherIRB-approved de-identified or limited data set access

AuditLog AMI Schema

FieldTypeNotes
PatientIDShortGUIDPartition key — FK to Patient
ActorUserIDShortGUIDWho performed the action
AuditLogIDShortGUID (PK)Primary key
ActionTypeStringRead, Write, Delete, Export, BreakTheGlass
ResourceTypeStringEntity name (e.g., Medication, Encounter)
ResourceIDShortGUIDID of the accessed record
AccessChannelStringWeb, Mobile, API, FHIR, Bulk
AuthorizationChainTypeStringSee chain types above
AuthorizationChainDetailJSONFull chain: org, role, relationship IDs
JustificationStringRequired for BreakTheGlass; optional otherwise
SourceIPStringClient IP address
EventDateTimeUTCDateTimeWhen the event occurred
CreatedDateTimeUTCDateTimeAudit
ModifiedDateTimeUTCDateTimeAudit
ModifiedByUserIDShortGUIDAudit
PreviousHashStringHash-chain integrity — SHA-256 of prior entry

Classification Flow

graph TD
  A["New Entity"] --> B{"Has OrganizationID?"}
  B -- No --> C["Global Entity"]
  B -- Yes --> D["Org-Scoped Entity"]
  C --> E{"Clinical fact?"}
  E -- Yes --> F["Add SourceOrganizationID
for provenance"] E -- No --> G["Pure global
(User, Patient, ProblemList)"] D --> H{"Patient-related?"} H -- Yes --> I["PatientID partition key"] H -- No --> J["Org-only partition
(Schedule, Slot, Resource)"]

Authorization Evaluation Flow

graph TD
  REQ["Data Access Request"] --> AUTH["Identify Actor & Role"]
  AUTH --> RBAC["RBAC Check:
Role has permission?"] RBAC -- No --> DENY["Deny"] RBAC -- Yes --> ABAC["ABAC Check:
Context attributes match?"] ABAC -- No --> BTG{"Break-the-Glass
available?"} BTG -- No --> DENY BTG -- Yes --> JUSTIFY["Require Justification"] JUSTIFY --> ALERT["Trigger Alert + Audit"] ALERT --> GRANT ABAC -- Yes --> SCOPE["Scope Check:
CareRelationship or
valid AuthorizationChain?"] SCOPE -- No --> DENY SCOPE -- Yes --> GRANT["Grant + Audit Entry"]

Provenance Model & Trust Tiers

Full provenance taxonomy lives in the PMR Deep Exemplar. The complete model — (a) internal canonical provenance, (b) external inbound provenance, (c) external outbound provenance, (d) trust-tier definitions, and (e) conflict-handling rules — is documented with worked examples in PMR Deep Exemplar → Provenance Model (see also Trust tier definitions).