Request and response

A clinic app sends a list of items it would like. The patient's health app shows the list, the patient decides what to share, and the health app answers with records and one outcome per item. This page walks through that JSON with one running example. Every example on it is checked against the client library's validators when the site is built.

Words on this page, and in the spec: clinic app = Verifier · the patient's wallet app = Wallet · patient = Holder · record = Artifact. Tags like XV-2 link to the requirement in the spec, which is the authority where this page and it differ.

At a glance

A clinic asks for five items. The patient shares four of them and declines insurance. Every item id appears once in the outcomes. Records name the items they answer. Point at an id to follow it across.

How request items, outcomes and records connect Five request items, each with one outcome: patient fulfilled, insurance declined, medications partial, immunizations fulfilled, intake fulfilled. Four records: a FHIR Bundle fulfills patient and medications; a SMART Health Card and a second FHIR Bundle both fulfill immunizations; a QuestionnaireResponse fulfills intake. Insurance has no record. The request · items[] What the clinic asks for Outcome One per item The response · artifacts[] Each record names the items it fulfills[] Patient demographics patient fulfilled Insurance card insurance · no record returned declined Medications medications · one kept private partial Immunizations immunizations fulfilled Depression screening form intake fulfilled FHIR BUNDLE Patient and two MedicationRequests fulfills: patient, medications SMART HEALTH CARD Signed immunization record fulfills: immunizations FHIR BUNDLE One Immunization not on the card fulfills: immunizations QUESTIONNAIRE RESPONSE The patient's answers fulfills: intake
How request items, outcomes and records connect Five request items, each with one outcome. Patient demographics: fulfilled by a FHIR Bundle with the Patient and two MedicationRequests. Insurance card: declined, no record. Medications: partial, fulfilled by the same Bundle. Immunizations: fulfilled by a SMART Health Card and a second FHIR Bundle. Depression screening form: fulfilled by a QuestionnaireResponse. Each item, its outcome, and the records that fulfill it (fulfills[]) Patient demographics patient fulfilled FHIR BUNDLE Patient and two MedicationRequests also fulfills medications Insurance card insurance · no record returned declined Medications medications · one kept private partial FHIR BUNDLE The same Bundle as above fulfills patient, medications Immunizations immunizations fulfilled SMART HEALTH CARD Signed immunization record FHIR BUNDLE One Immunization not on the card Depression screening form intake fulfilled QUESTIONNAIRE RESPONSE The patient's answers

Two rules hold the model together. Every item gets exactly one row in requestStatus[]. Every record lists the item ids it answers in fulfills[]. One record can answer several items, and one item can be answered by several records.

The request

The app builds this object and hands it to the wallet.

{
  "type": "smart-health-checkin-request",
  "version": "1",
  "id": "checkin-7f3a",
  "purpose": "Clinic check-in",
  "fhirVersions": ["4.0.1"],
  "items": [ … ]
}
type
Always smart-health-checkin-request.
version
Always "1".
id
Chosen by the app. The response echoes it back as requestId.
purpose
Why the app is asking, shown to the patient.
fhirVersions
The FHIR releases the app can read.
items
What the app would like, one entry per thing the patient reviews.

An item

Each item is one thing the patient reviews and one row in the outcomes.

{
  "id": "insurance",
  "title": "Insurance card",
  "summary": "Used for billing today's visit",
  "required": true,
  "content": {
    "kind": "selection.fhir",
    "profiles": ["http://hl7.org/fhir/us/insurance-card/StructureDefinition/C4DIC-Coverage"]
  },
  "accept": ["application/fhir+json"]
}
id
Short and unique within the request. The response refers to the item by this id. ITEM-1
title
What the patient sees.
summary
Optional. One more line for the patient.
required
Optional. Tells the patient the clinic needs this item.It is advice only. The patient can still decline, and the wallet can still answer unavailable. HOLD-3
content
What is being asked for. See What an item can ask for.
accept
Media types the app can read for this item, most preferred first. ACC-1

