Sending Orders
For a vendor-neutral overview of diagnostic ordering concepts and the FHIR data model, see Labs & Imaging and Order Labs and Imaging.
This guide explains how laboratory orders work in the Medplum-Health Gorilla labs integration.
The high-level workflow for sending laboratory & imaging orders is:
- Present the provider with the appropriate lab order form (CPOE)
- Create the appropriate FHIR resources based on that information
- Execute the appropriate Medplum Bot to send the order to the performing lab.
Key Concepts
Laboratory ordering involves unique complexities: coordinating between providers, labs, and collection facilities; handling physical specimens; and gathering clinical data. Understanding these core concepts is essential before implementing the technical details.
The following concepts form the foundation of laboratory ordering:
| Concept | Description | Importance |
|---|---|---|
| Order | Request for one or more lab tests, uniquely identified and tracked. | Organizes tests, specimens and results. Maintains chain of custody and enables status tracking. |
| Test | Specific diagnostic procedure with defined requirements for specimen and data collection. | Determines specimen needs, required clinical data, and handling protocols. Impacts turnaround times and costs. |
| Specimen | Physical sample (blood, urine, etc.) for analysis. | Samples are only valid for a certain amount of time after collection. Accurately measuring sample collection time is critical for accurate processing Samples can either be collected by the performing lab, or by the requesting provider. |
| Ask on Order Entry (AOE) | Required questions for some labs to gather additional clinical context for test processing. | Affects lab processing, result interpretation, and billing. Missing data can cause rejections or delays. |
| Order Splitting | Breaking a single order into multiple independent orders, typically when tests require different specimen types. | Allows labs to process specimens independently and maintain separate workflows for different test types. Prevents delays when specimens are collected at different times. |
Creating an Order Form in React
Medplum provides a "headless UX" approach through specialized React components for building laboratory order forms.
Core Components
The integration centers around two main components:
HealthGorillaLabOrderProvider: Context provider for state managementuseHealthGorillaLabOrder: Hook for accessing state and operations
State Management
The hook manages a comprehensive state object:
type HealthGorillaLabOrderState = {
performingLab: LabOrganization | undefined;
selectedTests: TestCoding[];
testMetadata: Record<string, TestMetadata>;
diagnoses: DiagnosisCodeableConcept[];
billingInformation: BillingInformation;
specimenCollectedDateTime: Date | undefined;
orderNotes: string | undefined;
};
Building an Order Form
You can find an open-source example order form here. Feel free to use this as a starting point.
- Wrap your application:
function OrderPage() {
return (
<HealthGorillaLabOrderProvider>
<OrderForm />
</HealthGorillaLabOrderProvider>
);
}
- Initialize with context:
function OrderForm() {
const [patient, setPatient] = useState<Patient>();
const [requester, setRequester] = useState<Practitioner>();
const labOrderReturn = useHealthGorillaLabOrder({
patient,
requester,
});
}
- Access operations:
const {
state,
searchAvailableTests,
setTests,
setDiagnoses,
updateBillingInformation,
setSpecimenCollectedDateTime,
setOrderNotes,
createOrderBundle,
} = labOrderReturn;
| Command | Purpose |
|---|---|
searchAvailableTests | Uses the autocomplete bot to fetch available lab tests from Health Gorilla's API based on search string |
searchAvailableLabs | Uses the autocomplete bot to fetch available diagnostic labs from Health Gorilla's API based on search string |
setTests | Updates selected tests in order state |
setDiagnoses | Updates ICD-10 diagnosis codes in order state |
updateBillingInformation | Updates payment details (patient, insurance, customer account) |
setSpecimenCollectedDateTime | Sets when specimens were/will be collected |
setOrderNotes | Adds notes/instructions for the lab |
createOrderBundle | Creates FHIR resources for the complete order |
setPerformingLab | Sets which lab will process the tests |
setPerformingLabAccountNumber | Overrides the practice-level lab account number for the order (see Lab Account Numbers) |
validateOrder | Checks order for required fields and valid data |
Example
Lab Selection:
<MyAutoComplete
label="Performing Lab"
loadOptions={searchAvailableLabs}
onChange={(e) => {
setPerformingLab(e.value as Organization);
}}
/>
Test Selection:
<MyAutoComplete label="Selected tests" loadOptions={searchAvailableTests} onChange={setTests} />
Diagnosis Code Selection
<ValueSetAutocomplete
label="Diagnoses"
binding="http://hl7.org/fhir/sid/icd-10-cm"
name="diagnoses"
maxValues={10}
onChange={(items) => {
const codeableConcepts = items.map((item) => ({
coding: [item],
})) as DiagnosisCodeableConcept[];
setDiagnoses(codeableConcepts);
}}
/>
Billing Type:
<input
type="radio"
id="billToPatient"
name="billTo"
value="patient"
onChange={(e) => {
updateBillingInformation({ billTo: e.target.value });
}}
/>
<label htmlFor="billToPatient">Patient</label>
<input
type="radio"
id="billToInsurance"
name="billTo"
value="insurance"
onChange={(e) => {
updateBillingInformation({ billTo: e.target.value });
}}
/>
<label htmlFor="billToInsurance">Insurance</label>
[Optional] Insurance Coverage Selection (if billTo is insurance):
{
patient && state.billingInformation.billTo === 'insurance' && (
<select
name="coverage"
onChange={(e) => {
updateBillingInformation({
patientCoverage: { reference: `Coverage/${e.target.value}` },
});
}}
>
<option value="">Select Insurance Coverage</option>
{coverages.map((coverage) => (
<option key={coverage.id} value={coverage.id}>
{coverage.payor?.[0]?.display || 'Unknown Insurance'}
</option>
))}
</select>
);
}
[Optional] Specimen Collection Time (if drawing in-house):
<input
type="datetime-local"
onChange={(e) => {
setSpecimenCollectedDateTime(e.target.value ? new Date(e.target.value) : undefined);
}}
/>
Order Creation:
async function handleOrderCreation() {
try {
const { serviceRequest } = await createOrderBundle();
await sendLabOrderToHealthGorilla(medplum, serviceRequest);
} catch (err) {
if (err instanceof LabOrderValidationError) {
// Handle validation errors
}
}
}
Lab Account Numbers
Some labs (notably Labcorp) require account numbers to be included with each order. Account number requirements vary by lab—some require none, some require one, and some require both of the following:
Physician-level account number
Stored as an identifier on the Practitioner resource with type AN and an assigner reference pointing to the performing lab's Organization in Medplum. The send-to-health-gorilla bot automatically reads this identifier and includes it in the order—no form input required.
{
"resourceType": "Practitioner",
"identifier": [
{
"type": {
"coding": [{ "system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "AN" }]
},
"value": "[PHYSICIAN_ACCOUNT_NUMBER]",
"assigner": { "reference": "Organization/[PERFORMING_LAB_ORGANIZATION_ID]" }
}
]
}
Practice-level account number
Stored as a nested extension on the Practitioner resource, keyed by lab. Each entry has a lab sub-extension (reference to the performing lab Organization) and a value sub-extension (the account number string). The bot automatically reads it and includes it in the order—no form input required.
{
"resourceType": "Practitioner",
"extension": [
{
"url": "https://medplum.com/integrations/health-gorilla/lab-org-account",
"extension": [
{ "url": "lab", "valueReference": { "reference": "Organization/[PERFORMING_LAB_ORGANIZATION_ID]" } },
{ "url": "value", "valueString": "[PRACTICE_ACCOUNT_NUMBER]" }
]
}
]
}
Multiple entries can be added per lab, and the bot will select the one matching the order's performing lab.
If a practitioner orders under multiple practice-level accounts with the same lab (e.g. multiple clinic locations), you can override the account number at order time using setPerformingLabAccountNumber from the useHealthGorillaLabOrder hook:
setPerformingLabAccountNumber(selectedAccountNumber);
FHIR Data Model
Laboratory orders use a two-level hierarchy of FHIR resources, with a parent order containing multiple individual tests.
Order Structure
The parent order is represented by a ServiceRequest resource with the profile https://medplum.com/profiles/integrations/health-gorilla/StructureDefinition/MedplumHealthGorillaOrder. This order contains high-level information like:
- The ordering provider (
ServiceRequest.requester) - The performing laboratory (
ServiceRequest.performer) - Overall order status (
ServiceRequest.status) - Shared documentation (
ServiceRequest.supportingInfo)
Each individual test within the order is represented by its own ServiceRequest resource that:
- Links back to the parent order using
ServiceRequest.basedOn - Contains the specific test code from the performing lab's compendium
- Holds test-specific details, including Ask on Order Entry (AoE) questions.
Supporting Resources
Several additional FHIR resources provide important order details:
-
Ask on Entry (AoE) Responses
- Stored as
QuestionnaireResponseresources - Referenced by each test's
ServiceRequest.supportingInfo - Contains answers to test-specific questions
- Stored as
-
Specimens
- The
Specimenresource tracks specimen details - Particularly important for recording
collectionDatewith in-house collections - Links specimen type and collection method information
- The
-
Documents Two types of
DocumentReferenceresources are linked throughServiceRequest.supportingInfo:- Requisition Form: The official order documentation with the lab's requisition number
- Specimen Label: PDF with specimen labeling information that can be affixed to sample collection tubes (used for in-house specimen collection)
Order Lifecycle
Orders progress through several states:
-
draft- Initial state from the order form
- Order has not yet been sent to the Health Gorilla
- All updates and changes are allowed in Medplum
-
active- Order has been transmitted to Health Gorilla and the performing lab
- The downstream lab (e.g Quest, Labcorp) consider this order immutable
- No modifications allowed in lab's system
- Set by the
send-to-health-gorillabot
-
completed- Results have been received
- Order processing is finished
- Set by the
receive-from-health-gorillabot
-
on-hold- An error occurred during processing
- Set
- Error details can be found
- Requires intervention to resolve
- Set by the
send-to-health-gorillabot
-
revoked- Order has been canceled
- Cannot be reactivated
- Set by the client application
Important: Once an order becomes active, it cannot be modified in the lab's system, even if updated in Medplum.
Automation Bots
send-to-health-gorilla Bot
Input: Order ServiceRequest
Actions:
- Syncs patient and practitioner data with Health Gorilla
- Assigns Health Gorilla identifiers
- Transmits order with billing information
- Updates order status
- Downloads and stores:
- Requisition forms
- Specimen labels
split-order Bot
In some cases, the send-to-health-gorilla, but will return an error as follows, asking for the order to be split and resubmitted.
{
"resourceType": "OperationOutcome",
"extension": [
{
"url": "https://api.healthgorilla.com/fhir/R4/fhir/StructureDefinition/operationoutcome-order-splitting",
"valueString": "436|1877;9230;900323"
}
],
"issue": [
{
"severity": "fatal",
"code": "processing",
"details": {
"coding": [
{
"code": "order-splitting-required"
}
],
"text": "Splitting required"
}
}
]
}
In this case, you can use the split order but to create new orders based off of the original. Each of these orders can then be submitted to send-to-health-gorilla
Example payload:
const params = {
resourceType: 'Parameters',
parameter: [
{ name: 'order', valueReference: { reference: 'ServiceRequest/example' } },
{ name: 'groups', valueString: '436|1877;9230;900323' },
],
};
Actions:
- Creates new parent orders for each group
- Reassigns child test
ServiceRequestresources - Updates original order to
entered-in-error - Uses
ServiceRequest.replacesfor order history
Each new order references the original using replaces for audit purposes.