Skip to content

Medical (EMR)

Medical Patient

MedicalPatients represents patients in a facility. It includes limited information about them that cannot be used to identify them (those are stored in PersonalDetails)

Searching For Patients

type SearchConfiguration {
  text: string // the search string to be used
  organization: string // the facility id to use to limit the number of results
}
type SearchConfiguration {
  text: string // the search string to be used
  organization: string // the facility id to use to limit the number of results
}

For easier search of MedicalPatients via their PersonalDetails, a convinience operator $search is implemented. it accepts a SearchConfiguration object that will be used to match the following fields:

  • name.firstName (fuzzy match, case insensitive)
  • name.lastName (fuzzy match, case insensitive)
  • name.firstName and name.lastName (when searchstring has the characted , will be used to search combination of both fields with last name first)

Connecting A Patient Account

a MedicalPatient can be associated to an owner account (aka PatientAccount) via 2 ways:

  • a privileged user updating MedicalPatient.account to the uid of an Account; or
  • creating an AccountInvitation with type patient w/c would send an invitation code to an account that it can use to signup and be automatically linked to a chosen MedicalPatient

Note

An account-invitation can be created using the account-invitations API's create method.

Merge Patient Profiles (and records)

2 patient profile (along with their records) can be merged using the merge patch operator. It will do the following merge strategy:

  • merge the to merge px' personal details to the target px's personal details (where target pxs takes priority)
  • mark to to merge px's profile (/medical-patients) as merged to the target px ToMergePx.mergedTo == TargetPx.id
  • archive the to merge px's profile
  • re-assign all records of to merge px to target px; i.e.
    • all medical records
    • all invoices, invoice items, and payments
    • diagnostic tests/records
    • insurance coverages
    • inventory transactions

Example

merge Px1 and Px2 where Px1 (target px) would be retained, and Px2 (to merge px) archived (but marked as merged to Px1)

sh
# merge Px2 to Px1
PATCH /medical-patients/<Px1.id>
{ merge: <Px2.id> }
# merge Px2 to Px1
PATCH /medical-patients/<Px1.id>
{ merge: <Px2.id> }

QR Code

Generates a JWT encoding selected data (set per service) including an access token that can be used to fetch the associated document without loggin in a privileged user.

Encoding

sh
# trigger the operator
GET /medical-patients/randomid?$generateQRCode=true

# responsed with a JWT which is expected to be encoded
# in the fronted to a QR image
ey****.***.***
# trigger the operator
GET /medical-patients/randomid?$generateQRCode=true

# responsed with a JWT which is expected to be encoded
# in the fronted to a QR image
ey****.***.***

Scanning and Parsing

The QR code must be decoded and parsed as a JWT. This JWT contains the ff standard fields (w/c may include additional fields depending on the service)

  • typ the type of the JWT which is usually the service where the QR code was generated
  • sub id of the document associated to the JWT
  • accessToken (optional) access token that can be used to re/query all relevant encoded data in the JWT
javascript
const decodedFromQRCode = 'ey***.***.****';
const payload = parsedJWTPayload(decodedFromQRCode);
// { typ: 'medical-patient', sub: 'randomid', accessToken: 'ey****.***.***' }

const requeried = await http.get(`/medical-patients/${payload.sub}`, {
  headers: { Authorization: `Bearer ${payload.accessToken}` }
});
// { id: 'randomid', ... }
const decodedFromQRCode = 'ey***.***.****';
const payload = parsedJWTPayload(decodedFromQRCode);
// { typ: 'medical-patient', sub: 'randomid', accessToken: 'ey****.***.***' }

const requeried = await http.get(`/medical-patients/${payload.sub}`, {
  headers: { Authorization: `Bearer ${payload.accessToken}` }
});
// { id: 'randomid', ... }

Medical Records

Creating Medical Records

Created Medical Records are drafts until finalized either via directly finalizing the MedicalRecord or via the cascading effect of finalizing a MedicalEncounter.

Finalizing Medical Records

Only when a MedicalRecord is finailized will history tracking be enabled on a MedicalRecord so finalizing it is important. There are 2 ways to finalize a MedicalRecord:

  1. Finishing a MedicalEncounter will finalize all MedicalRecords associated to it; or
  2. A special patch operator finalize: true can be use in the medical-records API's patch method.

Example

sh
curl <base-url>/medical-records/<medical-record-id> \
-X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <accessToken>"
-d "{
  "finalize": true,
}"
curl <base-url>/medical-records/<medical-record-id> \
-X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <accessToken>"
-d "{
  "finalize": true,
}"

Medical Record Types

Care Plans

A care plan is a type of the MedicalNote record. This record contains the following:

FieldTypeDescription
typeStringThe record type. I.e. care-plan
orderStringThe MedicalProcedureOrder id
textStringThe field for doctor's notes
doctorStringThe uid of the doctor who provided the record

