When HTTP 200 is NOT OK

Are you under the impression that receiving an HTTP 200 (“OK”) response means your FHIR implementation is conformant to the FHIR specifications or implementation guide(s)? If so, we are sorry to disappoint you.

In our work to help implementers accelerate their FHIR implementations, we see many tests that are looking for that “HTTP 200” response as an indication that the FHIR functionality is working correctly.

HTTP is an internet standard administered by the Worldwide Web Consortium (“W3C”). In HTTP standard 1.1, the “200” response is defined as “The request has succeeded.” This is certainly a good start, but it is just that: The beginning of the full transaction.

  • Receiving an HTTP 200 response simply means that the server received your request, and it conforms to the HTTP standards for whatever HTTP request (“verb”) you were using (in FHIR, usually a GET).

HTTP 200 does NOT mean “I totally understand what you are asking for, and I have responded with exactly that.” Instead, it simply means “I understand your request.”

201 is similar

According to the W3C specifications, an HTTP 201 response code means “The request has been fulfilled and resulted in a new resource being created.” This may seem better–like we’re finally getting somewhere, but 201 is just the OK for a “PUT” (“create”).

Here’s the key takeaway:

HTTP response codes are rough signals, not validation of interoperability.

Inspecting the Full Response is Required

We know this sounds obvious–and it should be.

You don’t know whether the other system really understood your request until you see the actual response (the content of what you asked for). In the case of requesting a FHIR resource (like a patient), what we care about is that the patient we asked for is returned to us, and it matches (for example) the search criteria we used to ask for that patient.

Example: Read a Patient

Status:HTTP/1.1 200 OK
Headers:
Connection
keep-alive
Content-Length
2104
Content-Location
http://wildfhir4.aegis.net/fhir4-0-1/Patient/1def35f4de77443aa4dd4e8ff9e0662a/_history/1

Here we see the “HTTP/1.1 200 OK” status code in the response header. This looks great, right? We also see that we requested a patient with the ID value of “1def35f4de77443aa4dd4e8ff9e0662a”.

OK–but that’s all we can conclude without evaluating what was actually returned in the response body by the other system:

{
  "resourceType" : "Patient",
  "id" : "1def35f4de77443aa4dd4e8ff9e0662a",
  "meta" : {
    "versionId" : "1",
    "lastUpdated" : "2022-09-19T09:53:57.248-04:00",
    "security" : [{
      "system" : "http://terminology.hl7.org/CodeSystem/v3-ActReason",
      "code" : "HTEST",
      "display" : "test health data"
    }]
  },
  "text" : {
    "status" : "generated",
    "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p>Touchstone Test Data - Patient: Michael George Feelsgood</p></div>"
  },
  "identifier" : [{
    "use" : "usual",
    "type" : {
      "coding" : [{
        "system" : "http://terminology.hl7.org/CodeSystem/v2-0203",
        "code" : "MR"
      }]
    },
    "system" : "urn:oid:1.2.36.146.595.217.0.1",
    "value" : "8241905622-001",
    "period" : {
      "start" : "2012-09-19"
    },
    "assigner" : {
      "display" : "Acme Healthcare"
    }
  }],
  "active" : true,
  "name" : [{
    "use" : "official",
    "family" : "FeelsgoodAUeUtNc",
    "given" : ["MichaelAUeUtNc",
    "George"]
  }],
  "telecom" : [{
    "system" : "phone",
    "value" : "(001) 001 2379",
    "use" : "home"
  }],
  "gender" : "male",
  "birthDate" : "1997-09-19",
  "deceasedBoolean" : false,
  "address" : [{
    "use" : "home",
    "line" : ["91861 Davis Ford Rd"],
    "city" : "Manassas",
    "state" : "VA",
    "postalCode" : "20110"
  }],
  "contact" : [{
    "relationship" : [{
      "coding" : [{
        "system" : "http://terminology.hl7.org/CodeSystem/v2-0131",
        "code" : "N"
      }]
    }],
    "name" : {
      "family" : "FeelsgoodAUeUtNc",
      "given" : ["Margaret"]
    },
    "telecom" : [{
      "system" : "phone",
      "value" : "(001) 001 2379",
      "use" : "home"
    }],
    "address" : {
      "use" : "home",
      "line" : ["91861 Davis Ford Rd"],
      "city" : "Manassas",
      "state" : "VA",
      "postalCode" : "20110"
    },
    "gender" : "female"
  }],
  "managingOrganization" : {
    "reference" : "Organization/1a9970168ede4561a3bb1c37e6e7317c"
  }
}

This is a lot to inspect manually, but any decent FHIR testing engine can analyze these responses.

Example: Meaningful Diagnostics

A great FHIR testing engine can identify the specific errors and tell you exactly what is wrong, like in this example from a request for an advanced EOB. Here’s that lovely “HTTP 200” return code:

Operationsearch Bundle
StatusHTTP/1.1 200
ResourceBundleIdfc0a41d4-e8f5-419b-9aea-882ff381d466
 
But inspecting the payload (an actual example here from Touchstone) tells the real story:
 
Validation of response body against profile ‘http://hl7.org/fhir/us/davinci-pct/StructureDefinition/davinci-pct-aeob-bundle’ by FHIR specification’s Validation Engine produced the following results:
  1. ERROR: ExplanationOfBenefit.insurer: minimum required = 1, but only found 0
    (from http://hl7.org/fhir/StructureDefinition/ExplanationOfBenefit). Location:
     Bundle.entry[0].resource.ofType(ExplanationOfBenefit) (line 72, col 23).
  2. ERROR: ExplanationOfBenefit.insurer: minimum required = 1, but only found 0 
    (from http://hl7.org/fhir/us/davinci-pct/StructureDefinition/davinci-pct-aeob). Location: 
    Bundle.entry[0].resource.ofType(ExplanationOfBenefit) (line 72, col 23).

That “Other System” is Key

Testing as illustrated here should certainly be performed against your own implementation. That establishes your “we think we are compliant” baseline level of confidence in your implementation.

To actually know you are interoperable, you need to then test your implementation against other systems.

You can–and should–test with your business partners (other implementers with whom you plan to exchange health data using FHIR), but your should start with testing against “known good” reference implementations (RIs). If you are implementing a FHIR client, you should start with testing against a FHIR RI acting in the role of a server. If your implementation is a FHIR server, you should test against a FHIR RI acting in the role of a FHIR client.

If you are using Touchstone, you can select the AEGIS TouchstoneFHIR RI to initiate known-good requests to your system to validate your responses:

When validating your FHIR conformance as a request initiator, you can choose the AEGIS WildFhir Reference Implementation to respond with known-good responses:

After you have validated your implementation against these reference implementations (which takes minutes using Touchstone), you can select the implementations of other implementers to validate your real-world interoperability.

Don’t Settle for “OK”

There you have it. HTTP “OK” is a necessary first step, but it won’t objectively measure your interoperability, and it won’t make your FHIR implementations go faster.

Expect more. Try Touchstone for free at Touchstone.com.

Leave a Reply

Your email address will not be published. Required fields are marked *