Skip to content

Task-Based App Launch ​

SMART App Launch 2.1 added two experimental FHIR Task profiles that request the launch of a SMART app, for example as the outcome of a Clinical Reasoning or CDS Hooks workflow that recommends an app to staff (spec):

ProfileTask.codeRequests
task-ehr-launchlaunch-app-ehrAn EHR launch, with Task.for as the patient, Task.encounter as the encounter and an optional appContext
task-standalone-launchlaunch-app-standaloneA standalone launch

Both codes, and the input types smartonfhir-application (the app URL, valueUrl) and smartonfhir-appcontext (the app context, valueString), come from http://hl7.org/fhir/smart-app-launch/CodeSystem/smart-codes.

Proxy Smart is the EHR side of an EHR launch, so it is also the party that acts on these Tasks: POST /auth/launch/task takes a launch Task and answers with the app launch URL to open.

Request ​

POST /auth/launch/task with the caller's Bearer token and a JSON body carrying exactly one of:

FieldMeaning
taskThe launch Task itself
taskReferenceA Task on the FHIR server: Task/{id}, a bare id, or an absolute URL on that server
serverOptional FHIR server identifier the launch is for; defaults to the first configured server

A referenced Task is read through the proxy's own FHIR path with the caller's token. The scope, consent, tenant and role rules that guard any other FHIR read guard this one, so the caller needs a grant that covers reading the Task (for example user/Task.rs).

json
{
  "task": {
    "resourceType": "Task",
    "status": "requested",
    "intent": "proposal",
    "code": { "coding": [{ "system": "http://hl7.org/fhir/smart-app-launch/CodeSystem/smart-codes", "code": "launch-app-ehr" }] },
    "for": { "reference": "Patient/123" },
    "encounter": { "reference": "Encounter/456" },
    "input": [
      {
        "type": { "coding": [{ "system": "http://hl7.org/fhir/smart-app-launch/CodeSystem/smart-codes", "code": "smartonfhir-application" }] },
        "valueUrl": "https://app.example.org/launch"
      },
      {
        "type": { "coding": [{ "system": "http://hl7.org/fhir/smart-app-launch/CodeSystem/smart-codes", "code": "smartonfhir-appcontext" }] },
        "valueString": "{\"field1\":\"value\"}"
      }
    ]
  }
}

What the proxy checks ​

  1. The profile. A Task with status and intent, a code carrying one of the two launch codes, exactly one smartonfhir-application input whose valueUrl is an absolute http(s) URL, and at most one smartonfhir-appcontext input as a valueString. Other inputs are allowed, since the input slicing is open. Every violation is listed in the issues of the 400 response.
  2. The status. A Task that is cancelled or entered-in-error withdraws the request and is refused with 409.
  3. The app. The application URL must be the launch URL of exactly one enabled SMART app registered with the proxy: the launch_url set in SMART Apps, or one of the launch_uris of an app that registered dynamically. A fragment, a trailing slash and the case of the host are ignored; path and query must match. The proxy never sends a launch code to an app it does not know.
  4. The context (EHR launch). Task.for must reference a Patient and Task.encounter an Encounter, as relative references or absolute URLs on the server the launch is for. A logical, contained or foreign-server reference cannot become launch context. When the Task names both, the Encounter is read with the caller's token and its subject must be that patient.
  5. The caller (EHR launch). The launch code is minted through the same code as POST /auth/launch: a patient may launch for their own record and a practitioner for any patient. The code is bound to the matched app's client id, to the caller and to the FHIR base handed to the app as iss, and it works for one authorization request.
  6. The context (standalone launch). A standalone launch hands the app iss only, so no launch context reaches it. A smartonfhir-appcontext input would be lost and is refused with 400; ask for launch-app-ehr to deliver it. Task.for and Task.encounter are accepted, as in the specification's own example, and listed in context_not_carried so the caller knows the app will choose its own patient and encounter.

Response ​

json
{
  "launch_type": "ehr",
  "launch_url": "https://app.example.org/launch?iss=https%3A%2F%2Fapi.example.org%2Fproxy-smart-backend%2Fhapi%2FR4&launch=eyJ...",
  "iss": "https://api.example.org/proxy-smart-backend/hapi/R4",
  "client_id": "cds-app",
  "launch": "eyJ...",
  "expires_in": 300,
  "task": "Task/task-for-ehr-launch"
}

Open launch_url in the user's browser. For an EHR launch it carries iss and launch, and the app continues with the regular EHR launch at /auth/authorize. For a standalone launch it carries iss only and no launch code is minted, so the app runs its own standalone launch and the user selects the context; context_not_carried then names the Task elements the app does not receive. Existing query parameters on the application URL are kept.

appContext ​

The appContext input reaches the app the way a CDS Hooks Link's appContext does: as the appContext field of the token response, exactly as the Task carried it. POST /auth/launch accepts the same appContext field for EHRs that mint launch codes directly.

Errors ​

StatuserrorWhen
400invalid_requestBoth or neither of task and taskReference, a Task that does not conform to a profile (see issues), an unregistered or shared application URL, context the server cannot provide, an encounter that is not the patient's, or appContext on a standalone launch
401unauthorizedNo or an invalid Bearer token, or the FHIR server did not accept it
403access_deniedThe caller may not read the Task or the Encounter, or may not launch for its patient
404not_found / invalid_requestThe Task or the FHIR server does not exist
409invalid_requestThe Task is cancelled or entered-in-error
502server_errorReading the Task or the Encounter failed upstream
503temporarily_unavailableThe registered apps cannot be read from Keycloak

Task status and output ​

The specification defines no status transitions and no Task.output for a launch, so the proxy reads the Task and leaves it unchanged. Workflow engines that track whether a recommended app was opened update the Task themselves.

Proxy Smart — Healthcare Interoperability Platform