What an item can ask for

content.kind is either selection.fhir (share records the patient already has) or form.fhir (fill in a form). A wallet that sees a kind it doesn't know answers unsupported for that item and handles the rest of the request as usual SEL-9. The same goes for a selector whose members have the wrong types, such as a profilesFrom that is a string SEL-10.

An exact profile

Records that claim this StructureDefinition in meta.profile. List several to accept any of them.

{
  "id": "patient",
  "title": "Patient demographics",
  "content": {
    "kind": "selection.fhir",
    "profiles": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"]
  },
  "accept": ["application/fhir+json"]
}

Keep canonical strings exactly as written, including any |version CAN-2. A wallet reports fulfilled for a versioned profile only when a returned record claims that exact version CAN-5. See Versions in canonicals.

A profile family

Any profile from an implementation guide, such as all of US Core. A profile belongs to the family when its URL starts with the family URL and a / SEL-5. Add resourceTypes to narrow it. If profiles is also present, it widens the match rather than narrowing it SEL-3.

{
  "id": "medications",
  "title": "Medications",
  "content": {
    "kind": "selection.fhir",
    "profilesFrom": ["http://hl7.org/fhir/us/core"],
    "resourceTypes": ["MedicationRequest"]
  },
  "accept": ["application/fhir+json"]
}

No filter

Leave out all three filters to let the patient and their wallet decide what's relevant for the visit. Partial answers are expected SEL-7.

{
  "id": "anything-helpful",
  "title": "Anything else that would help your visit",
  "content": { "kind": "selection.fhir" },
  "accept": ["application/fhir+json", "application/smart-health-card"]
}

A form by URL

The wallet finds the Questionnaire from its canonical URL CAN-4, shows it, and returns a QuestionnaireResponse whose questionnaire is the same string FORM-5. If it can't load the form, it answers unsupported or error rather than guessing FORM-4.

{
  "id": "intake",
  "title": "Depression screening (PHQ-2)",
  "content": {
    "kind": "form.fhir",
    "questionnaireCanonical": "https://smart-health-checkin.org/connectathon/Questionnaire/phq-2.json"
  },
  "accept": ["application/fhir+json"]
}

A form, inline

Send the whole Questionnaire when it isn't published anywhere. You can send both: the canonical names the form and the inline copy is what gets shown FORM-2. Without a canonical, the answers' questionnaire is the inline form's url, plus |version if it has one FORM-5.

{
  "id": "visit-reason",
  "title": "Reason for today's visit",
  "content": {
    "kind": "form.fhir",
    "questionnaire": {
      "resourceType": "Questionnaire",
      "status": "active",
      "item": [
        { "linkId": "reason", "text": "What brings you in today?", "type": "string" }
      ]
    }
  },
  "accept": ["application/fhir+json"]
}

Versions in canonicals

A canonical can name one version of a form or profile by adding | and the version. The text before the first | is the URL; the rest is the version CAN-1.

{
  "id": "intake",
  "title": "Depression screening (PHQ-2)",
  "content": {
    "kind": "form.fhir",
    "questionnaireCanonical": "https://smart-health-checkin.org/connectathon/Questionnaire/phq-2.json|1"
  },
  "accept": ["application/fhir+json"]
}
  1. The wallet looks the form up any way it likes: a cache, a FHIR server search, or fetching the bare URL …/phq-2.json.
  2. It checks what it got: a Questionnaire whose url is exactly …/phq-2.json and whose version is exactly 1. Anything else doesn't count, and the item is unsupported CAN-4.
  3. The answers carry "questionnaire": "…/phq-2.json|1", the request's string unchanged FORM-5.

The same applies to profiles[]: a record counts toward …|1.2 only if its meta.profile has exactly that string CAN-5.