Lab Orders

There are two medical records that comprise the lab test flow: lab-test-order and lab-test-result. It should bear mentioning at this point that there are several Diagnostic entities related to these records. The distinctions are as follows:

  • LabTestOrder: When a doctor identifies a need for lab tests, the doctor may produce a written order for the laboratory's reference. This is represented by a LabTestOrder, which is a MedicalRecord of type lab-test-order.
  • DiagnosticTest: The doctor may write down one or more tests as part of the written order. Digitally, this is represented by a LabTestOrder containing references to one or more DiagnosticTests. DiagnosticTests in this case are a stand-alone entity that contains information about the actual test to be done. For example, a single LabTestOrder may include the DiagnosticTest for Complete Blood Count and the DiagnosticTest for Urinalysis.
  • DiagnosticMeasure: A single DiagnosticTest may in fact be composed of one or more values that are measured as part of the overall test. These are called test measures, and they are represented by another entity called DiagnosticMeasure. A good example here is the Complete Blood Count: it is a single DiagnosticTest, but is composed of several DiagnosticMeasures for Red Blood Cell count, White Blood Cell count, Platelet count, Differential count (eosinophils, basophils, etc.), and so on.
  • LabTestResult: A doctor's order for lab tests is fulfilled by a laboratory that will produce results for each test declared in the order. Each test result will also have values for each test measure declared in the test. To manage this, a LabTestResult is used, which is a MedicalRecord of type lab-test-result. A LabTestResult represents the results for a single DiagnosticTest. It contains an array of result values, one for each DiagnosticMeasure associated with that DiagnosticTest. Since LabTestOrders can have more than one DiagnosticTest, it is expected that it can also have more than one LabTestResult.

With these distinctions and flow in mind, the shape of each relevant record can now be understood:

LabTestOrder

FieldTypeDescription
typeStringShould be 'lab-test-order'
testsLabTestOrderItem[]Array of items, which represent the tests in the order

LabTestOrderItem

FieldTypeDescription
idStringThe ID of the DiagnosticTest to be done
nameStringThe name of the DiagnosticTest

LabTestResult

FieldTypeDescription
orderStringThe ID of the LabTestOrder to which this result is attached
testStringThe ID of the DiagnosticTest for which this result is given
resultsLabTestResultItem[]Array of items, which represent each result value in the test

LabTestResultItem

FieldTypeDescription
testStringThe ID of the DiagnosticTest, for convenience
testNameStringThe name of the DiagnosticTest, for convenience
measureStringThe ID of the DiagnosticMeasure for which this result value is given
measureNameStringThe name of the DiagnosticMeasure, for convenience
valueanyThe result value for the DiagnosticMeasure
sivalueanyThe SI counterpart of value, if both Conventional units and SI units are provided
valuesany[]In some cases, DiagnosticMeasures require more than one value. Either value or values are filled up
sivaluesany[]The SI counterpart of values

Imaging Orders

Imaging orders follow the same flow as lab orders. In fact, they share most of the same entities. Here are some crucial differences:

  • ImagingTestOrder: Same as LabTestOrder, except this is a MedicalRecord of type imaging-test-order.
  • DiagnosticTest: Exactly the same use case as for lab. To distinguish the two use cases, DiagnosticTests have type laboratory if intended for LabTestOrders, and type radiology if intended for ImagingTestOrders.
  • DiagnosticMeasure: Unused in practice. Imaging tests produce images, which do not fall under the same test measure model. Instead, each imaging DiagnosticTest expects only a single, qualitative, rich-text report as its result.
  • ImagingTestResult: Same as LabTestResult, except this is a MedicalRecord of type imaging-test-result. As hinted above, there is only ever a single result: a qualitative rich-text report.

ImagingTestOrder

FieldTypeDescription
typeStringShould be 'imaging-test-order'
testsImagingTestOrderItem[]Array of items, which represent the tests in the order

ImagingTestOrderItem

FieldTypeDescription
idStringThe ID of the DiagnosticTest to be done
nameStringThe name of the DiagnosticTest

ImagingTestResult

FieldTypeDescription
orderStringThe ID of the ImagingTestOrder to which this result is attached
testStringThe ID of the DiagnosticTest for which this result is given
resultsImagingTestResultItem[]Array of only a single item: the rich-text report

ImagingTestResultItem

FieldTypeDescription
testStringThe ID of the DiagnosticTest, for convenience
testNameStringThe name of the DiagnosticTest, for convenience
valueanyThe rich-text report as an HTML string

Medical Forms

Medical forms is the generic medical record object for the following certificates.

  • Medical Certificate
  • Fitness Certificate
  • Health History
  • Waiver
  • Claims
  • Questionnaires
  • Generic - The generic version for flexible usage

This record has the following fields:

