Follow-up order
DSP follow-up orders are a provider-intent artifact (“see me in 2 weeks”) rather than a booked appointment. Model as a ServiceRequest with an occurrence timing; a later Appointment realizes it if scheduled.
order.follow_up
ServiceRequest (category=follow-up) and optionally Appointment
Field-by-field
| DSP | FHIR R4 | Notes |
|---|---|---|
type: FOLLOW_UP_ORDER | ServiceRequest.category = follow-up | Suggest a DSP-FHIR code follow-up bound to an IG CodeSystem, or SNOMED 390906007. |
order_data.return_in_value + return_in_unit | ServiceRequest.occurrenceTiming.repeat.boundsDuration or extension return-in | An explicit Duration (e.g. value=2, unit=wk, UCUM). |
order_data.approximation | Extension approximation on the Duration | boolean. |
order_data.prn | asNeeded[x] on Dosage isn't applicable here; use extension or ServiceRequest.asNeededBoolean pattern via profile | Or carry as patientInstruction. |
reason | reasonCode / reasonReference |
Side-by-side example
DSP
{
"id": "followup-001",
"type": "FOLLOW_UP_ORDER",
"context": { "content_type": "order.follow_up" },
"description": "Follow up in approximately 2 weeks or as needed",
"intent": "order",
"reason": "Routine follow-up visit",
"reason_references": ["cond-001"],
"order_data": {
"return_in_value": "2",
"return_in_unit": "week",
"approximation": true,
"prn": true
}
} FHIR R4 (DSP-FHIR profile)
{
"resourceType":"ServiceRequest",
"status":"active","intent":"order",
"category":[{"coding":[{"system":"https://dsp-fhir.org/CodeSystem/order-category","code":"follow-up"}]}],
"code":{"text":"Follow up in approximately 2 weeks or as needed"},
"reasonCode":[{"text":"Routine follow-up visit"}],
"reasonReference":[{"reference":"Condition/cond-001"}],
"occurrenceTiming":{
"repeat":{
"boundsDuration":{
"value":2,"unit":"wk","system":"http://unitsofmeasure.org","code":"wk",
"extension":[{"url":"https://dsp-fhir.org/StructureDefinition/approximation","valueBoolean":true}]
}
}
},
"subject":{"reference":"Patient/pat-67890"},
"encounter":{"reference":"Encounter/enc-12345"},
"extension":[
{"url":"https://dsp-fhir.org/StructureDefinition/prn","valueBoolean":true}
]
} DSP → FHIR mapping
Skeleton for DspFollowUpOrder → ServiceRequest(category=follow-up) with the return-in Duration + approximation-flag pattern.
map "https://dsp-fhir.org/StructureMap/DspFollowUpOrderToServiceRequest" = "DspFollowUpOrderToServiceRequest"
uses "https://dsp-fhir.org/StructureDefinition/DspOrderFollowUpResource" alias DspFU as source
uses "http://hl7.org/fhir/StructureDefinition/ServiceRequest" alias ServiceRequest as target
group DspFollowUpOrderToServiceRequest(source src : DspFU, target tgt : ServiceRequest) {
src.id as id -> tgt.id = id;
src -> tgt.status = 'active';
src -> tgt.intent = 'order';
src -> tgt.category as cat, cat.coding as co,
co.system = 'https://dsp-fhir.org/CodeSystem/dsp-order-category',
co.code = 'follow-up' "category";
src.payload as p then {
// Return-in Duration (+ approximation flag)
p.return_in_value as v -> tgt.extension as ext then {
v -> ext.url = 'https://dsp-fhir.org/StructureDefinition/dsp-return-in',
ext.value = create('Duration') as dur,
dur.value = v,
dur.system = 'http://unitsofmeasure.org';
p.return_in_unit as u -> dur.code = u, dur.unit = u;
p.approximation as ap -> dur.extension as apExt,
apExt.url = 'https://dsp-fhir.org/StructureDefinition/dsp-approximation',
apExt.value = create('boolean') as b, b.value = ap;
} "return-in";
p.reason as r -> tgt.reasonCode as rc, rc.text = r "reason";
p.prn as prn -> tgt.extension as ext then {
prn -> ext.url = 'https://dsp-fhir.org/StructureDefinition/dsp-prn',
ext.value = create('boolean') as b, b.value = 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";
} FHIR → DSP (canonical $graphql read)
The return-in Duration is preserved on a dedicated extension carrying value, unit, and the approximation flag, so the DSP fields recover exactly.
Canonical query
query DspFollowUpOrder($id: ID!) {
ServiceRequest(id: $id) {
id status intent
code { text }
reasonCode { text }
reasonReference { reference }
returnIn: extension(url: "https://dsp-fhir.org/StructureDefinition/dsp-return-in") {
valueDuration {
value unit code
approximation: extension(url: "https://dsp-fhir.org/StructureDefinition/dsp-approximation") {
valueBoolean
}
}
}
prn: extension(url: "https://dsp-fhir.org/StructureDefinition/dsp-prn") {
valueBoolean
}
confidence: extension(url: "https://dsp-fhir.org/StructureDefinition/dsp-confidence-score") {
valueDecimal
}
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 toDspFollowUpOrder(s) {
const dur = s.returnIn?.[0]?.valueDuration;
const turnRefs = s.turnRefs ?? [];
return {
id: s.id,
type: 'FOLLOW_UP_ORDER',
intent: s.intent,
description: s.code?.text,
reason: s.reasonCode?.[0]?.text,
reason_references: s.reasonReference?.map(r => r.reference),
order_data: {
return_in_value: dur?.value,
return_in_unit: dur?.code ?? dur?.unit,
approximation: dur?.approximation?.[0]?.valueBoolean,
prn: s.prn?.[0]?.valueBoolean,
},
confidence_score: s.confidence?.[0]?.valueDecimal,
provenance: turnRefs.map(t => t.turn?.valueInteger),
transcript_ref: turnRefs[0]?.transcript?.valueReference?.reference,
};
} SQL-on-FHIR v2 ViewDefinitions that flatten 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_service_request_follow_up — DSP Follow-up (ServiceRequest)
Flattens DSP dsp follow-up (servicerequest) orders. Filtered by category=follow-up.
{
"resourceType": "ViewDefinition",
"url": "https://dsp-fhir.org/ViewDefinition/dsp-service-request-follow-up",
"name": "dsp_service_request_follow_up",
"title": "DSP Follow-up (ServiceRequest)",
"status": "draft",
"description": "Flattens DSP dsp follow-up (servicerequest) orders. Filtered by category=follow-up.",
"resource": "ServiceRequest",
"fhirVersion": [
"4.0.1"
],
"where": [
{
"path": "category.coding.where(code='follow-up').exists()"
}
],
"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": "priority",
"path": "priority",
"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": "code_system",
"path": "code.coding.system.first()",
"type": "uri"
},
{
"name": "code",
"path": "code.coding.code.first()",
"type": "code"
},
{
"name": "code_display",
"path": "code.coding.display.first()",
"type": "string"
},
{
"name": "code_text",
"path": "code.text",
"type": "string"
},
{
"name": "reason_code_text",
"path": "reasonCode.text.first()",
"type": "string"
},
{
"name": "reason_reference",
"path": "reasonReference.reference.first()",
"type": "string"
},
{
"name": "body_site_code",
"path": "bodySite.coding.code.first()",
"type": "code"
},
{
"name": "occurrence_timing_bounds_value",
"path": "occurrence.ofType(Timing).repeat.bounds.ofType(Duration).value",
"type": "decimal"
},
{
"name": "occurrence_timing_bounds_unit",
"path": "occurrence.ofType(Timing).repeat.bounds.ofType(Duration).unit",
"type": "string"
},
{
"name": "approximate_match",
"path": "extension('https://dsp-fhir.org/StructureDefinition/approximate-match').value.ofType(boolean)",
"type": "boolean"
}
]
}
]
}dsp_appointment_follow_up — DSP proposed follow-up Appointment
Flattens the proposed Appointment materialized via ActivityDefinition/$apply from a DSP follow-up ServiceRequest.
{
"resourceType": "ViewDefinition",
"url": "https://dsp-fhir.org/ViewDefinition/dsp-appointment-follow-up",
"name": "dsp_appointment_follow_up",
"title": "DSP proposed follow-up Appointment",
"status": "draft",
"description": "Flattens the proposed Appointment materialized via ActivityDefinition/$apply from a DSP follow-up ServiceRequest.",
"resource": "Appointment",
"fhirVersion": [
"4.0.1"
],
"where": [
{
"path": "basedOn.reference.where($this.startsWith('ServiceRequest/')).exists()"
}
],
"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": "based_on_service_request",
"path": "basedOn.reference.where($this.startsWith('ServiceRequest/')).substring(15).first()",
"type": "string"
},
{
"name": "start",
"path": "start",
"type": "instant"
},
{
"name": "end",
"path": "end",
"type": "instant"
},
{
"name": "minutes_duration",
"path": "minutesDuration",
"type": "positiveInt"
},
{
"name": "patient_ref",
"path": "participant.actor.reference.where($this.startsWith('Patient/')).substring(8).first()",
"type": "string"
},
{
"name": "practitioner_ref",
"path": "participant.actor.reference.where($this.startsWith('Practitioner/')).substring(13).first()",
"type": "string"
},
{
"name": "approximate_match",
"path": "extension('https://dsp-fhir.org/StructureDefinition/approximate-match').value.ofType(boolean)",
"type": "boolean"
}
]
}
]
}ServiceRequest with a DSP-FHIR category and a couple of
extensions is the cleanest option. If the follow-up is actually booked, emit an
Appointment that references this ServiceRequest
via basedOn.