What the patient sees

  1. The wallet shows the purpose and each item's title and summary.
  2. For each item it finds matching records, or shows the form.
  3. The patient shares, leaves out, or fills in each item. They can share less than was asked for, and the wallet can offer other records that would help.
  4. The wallet sends back the records and one outcome per item.

The patient decides item by item HOLD-1. If they review the request and decline everything, the wallet still answers, with every item declined. If they close it without reviewing, nothing comes back and the browser call ends with an error HOLD-4.

Text in the request is not proof of who is asking. The browser and the wallet's trust decisions establish that, outside the JSON, so the request must not carry names, logos, or URLs claiming to identify the clinic ID-1 ID-2.

The response

{
  "type": "smart-health-checkin-response",
  "version": "1",
  "requestId": "checkin-7f3a",
  "artifacts": [ … ],
  "requestStatus": [ … ]
}
requestId
The request's id, exactly.
artifacts
The records the patient shared. Can be empty.
requestStatus
One outcome for every item in the request.

A record

The JSON calls each shared record an artifact. Two formats are defined.

FHIR JSON
{
  "id": "a1",
  "mediaType": "application/fhir+json",
  "fhirVersion": "4.0.1",
  "fulfills": ["patient", "medications"],
  "value": {
    "resourceType": "Bundle",
    "type": "collection",
    "entry": [ … ]
  }
}
SMART Health Card
{
  "id": "a2",
  "mediaType": "application/smart-health-card",
  "fulfills": ["immunizations"],
  "value": {
    "verifiableCredential": ["eyJ6aXAiOiJ…"]
  }
}
id
Unique within the response. ART-1
mediaType
application/fhir+json or application/smart-health-card.It must be in the accept list of every item the record fulfills. ACC-2
fulfills
The item ids this record answers. At least one.
fhirVersion
FHIR JSON only, such as 4.0.1 ART-2. A health card carries its FHIR version inside the signed card ART-6.
value
For FHIR JSON, a resource or a Bundle. For a health card, the card file's JSON.

Outcomes

Each item gets one row, whether or not any record came back for it RSP-2.

{ "item": "medications", "status": "partial", "message": "One medication was kept private" }
StatusMeaningFor example
fulfilledShared what was asked for.The insurance card was shared.
partialShared some of it.Two of three medications were shared.
declinedThe patient chose not to share.The patient turned off the insurance card.
unavailableThe wallet has nothing that matches.No immunizations are on file.
unsupportedThe wallet can't handle this kind of item.An unknown content.kind, or a form it can't load.
errorIt tried and failed.The patient portal behind it timed out.

fulfilled and partial should come with at least one record that lists the item ART-7. The others usually come with none.

What the app checks

Before using a response, the app checks it against the request it sent. One bad record or status row costs only that record or item; the rest of the response is still used. The client library does this for you, and the SMART Testing EHR runs the same checks against your wallet.

These reject the whole response

These affect only one item

These set aside one record

A set-aside record doesn't count toward the items it lists XV-4. Several valid records for one item are fine; the app looks at all of them MM-2. Problems below the JSON, in the mdoc envelope, are warnings rather than failures; see Security and timeouts.

Passing these checks means the response is well formed. Whether to trust a health card's issuer XV-13 or to file a record into the chart is up to the clinic.

Complete example

The request and response from At a glance, in full.