FieldTypeDescription
typeStringThe record type. Enum: med-certificate, fit-certificate, health-history, waiver, claims, questionnaire and general
orderStringThe order id
templateStringTemplate id. This record has a dynamic template that represents the layout of the form
valuesArrayThe values array of objects contains all of the data to be substituted to form's template layout

Here's a sample template data:

json
{
  "id": "5cc7f95b2d89a34340b79111",
  "type": "med-certificate",
  "template": "I, {doctor_name_0}, after careful examination hereby certify that {patient_name_0} who complained about {patient_complaint_0} was suffering from {patient_diagnosis_0}. \n<br>\nI consider that a period of absence from {custom_text_absence_from_0} from {date_0} to {date_1} is absolutely necessary for the restoration of patient's health.",
  "name": "Medical Certificate Template 2",
  "description": "Template 2. For general purposes (Without Follow-up date)"
}
{
  "id": "5cc7f95b2d89a34340b79111",
  "type": "med-certificate",
  "template": "I, {doctor_name_0}, after careful examination hereby certify that {patient_name_0} who complained about {patient_complaint_0} was suffering from {patient_diagnosis_0}. \n<br>\nI consider that a period of absence from {custom_text_absence_from_0} from {date_0} to {date_1} is absolutely necessary for the restoration of patient's health.",
  "name": "Medical Certificate Template 2",
  "description": "Template 2. For general purposes (Without Follow-up date)"
}

You'll notice that the template literals contains the keywords enclosed with opening and closing curly braces. These are the tokens to be use for interpolating the data from the values field.

Prescriptions

A Prescription is a medical record with type medication-order. This is the digital representation of the patient's medication order that contains a list of medicines. Medicines are populated in the field called items[Medicine]. The prescription document also contains the following information, clinic info, patient details, and medicines.

Prescription

FieldTypeDescription
typeStringMedical record type. I.e. medication-order
noteStringOptional doctor's note
itemsArrayAn array of Medicine objects

Medicine

FieldTypeDescription
medicineStringReference id to the medicine data.
brandNameStringBrand name of the medicine
genericNameStringGeneric name of the medicine
dispenseStringMedicine dispense
dosageSigStringMedicine dosage signature
dosageAmountNumberAmount of dosage
formulationStringMedicine formulation
formulationClassificationStringMedicine formulation classification
formulationFormStringMedicine form of formulation
frequencyStringMedicine intake frequency
noteStringOptional doctor notes

QR Code

Generates a JWT encoding selected data (set per service) including an access token that can be used to fetch the associated document without loggin in a privileged user.

Encoding

sh
# trigger the operator
GET /medical-records/randomid?$generateQRCode=true

# responsed with a JWT which is expected to be encoded
# in the fronted to a QR image
ey****.***.***
# trigger the operator
GET /medical-records/randomid?$generateQRCode=true

# responsed with a JWT which is expected to be encoded
# in the fronted to a QR image
ey****.***.***

Scanning and Parsing

The QR code must be decoded and parsed as a JWT. This JWT contains the ff standard fields (w/c may include additional fields depending on the service)

  • typ the type of the JWT which is usually the service where the QR code was generated
  • sub id of the document associated to the JWT
  • accessToken (optional) access token that can be used to re/query all relevant encoded data in the JWT
javascript
const decodedFromQRCode = 'ey***.***.****';
const payload = parsedJWTPayload(decodedFromQRCode);
// { typ: 'medical-record', sub: 'randomid', accessToken: 'ey****.***.***' }

const requeried = await http.get(`/medical-records/${payload.sub}`, {
  headers: { Authorization: `Bearer ${payload.accessToken}` }
});
// { id: 'randomid', ... }
const decodedFromQRCode = 'ey***.***.****';
const payload = parsedJWTPayload(decodedFromQRCode);
// { typ: 'medical-record', sub: 'randomid', accessToken: 'ey****.***.***' }

const requeried = await http.get(`/medical-records/${payload.sub}`, {
  headers: { Authorization: `Bearer ${payload.accessToken}` }
});
// { id: 'randomid', ... }

Medical Encounter

MedicalEncounters represents a patient's journey/visit. it would be used to associate multiple MedicalRecords, the Billing information, Diagnostic records, and other services rendered to the patient

Creating Encounters

A medical encounter can be started using the medical-encounters API's create method.

Notes

  • A create payload with invoice: true will auto create an associated BillingInvoice.

Finishing Encounters

MedicalEncounters should be marked as finished at the end of the actual patient visit as it would cascade to finalizing the associated MedicalRecords created during the visit (they would be considered drafts otherwise). To finalize an MedialEncounter, a special patch operator finish: true can be use in the medical-encounters API's patch method.

Example

sh
curl <base-url>/medical-encounters/<encounter-id> \
-X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <accessToken>"
-d "{
  "finish": true,
}"
curl <base-url>/medical-encounters/<encounter-id> \
-X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <accessToken>"
-d "{
  "finish": true,
}"

Released under the MIT License.