Patient Merge Interface

Version 1.0

7 March 2017

The MITRE Corporation

Introduction

Purpose

This document describes the commands and objects used for the Patient Merging Service. This service provides a RESTful API using FHIR objects for requests and responses. This document will be updated periodically as additional functionality is added to the Patient Merging Service.

Scope

This document describes the interaction between the Patient Merging Service and a FHIR server, with the aim to merge FHIR resources identified as duplicates into a new, updated FHIR resource. The web service calls and data payloads are detailed below, as well as examples for resolving or aborting these merge operations. A later section documents convenience features for sharing a merge session.

Assumptions

This document assumes FHIR Specification STU3, which is the current officially released version at the time of this writing.

Technical Approach

The Patient Merging Service is invoked by a client providing two FHIR URLs for Patient resources, corresponding to patient data assumed to be similar enough to warrant a desire to merge into a single resource. This invocation can be referred to as a “merge session” for the remainder of this document. If the records can be successfully be auto merged by the service, the service creates a new FHIR resource and sends the resulting Bundle (referred to as the target Bundle) back to the client in the response. If the records need intervention to choose the correct merge result, an OperationOutcome is returned to the client, with IDs to the conflicting data values. The user enters or chooses the correct data, resubmits the request to the service (via the /resolve endpoint), and the validation continues to resolve the new object. The resolving of conflicting data is designed to take place on an incremental basis if the client desires (e.g. resolve conflicting medication dosages, resubmit, then resolve a conflicting encounter dates, resubmit, etc.). The client can also choose to abort the merge session, which will stop the creation of the new FHIR resource, deleting the in-progress, new resource as well. A merge session can also be transmitted to another user (e.g. a clinician) for consultation on the appropriate value that should be used to resolve a merge.

Use Cases

The first use case is a user interface program that offers fields and actions for viewing and updating merge data. The second use case is an outside party receiving the URL for a merge session, wherein that party can then modify and resubmit the correct data, using the same endpoints.

Message Definitions

The ptmerge service is exposed as a RESTful API. All communication with the ptmerge service is done through the following API routes:


POST /merge

Initiates a new merge session. Fully qualified URLs to two FHIR bundles must be provided as query parameters:

There are 2 simple ways to construct a path to source bundles:

GET [base]/Bundle/:id

GET [base]/Patient/:id/$everything

And one way that offers more control over the content in the source bundle:

GET [base]/Patient?_id=:id&_include=<paths_to_include>&_revinclude=<paths_to_include>

Where the paths to _include and _revinclude for a given resource are described in the FHIR server’s CapabilityStatement. In practice, $everything is actually the union of _include=*&_revinclude=*

During the merge the two source bundles will not, and should not be modified in any way.

Request

POST /merge?source1=http://fhir.example.com/Bundle/58939a3188a94d1a28634257&source2=http://fhir.example.com/Bundle/58939a3188a94d1a28634206

The POST body should be empty. Any content in the body will be ignored.

Responses

Status Description
200 The merge had no conflicts and succeeded immediately. The response body contains the complete, merged bundle.
201 The merge has one or more outstanding conflicts. The response body contains a bundle of OperationOutcomes detailing the merge conflicts.
500 An unexpected error occurred. That’s all we know. The current state of the merge session is unknown.

200 OK: Successful Merge

If a merge succeeds immediately, ptmerge responds with the complete, merged bundle in JSON format:

{
  "resourceType": "Bundle",
  "id": "58939a3188a94d1a28634257",
  "type": "collection",
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "58939a3188a94d1a28634257",
        "name": [{
          "family": "Abbott",
      	  "given": ["Clint"]
        }],
        "gender": "male",
        "birthDate": "1950-09-02"
      }
    }
  ]
}

201 Created: Merge With Conflicts

If a merge has conflicts, ptmerge responds with a bundle of one or more OperationOutcome resources. Each OperationOutcome describes a single merge conflict. A new merge session is started and the merge_id is returned in the Location header. A new “target” bundle is created on the host FHIR server and is populated with merged resources as the merge session progresses. The OperationOutcomes in the response are also created on the host FHIR server.