Request
{
  "type": "smart-health-checkin-request",
  "version": "1",
  "id": "checkin-7f3a",
  "purpose": "Clinic check-in",
  "fhirVersions": ["4.0.1"],
  "items": [
    {
      "id": "patient",
      "title": "Patient demographics",
      "content": {
        "kind": "selection.fhir",
        "profiles": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"]
      },
      "accept": ["application/fhir+json"]
    },
    {
      "id": "insurance",
      "title": "Insurance card",
      "summary": "Used for billing today's visit",
      "required": true,
      "content": {
        "kind": "selection.fhir",
        "profiles": ["http://hl7.org/fhir/us/insurance-card/StructureDefinition/C4DIC-Coverage"]
      },
      "accept": ["application/fhir+json"]
    },
    {
      "id": "medications",
      "title": "Medications",
      "content": {
        "kind": "selection.fhir",
        "profilesFrom": ["http://hl7.org/fhir/us/core"],
        "resourceTypes": ["MedicationRequest"]
      },
      "accept": ["application/fhir+json"]
    },
    {
      "id": "immunizations",
      "title": "Immunizations",
      "content": {
        "kind": "selection.fhir",
        "profilesFrom": ["http://hl7.org/fhir/us/core"],
        "resourceTypes": ["Immunization"]
      },
      "accept": ["application/smart-health-card", "application/fhir+json"]
    },
    {
      "id": "intake",
      "title": "Depression screening (PHQ-2)",
      "content": {
        "kind": "form.fhir",
        "questionnaireCanonical": "https://smart-health-checkin.org/connectathon/Questionnaire/phq-2.json"
      },
      "accept": ["application/fhir+json"]
    }
  ]
}
Response
{
  "type": "smart-health-checkin-response",
  "version": "1",
  "requestId": "checkin-7f3a",
  "artifacts": [
    {
      "id": "a1",
      "mediaType": "application/fhir+json",
      "fhirVersion": "4.0.1",
      "fulfills": ["patient", "medications"],
      "value": {
        "resourceType": "Bundle",
        "type": "collection",
        "entry": [
          { "resource": { "resourceType": "Patient", "meta": { "profile": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"] }, "name": [{ "family": "Okafor", "given": ["Sam"] }], "birthDate": "1986-04-12" } },
          { "resource": { "resourceType": "MedicationRequest", "meta": { "profile": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest"] }, "status": "active", "medicationCodeableConcept": { "text": "Metformin ER 500 mg" } } },
          { "resource": { "resourceType": "MedicationRequest", "meta": { "profile": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest"] }, "status": "active", "medicationCodeableConcept": { "text": "Lisinopril 10 mg" } } }
        ]
      }
    },
    {
      "id": "a2",
      "mediaType": "application/smart-health-card",
      "fulfills": ["immunizations"],
      "value": { "verifiableCredential": ["eyJ6aXAiOiJERUYiLCJhbGciOiJFUzI1NiIsImtpZCI6Ii4uLiJ9.…"] }
    },
    {
      "id": "a3",
      "mediaType": "application/fhir+json",
      "fhirVersion": "4.0.1",
      "fulfills": ["immunizations"],
      "value": {
        "resourceType": "Immunization",
        "meta": { "profile": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-immunization"] },
        "status": "completed",
        "vaccineCode": { "text": "Tdap" },
        "occurrenceDateTime": "2021-05-18"
      }
    },
    {
      "id": "a4",
      "mediaType": "application/fhir+json",
      "fhirVersion": "4.0.1",
      "fulfills": ["intake"],
      "value": {
        "resourceType": "QuestionnaireResponse",
        "questionnaire": "https://smart-health-checkin.org/connectathon/Questionnaire/phq-2.json",
        "status": "completed",
        "item": [
          { "linkId": "phq2-1", "text": "Little interest or pleasure in doing things", "answer": [{ "valueCoding": { "system": "http://loinc.org", "code": "LA6568-5", "display": "Not at all" } }] },
          { "linkId": "phq2-2", "text": "Feeling down, depressed, or hopeless", "answer": [{ "valueCoding": { "system": "http://loinc.org", "code": "LA6569-3", "display": "Several days" } }] }
        ]
      }
    }
  ],
  "requestStatus": [
    { "item": "patient", "status": "fulfilled" },
    { "item": "insurance", "status": "declined" },
    { "item": "medications", "status": "partial", "message": "One medication was kept private" },
    { "item": "immunizations", "status": "fulfilled" },
    { "item": "intake", "status": "fulfilled" }
  ]
}

Where next