Skip to main content

Sending Orders

Prerequisites

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:

  1. Present the provider with the appropriate lab order form (CPOE)
  2. Create the appropriate FHIR resources based on that information
  3. 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:

ConceptDescriptionImportance
OrderRequest for one or more lab tests, uniquely identified and tracked.Organizes tests, specimens and results. Maintains chain of custody and enables status tracking.
TestSpecific diagnostic procedure with defined requirements for specimen and data collection.Determines specimen needs, required clinical data, and handling protocols. Impacts turnaround times and costs.
SpecimenPhysical 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 SplittingBreaking 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:

  1. HealthGorillaLabOrderProvider: Context provider for state management
  2. useHealthGorillaLabOrder: 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.

  1. Wrap your application:
function OrderPage() {
return (
<HealthGorillaLabOrderProvider>
<OrderForm />
</HealthGorillaLabOrderProvider>
);
}
  1. Initialize with context:
function OrderForm() {
const [patient, setPatient] = useState<Patient>();
const [requester, setRequester] = useState<Practitioner>();

const labOrderReturn = useHealthGorillaLabOrder({
patient,
requester,
});
}
  1. Access operations:
const {
state,
searchAvailableTests,
setTests,
setDiagnoses,
updateBillingInformation,
setSpecimenCollectedDateTime,
setOrderNotes,
createOrderBundle,
} = labOrderReturn;
CommandPurpose
searchAvailableTestsUses the autocomplete bot to fetch available lab tests from Health Gorilla's API based on search string
searchAvailableLabsUses the autocomplete bot to fetch available diagnostic labs from Health Gorilla's API based on search string
setTestsUpdates selected tests in order state
setDiagnosesUpdates ICD-10 diagnosis codes in order state
updateBillingInformationUpdates payment details (patient, insurance, customer account)
setSpecimenCollectedDateTimeSets when specimens were/will be collected
setOrderNotesAdds notes/instructions for the lab
createOrderBundleCreates FHIR resources for the complete order
setPerformingLabSets which lab will process the tests
setPerformingLabAccountNumberOverrides the practice-level lab account number for the order (see Lab Account Numbers)
validateOrderChecks 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)
  • Diagnosis codes (ServiceRequest.reasonCode)
  • Minimum Patient demographic information (ServiceRequest.subject)
  • 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.

Diagnosis codes

ICD-10 diagnosis codes belong on the parent lab order as ServiceRequest.reasonCode. Child test ServiceRequest resources do not carry diagnoses; they only hold the lab test code.

When using useHealthGorillaLabOrder, call setDiagnoses (or addDiagnosis / removeDiagnosis) with ICD-10 CodeableConcepts — typically from a ValueSetAutocomplete bound to http://hl7.org/fhir/sid/icd-10-cm. createOrderBundle passes that state into createLabOrderBundle(), which writes it to the parent order's reasonCode.

If you build the order bundle yourself, set reasonCode on the parent order to ICD-10 CodeableConcepts before calling $health-gorilla-send. The send-to-health-gorilla bot reads reasonCode from the parent order and includes those codes on the outbound Health Gorilla RequestGroup. For Health Gorilla compatibility, it rewrites coding systems from http://hl7.org/fhir/sid/icd-10-cm to http://hl7.org/fhir/sid/icd-10.

Diagnosis codes are especially important for Medicare orders (see capture diagnosis codes for Medicare patients).

Patient requirements

The order's ServiceRequest.subject must reference a Patient that satisfies the Medplum Health Gorilla Patient profile (https://medplum.com/profiles/integrations/health-gorilla/StructureDefinition/MedplumHealthGorillaPatient). Before transmitting an order, send-to-health-gorilla validates and syncs that patient to Health Gorilla; missing required demographics cause the send to fail.

RequirementFHIR pathNotes
Birth datePatient.birthDateRequired
NamePatient.nameAt least one name with family and at least one given
AddressPatient.addressAt least one address with line, city, state, and postalCode. If country is omitted, the send bot defaults it to US
Medical record number (MRN)Patient.identifierExactly one identifier with type code MR (http://terminology.hl7.org/CodeSystem/v2-0203)
PhonePatient.telecomAt least one phone with use of home or mobile
SexPatient.gender or US Core Birth SexEither gender, or the US Core Birth Sex extension

Email and work phone are allowed by the profile but are not required to send an order.

Example of the required MRN identifier:

{
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "MR"
}
]
},
"system": "https://example.com/mrn",
"value": "123456"
}

Use FHIR profile validation in your project so patients are complete at registration time, rather than discovering gaps only when an order is submitted.

Supporting Resources

Several additional FHIR resources provide important order details:

  1. Ask on Entry (AoE) Responses

    • Stored as QuestionnaireResponse resources
    • Referenced by each test's ServiceRequest.supportingInfo
    • Contains answers to test-specific questions
  2. Specimens

    • The Specimen resource tracks specimen details
    • Particularly important for recording collectionDate with in-house collections
    • Links specimen type and collection method information
  3. Documents Two types of DocumentReference resources are linked through ServiceRequest.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:

  1. 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
  2. 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-gorilla bot
  3. completed

    • Results have been received
    • Order processing is finished
    • Set by the receive-from-health-gorilla bot
  4. on-hold

    • An error occurred during processing
    • Set
    • Error details can be found
    • Requires intervention to resolve
    • Set by the send-to-health-gorilla bot
  5. revoked

    • Order has been canceled
    • Cannot be reactivated
    • Set by the client application
caution

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:

  1. Syncs patient and practitioner data with Health Gorilla
  2. Assigns Health Gorilla identifiers
  3. Transmits order with billing information and diagnosis codes from ServiceRequest.reasonCode
  4. Updates order status
  5. Downloads and stores:
    • Requisition forms
    • Specimen labels

split-order Bot

Some performing labs require a single multi-test order to be split into multiple requisitions before submission. That lab-side constraint is why the split-order bot exists: it is a Medplum preprocessing step that turns one parent order into the per-requisition orders Health Gorilla expects for those labs.

When splitting is required, send-to-health-gorilla returns an error like the following, 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 that case, use the split-order bot to create new orders from the original. Each of those 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:

  1. Creates new parent orders for each group
  2. Reassigns child test ServiceRequest resources
  3. Updates original order to entered-in-error
  4. Uses ServiceRequest.replaces for order history

Each new order references the original using replaces for audit purposes.