{
  "id": "58939a3188a94d1a28634257",
  "resourceType": "Bundle",
  "type": "transaction-response",
  "entry": [
    {
      "resource": {
        "resourceType": "OperationOutcome",
        "id": "58b5856c97bba9a4096d9916",
        "issue": [
          {
            "severity": "information",
            "code": "conflict",
            "diagnostics": "Patient:58b5856b97bba9a40db1a52a",
            "location": [
              "maritalStatus.coding[0].display",
              "maritalStatus.coding[0].code"
            ]
          }
        ]
      },
      "response": {
        "status": "201"
      }
    }
  ]
}

The diagnostics field captures information about the target resource for each conflict in the target bundle. This field indicates the resource type of the target (e.g. Patient) and its ID (e.g. 58b5856b97bba9a40db1a52a), separated by a colon.

POST /merge/:merge_id/resolve/:conflict_id

Attempts to resolve a merge conflict. The request body must contain the complete resource that resolves the conflict. To avoid ambiguity a valid request always resolves the merge conflict.

:merge_id - The ID referring to the current merge session.

:conflict_id - The ID referring to the merge conflict that is currently being resolved. This matches an OperationOutcome ID.

Request

POST /merge/58939a3188a94d1a28634257/resolve/58939a3188a94d1a28634110

The POST body must contain the resource that resolves the conflict, in FHIR JSON format. For example:

{
  "resourceType": "Encounter",
  "id": "58939a3188a94d1a28634257",
  "status": "finished",
  "type": [{
    "coding": [{
      "system": "http://www.ama-assn.org/go/cpt",
      "code": "99201"
    }],
    "text": "Office Visit"
  }],
  "patient": {
    "reference": "5a909a318da94d1h28i34236"
  },
  "period": {
    "start": "2012-09-20T08:00:00-05:00"
  }
}

Responses

Status Description
200 The conflict resolution request succeeded. This does not necessarily mean that the conflict was resolved. The response body contains a bundle detailing the outcome of the conflict resolution.
400 Bad request. This may occur if the specific merge session or merge conflict was already resolved.
404 Not found. The specified merge session or merge conflict does not exist.
500 An unexpected error occurred. That’s all we know. The current state of the merge session is unknown.

200 OK: The Resolution Request Succeeded

There are two possible outcomes in this scenario:

  1. The last merge conflict in this session was resolved. The response body contains the complete, merged bundle as FHIR JSON.

  2. The conflict was resolved, but additional conflicts still exist. The response body contains the current set of unresolved merge conflicts, as a bundle of OperationOutcomes.

POST /merge/:merge_id/abort

Terminates an in-progress merge session. This operation cannot be undone.

:merge_id - The ID referring to the current merge session.

An abort request also:

  1. Deletes all FHIR resources related to the merge.
  2. Deletes any record of the merge session.

Request

POST /merge/5a909a318da94d1h28i34236/abort

The POST body should be empty. Any content in the body will be ignored.

Responses

Status Description
204 The merge was successfully aborted. The target bundle and all OperationOutcomes that were part of this merge session were also deleted.
404 Not found. The specified merge session does not exist.
500 An unexpected error occurred. That’s all we know. The current state of the merge session is unknown.

GET /merge

Get the metadata for all merge sessions that ptmerge has processed.

Request

GET /merge

Responses

This route returns custom JSON, not FHIR JSON.

Status Description
200 The metadata request succeeded. The response body contains the complete metadata for all known merge sessons.
500 An unexpected error occurred. That’s all we know.

200 OK: Valid Request

If the request is valid, ptmerge responds with the JSON metadata for all known merge sessions. For example:

