Medication order

DSP medication orders land on MedicationRequest. DSP's free-text dosing narrative fits R5's renderedDosage better than R4's dosageInstruction.text — a good candidate for a cross-version extension.

DSP content_type

order.medication

FHIR target

MedicationRequest (R4) + R5 renderedDosage (xver)

Field-by-field

DSPFHIR R4Notes
type: MEDICATION_ORDERMedicationRequest resource type
intent (proposal/plan/order/...)MedicationRequest.intentDirect valueset match.
descriptiondosageInstruction.text; also cross-version extension renderedDosageInstruction (R5 xver)Free-text "Chantix 0.5 mg, take one a day for 3 days, then twice a day".
reason (string)reasonCode.text
reason_referencesreasonReference[] → Condition / Observation
order_data.medication (RxNorm)medicationCodeableConceptOr medicationReferenceMedication for compound drugs.
order_data.formMedication.form or dosageInstruction.method
order_data.dosage + dosage_unitdosageInstruction.doseAndRate.doseQuantityUCUM units required.
order_data.frequencydosageInstruction.timing.repeatcount→frequency, period/period_unit→period/periodUnit; prnasNeededBoolean.
order_data.frequency.textdosageInstruction.textKeep the narrative; structured fields are best-effort.
order_data.durationdosageInstruction.timing.repeat.bounds[x]boundsDuration with UCUM unit.
order_data.route + route_codeddosageInstruction.routeBind to NCIT or SNOMED CT route codes.
order_data.refillsdispenseRequest.numberOfRepeatsAllowed
order_data.dispensedispenseRequest.quantity"56" tablets.
order_data.discontinue / discontinue_datestatus = stopped + statusReason (R4 native) + meta.lastUpdated for the dateOr represent via a new MedicationRequest with intent=order and status=stopped.
order_data.notesnote.text
confidence_scoreDSP extension confidence-scoreNo native request-side element in R4 or R5. The IG defines confidence-score as a new extension; R5's grading pattern from Evidence.certainty informs its valueset.
update_statusmeta.versionId + Provenance.activity

Example

DSP

{
  "id": "med-001",
  "type": "MEDICATION_ORDER",
  "intent": "order",
  "description": "Chantix 0.5 mg, take one a day for 3 days, then twice a day",
  "reason": "Smoking cessation",
  "order_data": {
    "medication": { "text":"Chantix",
      "codes":[{"system":"rxnorm","code":"1000048"}]},
    "form": "tablet",
    "dosage": "0.5", "dosage_unit":"mg",
    "frequency": {"count":1,"period":1,"period_unit":"d","prn":false},
    "route_coded": {"codes":[{"system":"NCIT","code":"C38288"}]},
    "refills":"5","dispense":"56"
  },
  "confidence_score": 1.0
}

FHIR R4 (DSP-FHIR profile)

{
  "resourceType":"MedicationRequest",
  "status":"active","intent":"order",
  "medicationCodeableConcept":{
    "coding":[{"system":"http://www.nlm.nih.gov/research/umls/rxnorm","code":"1000048"}],
    "text":"Chantix"
  },
  "subject":{"reference":"Patient/pat-67890"},
  "encounter":{"reference":"Encounter/enc-12345"},
  "reasonCode":[{"text":"Smoking cessation"}],
  "dosageInstruction":[{
    "text":"Chantix 0.5 mg, take one a day for 3 days, then twice a day",
    "timing":{"repeat":{"frequency":1,"period":1,"periodUnit":"d"}},
    "asNeededBoolean":false,
    "route":{"coding":[{"system":"http://ncithesaurus.nci.nih.gov","code":"C38288","display":"Oral"}]},
    "doseAndRate":[{"doseQuantity":{"value":0.5,"unit":"mg","system":"http://unitsofmeasure.org","code":"mg"}}]
  }],
  "dispenseRequest":{"numberOfRepeatsAllowed":5,"quantity":{"value":56,"unit":"tablet"}},
  "extension":[{
    "url":"https://dsp-fhir.org/StructureDefinition/confidence-score",
    "valueDecimal":1.0
  }]
}

