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.

DSP content_type

order.follow_up

FHIR target

ServiceRequest (category=follow-up) and optionally Appointment

Field-by-field

DSPFHIR R4Notes
type: FOLLOW_UP_ORDERServiceRequest.category = follow-upSuggest a DSP-FHIR code follow-up bound to an IG CodeSystem, or SNOMED 390906007.
order_data.return_in_value + return_in_unitServiceRequest.occurrenceTiming.repeat.boundsDuration or extension return-inAn explicit Duration (e.g. value=2, unit=wk, UCUM).
order_data.approximationExtension approximation on the Durationboolean.
order_data.prnasNeeded[x] on Dosage isn't applicable here; use extension or ServiceRequest.asNeededBoolean pattern via profileOr carry as patientInstruction.
reasonreasonCode / 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,
  };
}
No data loss. The Duration carries value/unit/approximation in one structured slot.

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"
        }
      ]
    }
  ]
}
Follow-up is the weakest-fit DSP order type in R4. FHIR has no "return visit request" primitive; profiling 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.