{
  "timestamp": "2017-02-28T09:31:57.280949164-05:00",
  "merges": [
    {
      "id": "58b48f7497bba924465bc141",
      "source1": "http://localhost:3001/Bundle/58b4890a97bba924cd97ddfe",
      "source2": "http://localhost:3001/Bundle/58b4893397bba924cd97ddff",
      "targetBundle": "http://localhost:3001/Bundle/58b48f7497bba924cd97de03",
      "conflicts": {
        "58b48f7497bba924cd97de04": {
          "operationOutcome": "http://localhost:3001/OperationOutcome/58b48f7497bba924cd97de04",
          "targetResource": {
            "id": "58b48f7497bba924465bc137",
            "type": "Encounter"
          },
          "resolved": false
        },
        "58b48f7497bba924cd97de05": {
          "operationOutcome": "http://localhost:3001/OperationOutcome/58b48f7497bba924cd97de05",
          "targetResource": {
            "id": "58b48f7497bba924465bc13b",
            "type": "Patient"
          },
          "resolved": false
        }
      },
      "completed": false,
      "start": "2017-03-08T12:34:02.551-05:00"
    },
    {
      "id": "58b5820d97bba9a40db1a51e",
      "source1": "http://localhost:3001/Bundle/58b4890a97bba924cd97ddfe",
      "source2": "http://localhost:3001/Bundle/58b4893397bba924cd97ddff",
      "targetBundle": "http://localhost:3001/Bundle/58b5820d97bba9a4096d9911",
      "conflicts": {
        "58b5820d97bba9a4096d9912": {
          "operationOutcome": "http://localhost:3001/OperationOutcome/58b5820d97bba9a4096d9912",
          "targetResource": {
            "id": "58b5820d97bba9a40db1a513",
            "type": "Patient"
          },
          "resolved": false
        },
        "58b5820d97bba9a4096d9913": {
          "operationOutcome": "http://localhost:3001/OperationOutcome/58b5820d97bba9a4096d9913",
          "targetResource": {
            "id": "58b5820d97bba9a40db1a517",
            "type": "Encounter"
          },
          "resolved": true
        }
      },
      "completed": false,
      "start": "2017-03-08T12:34:02.551-05:00"
    }
  ]
}

In this example there are 2 known merge sessions:

  1. The first is an in-progress merge session with 2 conflicts, both outstanding.

  2. The second is a merge session with 2 conflicts, one resolved.

GET /merge/:merge_id

Get the metadata for a specific merge session.

:merge_id - The ID referring to the specific merge session.

Request

GET /merge/58939a3188a94d1a28634257

Responses

Status Description
200 The metadata request succeeded. The response body contains the metadata for one, specific merge.
404 Not found. The specified merge session does not exist.
500 An unexpected error occurred. That’s all we know.

200 OK: Valid Request

If the request is valid, ptmerge responds with the JSON metadata for the merge session. For example:

{
  "timestamp": "2017-02-28T10:27:50.531954239-05:00",
  "merge": {
    "id": "58b5856c97bba9a40db1a52f",
    "source1": "http://localhost:3001/Bundle/58b4890a97bba924cd97ddfe",
    "source2": "http://localhost:3001/Bundle/58b4893397bba924cd97ddff",
    "targetBundle": "http://localhost:3001/Bundle/58b5856b97bba9a4096d9914",
    "conflicts": {
      "58b5856c97bba9a4096d9915": {
        "operationOutcome": "http://localhost:3001/OperationOutcome/58b5856c97bba9a4096d9915",
        "targetResource": {
          "id": "58b5856b97bba9a40db1a526",
          "type": "Encounter"
        },
        "resolved": true
      },
      "58b5856c97bba9a4096d9916": {
        "operationOutcome": "http://localhost:3001/OperationOutcome/58b5856c97bba9a4096d9916",
        "targetResource": {
          "id": "58b5856b97bba9a40db1a52a",
          "type": "Patient"
        },
        "resolved": true
      }
    },
    "completed": false,
    "start": "2017-03-08T12:34:02.551-05:00"
  }
}

GET /merge/:merge_id/target

Get a merge session’s target bundle.

:merge_id - The ID referring to the specific merge session.

Request

GET /merge/58939a3188a94d1a28634257/target

Responses

Status Description
200 The request succeeded. The response body contains the up-to-date target bundle.
404 Not found. The specified merge session does not exist.
500 An unexpected error occurred. That’s all we know.

Note that the bundle returned in a successful response may be partially complete. This depends entirely on the current state of the merge session.