DSP → FHIR mapping

Executable FHIR Mapping Language skeleton for DspMedicationOrder → MedicationRequest. The full map ships in the downloadable IG package.

map "https://dsp-fhir.org/StructureMap/DspMedicationOrderToMedicationRequest" = "DspMedicationOrderToMedicationRequest"

uses "https://dsp-fhir.org/StructureDefinition/DspOrderMedicationResource" alias DspMedOrder as source
uses "http://hl7.org/fhir/StructureDefinition/MedicationRequest" alias MedicationRequest as target

group DspMedicationOrderToMedicationRequest(source src : DspMedOrder, target tgt : MedicationRequest) {
  src.id as id -> tgt.id = id;
  src -> tgt.status = 'active';
  src -> tgt.intent = 'order';

  src.payload as p then {
    p.drug as drug -> tgt.medication = create('CodeableConcept') as cc,
      cc.text = drug "drug-text";
    p.rxnorm_code as rx -> tgt.medication as med, med.coding as co,
      co.system = 'http://www.nlm.nih.gov/research/umls/rxnorm', co.code = rx "rxnorm";

    // Rendered sig - both extension (xver) and literal dosageInstruction.text
    p.rendered_dosage_instruction as rdi -> tgt.extension as ext then {
      rdi -> ext.url = 'https://dsp-fhir.org/StructureDefinition/dsp-rendered-dosage-instruction',
             ext.value = create('string') as v, v.value = rdi;
    } "rendered-sig";
    p.rendered_dosage_instruction as rdi -> tgt.dosageInstruction as di, di.text = rdi "sig-text";

    p.prn as prn -> tgt.dosageInstruction as di, di.asNeededBoolean = prn "prn";
  };

  src.confidence as conf -> tgt.extension as ext then {
    conf -> ext.url = 'https://dsp-fhir.org/StructureDefinition/dsp-confidence-score',
            ext.value = create('decimal') as v, v.value = conf;
  } "confidence";

  // One extension per turn index (controller adds the version-pinned transcript reference).
  src.transcript_turn_refs as t -> tgt.extension as ext then {
    t -> ext.url = 'https://dsp-fhir.org/StructureDefinition/dsp-transcript-turn-ref',
         ext.extension as turnExt,
         turnExt.url = 'turn',
         turnExt.value = create('integer') as v, v.value = t;
  } "turn-ref";
}

FHIR → DSP (canonical $graphql read)

Standard FHIR $graphql selects every native MedicationRequest field and DSP-namespaced extension that the write projection populated. A small adapter renames the result into the DSP shape — no FML inverse engine required.

Canonical query

query DspMedicationOrder($id: ID!) {
  MedicationRequest(id: $id) {
    id status intent
    meta { profile source versionId lastUpdated }

    medicationCodeableConcept {
      text
      coding { system code display }
    }
    medicationReference { reference }

    reasonCode { text coding { system code display } }
    reasonReference { reference }

    dosageInstruction {
      text
      asNeededBoolean
      timing {
        repeat {
          frequency period periodUnit
          boundsDuration { value unit system code }
        }
      }
      route { coding { system code display } text }
      doseAndRate {
        doseQuantity { value unit system code }
      }
    }
    dispenseRequest {
      numberOfRepeatsAllowed
      quantity { value unit }
    }
    note { text }

    rendered: extension(url: "https://dsp-fhir.org/StructureDefinition/dsp-rendered-dosage-instruction") {
      valueString
    }
    confidence: extension(url: "https://dsp-fhir.org/StructureDefinition/dsp-confidence-score") {
      valueDecimal
    }
    spokenForms: extension(url: "https://dsp-fhir.org/StructureDefinition/dsp-spoken-forms") {
      valueString
    }
    turnRefs: extension(url: "https://dsp-fhir.org/StructureDefinition/dsp-transcript-turn-ref") {
      transcript: extension(url: "transcript") { valueReference { reference } }
      turn:       extension(url: "turn")       { valueInteger }
    }
  }
}

DSP reconstruction adapter