POST /merge/:merge_id/target/resources/:resource_id

Updates a resource in the target bundle. This endpoint is for updating resources that have no conflicts. To update a resource with conflicts, see Resolve Conflict.

:merge_id - The ID referring to the specific merge session. :resource_id - The ID of the resource in the target bundle to update.

Request

POST /merge/58939a3188a94d1a28634257/target/resources/58941a3188a95d1a2863425b

The request body must contain an updated resource of the same type. For example:

{
  "resourceType": "Encounter",
  "id": "58939a3188a94d1a28634257",
  "status": "finished",
  "type": [{
    "coding": [{
      "system": "http://www.ama-assn.org/go/cpt",
      "code": "99201"
    }],
    "text": "Office Visit"
  }],
  "patient": {
    "reference": "5a909a318da94d1h28i34236"
  },
  "period": {
    "start": "2012-09-20T08:00:00-05:00"
  }
}

Responses

Status Description
200 The request succeeded. The response body contains the up-to-date target resource.
400 Bad request. The target resource does not exist, or the request body was unreadable.
404 Not found. The specified merge session does not exist.
500 An unexpected error occurred. That’s all we know.

The response body contains the updated resource, for example:

{
  "resourceType": "Encounter",
  "id": "58939a3188a94d1a28634257",
  "status": "finished",
  "type": [{
    "coding": [{
      "system": "http://www.ama-assn.org/go/cpt",
      "code": "99201"
    }],
    "text": "Office Visit"
  }],
  "patient": {
    "reference": "5a909a318da94d1h28i34236"
  },
  "period": {
    "start": "2012-09-20T08:00:00-05:00"
  }
}

DELETE /merge/:merge_id/target/resources/:resource_id

Deletes a resource in the target bundle. This endpoint is for deleting resources that have no conflicts. To delete a conflict, see Delete Conflict. This operation cannot be undone.

:merge_id - The ID referring to the specific merge session. :resource_id - The ID of the resource in the target bundle to update.

Request

DELETE /merge/58939a3188a94d1a28634257/target/resources/58939a3188a94d1a28634258

The request body should be empty, and any content in the body will be ignored.

Responses

Status Description
204 No content, the request succeeded. The target resource was deleted successfully.
404 Not found. The specified merge session does not exist.
500 An unexpected error occurred. The target resource does not exist, or another unknown error occurred. That’s all we know.

GET /merge/:merge_id/conflicts

Get a merge session’s outstanding merge conflicts.

:merge_id - The ID referring to the specific merge session.

Request

GET /merge/58939a3188a94d1a28634257/conflicts

Responses

Status Description
200 The request succeeded. The response body contains a bundle of OperationOutcomes detailing the merge session’s outstanding merge conflicts. If the merge is complete or no more conflicts exist, this may be an empty bundle.
404 Not found. The specified merge session does not exist.
500 An unexpected error occurred. That’s all we know.

GET /merge/:merge_id/resolved

Get a merge session’s resolved merge conflicts.

:merge_id - The ID referring to the specific merge session.

Request

GET /merge/58939a3188a94d1a28634257/resolved

Responses

Status Description
200 The request succeeded. The response body contains a bundle of OperationOutcomes detailing the merge session’s resolved merge conflicts. If no merge conflicts were resolved yet, this may be an empty bundle.
404 Not found. The specified merge session does not exist.
500 An unexpected error occurred. That’s all we know.

DELETE /merge/:merge_id/conflicts/:conflict_id

Deletes a merge conflict and its related resources. This operation cannot be undone.

:merge_id - The ID referring to the specific merge session. :conflict_id - The ID of the conflict to delete (matches the ID of an OperationOutcome).

Request

DELETE /merge/58939a3188a94d1a28634257/conflicts/58939a3188a94d1a28634258

The request body should be empty, and any content in the body will be ignored.

Responses

Status Description
204 No content, the request succeeded. The conflict and its related resources were deleted successfully.
404 Not found. The specified merge session does not exist.
500 An unexpected error occurred. The target resource does not exist, or another unknown error occurred. That’s all we know.