function toDspMedicationOrder(m) {
  const med = m.medicationCodeableConcept;
  const di  = m.dosageInstruction?.[0];
  const turnRefs = m.turnRefs ?? [];
  return {
    id: m.id,
    type: 'MEDICATION_ORDER',
    intent: m.intent,
    description: m.rendered?.[0]?.valueString ?? di?.text,
    reason: m.reasonCode?.[0]?.text,
    reason_references: m.reasonReference?.map(r => r.reference),
    order_data: {
      medication: {
        text: med?.text,
        codes: (med?.coding ?? []).map(toDspCode),
      },
      dosage:      di?.doseAndRate?.[0]?.doseQuantity?.value,
      dosage_unit: di?.doseAndRate?.[0]?.doseQuantity?.unit,
      route_coded: { codes: (di?.route?.coding ?? []).map(toDspCode) },
      frequency: di?.timing?.repeat ? {
        count:       di.timing.repeat.frequency,
        period:      di.timing.repeat.period,
        period_unit: di.timing.repeat.periodUnit,
        prn:         di.asNeededBoolean,
      } : undefined,
      duration: di?.timing?.repeat?.boundsDuration && {
        value: di.timing.repeat.boundsDuration.value,
        unit:  di.timing.repeat.boundsDuration.unit,
      },
      refills:  m.dispenseRequest?.numberOfRepeatsAllowed,
      dispense: m.dispenseRequest?.quantity?.value,
      notes:    m.note?.map(n => n.text),
    },
    confidence_score: m.confidence?.[0]?.valueDecimal,
    spoken_forms:     m.spokenForms?.map(e => e.valueString) ?? [],
    provenance:       turnRefs.map(t => t.turn?.valueInteger),
    transcript_ref:   turnRefs[0]?.transcript?.valueReference?.reference,
  };
}
No data loss. The free-text dosing narrative is preserved exactly on dsp-rendered-dosage-instruction (the adapter prefers it over the structured dosageInstruction.text reconstruction).

SQL-on-FHIR v2 ViewDefinition that flattens every field above — including DSP extensions — into a tabular projection. Runnable as-is on any spec-conformant engine (Pathling, sof-exec, Aidbox SQL-on-FHIR, Databricks). See the SQL-on-FHIR v2 spec. These ViewDefinitions also ship as JSON in the IG zip.

dsp_medication_request — DSP MedicationRequest

Flattens DSP medication orders: route/dose/frequency/duration, substitution, first dosageInstruction.

{
  "resourceType": "ViewDefinition",
  "url": "https://dsp-fhir.org/ViewDefinition/dsp-medication-request",
  "name": "dsp_medication_request",
  "title": "DSP MedicationRequest",
  "status": "draft",
  "description": "Flattens DSP medication orders: route/dose/frequency/duration, substitution, first dosageInstruction.",
  "resource": "MedicationRequest",
  "fhirVersion": [
    "4.0.1"
  ],
  "select": [
    {
      "column": [
        {
          "name": "id",
          "path": "id",
          "type": "id",
          "description": "Server-assigned logical id."
        },
        {
          "name": "version_id",
          "path": "meta.versionId",
          "type": "id",
          "description": "FHIR version id (drives NEW/UPDATED classification)."
        },
        {
          "name": "last_updated",
          "path": "meta.lastUpdated",
          "type": "instant",
          "description": "Instant of last mutation."
        },
        {
          "name": "meta_source",
          "path": "meta.source",
          "type": "uri"
        },
        {
          "name": "payload_version",
          "path": "meta.tag.where(system='https://dsp-fhir.org/CodeSystem/payload-version').code.first()",
          "type": "code",
          "description": "DSP payload version this resource was last emitted under."
        },
        {
          "name": "confidence_score",
          "path": "extension('https://dsp-fhir.org/StructureDefinition/confidence-score').value.ofType(decimal)",
          "type": "decimal",
          "description": "DSP confidence (0..1)."
        },
        {
          "name": "transcript_ref",
          "path": "extension('https://dsp-fhir.org/StructureDefinition/transcript-turn-refs').extension('transcript').value.ofType(Reference).reference",
          "type": "string",
          "description": "DocumentReference/<id>/_history/<v> that pins the transcript version for turn indices."
        },
        {
          "name": "turn_indices",
          "path": "extension('https://dsp-fhir.org/StructureDefinition/transcript-turn-refs').extension('turn').value.ofType(integer)",
          "type": "integer",
          "collection": true,
          "description": "Turn indices joined by $ground into transcript content."
        },
        {
          "name": "spoken_forms",
          "path": "extension('https://dsp-fhir.org/StructureDefinition/spoken-forms').extension('form').value.ofType(string)",
          "type": "string",
          "collection": true
        },
        {
          "name": "search_terms",
          "path": "extension('https://dsp-fhir.org/StructureDefinition/search-terms').extension('term').value.ofType(string)",
          "type": "string",
          "collection": true
        },
        {
          "name": "status",
          "path": "status",
          "type": "code"
        },
        {
          "name": "intent",
          "path": "intent",
          "type": "code"
        },
        {
          "name": "subject_patient_id",
          "path": "subject.reference.substring(8)",
          "type": "string"
        },
        {
          "name": "encounter_id",
          "path": "encounter.reference.substring(10)",
          "type": "string"
        },
        {
          "name": "authored_on",
          "path": "authoredOn",
          "type": "dateTime"
        },
        {
          "name": "requester_practitioner_id",
          "path": "requester.reference.where($this.startsWith('Practitioner/')).substring(13).first()",
          "type": "string"
        },
        {
          "name": "medication_code_system",
          "path": "medication.ofType(CodeableConcept).coding.system.first()",
          "type": "uri"
        },
        {
          "name": "medication_code",
          "path": "medication.ofType(CodeableConcept).coding.code.first()",
          "type": "code"
        },
        {
          "name": "medication_display",
          "path": "medication.ofType(CodeableConcept).coding.display.first()",
          "type": "string"
        },
        {
          "name": "medication_text",
          "path": "medication.ofType(CodeableConcept).text",
          "type": "string"
        },
        {
          "name": "medication_reference",
          "path": "medication.ofType(Reference).reference",
          "type": "string",
          "description": "Populated when Medication is a contained/shared resource instead of inline CodeableConcept."
        },
        {
          "name": "dose_value",
          "path": "dosageInstruction.doseAndRate.dose.ofType(Quantity).value.first()",
          "type": "decimal"
        },
        {
          "name": "dose_unit",
          "path": "dosageInstruction.doseAndRate.dose.ofType(Quantity).unit.first()",
          "type": "string"
        },
        {
          "name": "route_code",
          "path": "dosageInstruction.route.coding.code.first()",
          "type": "code"
        },
        {
          "name": "frequency",
          "path": "dosageInstruction.timing.repeat.frequency.first()",
          "type": "integer"
        },
        {
          "name": "period",
          "path": "dosageInstruction.timing.repeat.period.first()",
          "type": "decimal"
        },
        {
          "name": "period_unit",
          "path": "dosageInstruction.timing.repeat.periodUnit.first()",
          "type": "code"
        },
        {
          "name": "duration",
          "path": "dosageInstruction.timing.repeat.bounds.ofType(Duration).value.first()",
          "type": "decimal"
        },
        {
          "name": "duration_unit",
          "path": "dosageInstruction.timing.repeat.bounds.ofType(Duration).unit.first()",
          "type": "string"
        },
        {
          "name": "as_needed",
          "path": "dosageInstruction.asNeeded.ofType(boolean).first()",
          "type": "boolean"
        },
        {
          "name": "substitution_allowed",
          "path": "substitution.allowed.ofType(boolean)",
          "type": "boolean"
        },
        {
          "name": "reason_reference",
          "path": "reasonReference.reference.first()",
          "type": "string"
        },
        {
          "name": "refills",
          "path": "dispenseRequest.numberOfRepeatsAllowed",
          "type": "unsignedInt"
        }
      ]
    }
  ]
}
R5 xver benefit: MedicationRequest.renderedDosageInstruction in R5 is an explicit string for exactly what DSP's description carries. Publish it as a cross-version extension on R4 instead of overloading dosageInstruction.text.