Connected Diagnostics Platform

DXAPI enables 3rd party applications to consume tests from diagnostic devices in a secure, scalable and interoperable way.

If you are a Health Applications software developer, DXAPI will allow you to provide your users with information about tests, without having to worry about connecting to different devices using different protocols. You use a simple and modern API and the platform will take care of the rest.

If you are a diagnostics device manufacturer, implementing DXAPI will enable your device to provide value added to your customers, by integrating into a platform that can be extended with unlimited number of applications. Your device reports messages to a secure and private server where your customers will define who has access to what. A number of 3rd party application can then be granted access to a subset of those tests and provide new services to patients, health workes, clinicians and even your own support staff.

A few examples of apps that could be created using this platform are:

GET /tests/schema[.format] Tests schema

{
  $schema: "http://json-schema.org/draft-04/schema#",
  type: "object",
  title: "en-US",
  properties: {
    test: {
      type: "object",
      title: "Test",
      properties: {
        id: {
          title: "Id",
          type: "string",
          searchable: true
        },
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        start_time: {
          title: "Start Time",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: true
        },
        end_time: {
          title: "End Time",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: true
        },
        reported_time: {
          title: "Reported Time",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: true
        },
        updated_time: {
          title: "Updated Time",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: true
        },
        error_code: {
          title: "Error Code",
          type: "integer",
          searchable: true
        },
        error_description: {
          title: "Error Description",
          type: "string",
          searchable: false
        },
        site_user: {
          title: "Site User",
          type: "string",
          searchable: true
        },
        name: {
          title: "Name",
          type: "string",
          searchable: true
        },
        status: {
          title: "Status",
          type: "string",
          enum: [
            "invalid",
            "error",
            "no_result",
            "success",
            "in_progress"
          ],
          values: {
            invalid: {
              name: "Invalid"
            },
            error: {
              name: "Error"
            },
            no_result: {
              name: "No Result"
            },
            success: {
              name: "Success"
            },
            in_progress: {
              name: "In Progress"
            }
          },
          searchable: true
        },
        assays: {
          title: "Assays",
          type: "array",
          items: {
            type: "object",
            properties: {
              name: {
              title: "Name",
              type: "string",
              searchable: true
              },
              condition: {
                title: "Condition",
                type: "string",
                enum: [
                  "atsc",
                  "cd4_count",
                  "ct",
                  "emb",
                  "hiv",
                  "hiv_1_m_n",
                  "hiv_1_o",
                  "hiv_2",
                  "inh",
                  "lvx",
                  "malaria_pf",
                  "malaria_pv",
                  "mtb",
                  "ng",
                  "pas",
                  "pza",
                  "rif",
                  "stm",
                  "tch"
                ],
                searchable: true
              },
              result: {
                title: "Result",
                type: "string",
                enum: [
                  "positive",
                  "negative",
                  "indeterminate",
                  "n/a"
                ],
                values: {
                  positive: {
                    name: "Positive",
                    kind: "positive"
                  },
                  negative: {
                    name: "Negative",
                    kind: "negative"
                  },
                  indeterminate: {
                    name: "Indeterminate"
                  },
                  n/a: {
                    name: "N/A"
                  }
                },
                searchable: true
              },
              quantitative_result: {
                title: "Quantitative Result",
                type: "string",
                searchable: true
              }
            }
          },
          searchable: true
        },
        type: {
        title: "Type",
        type: "string",
        enum: [
          "specimen",
          "qc"
        ],
        values: {
          specimen: {
            name: "Specimen"
          },
          qc: {
            name: "Qc"
          }
        },
        searchable: true
        }
      }
    },
    sample: {
      type: "object",
      title: "Sample",
      properties: {
        id: {
          title: "Id",
          type: "string",
          searchable: true
        },
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        type: {
          title: "Type",
          type: "string",
          searchable: true
        },
        collection_date: {
          title: "Collection Date",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: false
        }
      }
    },
    device: {
      type: "object",
      title: "Device",
      properties: {
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        name: {
          title: "Name",
          type: "string",
          searchable: false
        },
        model: {
          title: "Model",
          type: "string",
          searchable: true
        },
        serial_number: {
          title: "Serial Number",
          type: "string",
          searchable: true
        }
      }
    },
    institution: {
      type: "object",
      title: "Institution",
      properties: {
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        name: {
          title: "Name",
          type: "string",
          searchable: false
        }
      }
    },
    site: {
      type: "object",
      title: "Site",
      properties: {
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        name: {
          title: "Name",
          type: "string",
          searchable: false
        },
        path: {
          title: "Path",
          type: "string",
          searchable: true
        }
      }
    },
    patient: {
      type: "object",
      title: "Patient",
      properties: {
        id: {
          title: "Id",
          type: "string",
          searchable: false
        },
        name: {
          title: "Name",
          type: "string",
          searchable: false
        },
        dob: {
          title: "Dob",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: false
        },
        gender: {
          title: "Gender",
          type: "string",
          enum: [
            "male",
            "female",
            "other"
          ],
          values: {
            male: {
              name: "Male"
            },
            female: {
              name: "Female"
            },
            other: {
              name: "Other"
            }
          },
          searchable: true
        },
        email: {
          title: "Email",
          type: "string",
          searchable: false
        },
        phone: {
          title: "Phone",
          type: "string",
          searchable: false
        }
      }
    },
    location: {
      type: "object",
      title: "Location",
      properties: {
        id: {
          title: "Id",
          type: "string",
          searchable: false
        },
        parents: {
          title: "Parents",
          type: "string",
          searchable: true
        },
        admin_levels: {
          title: "Admin Levels",
          type: "string",
          searchable: true
        },
        lat: {
          title: "Lat",
          type: "string",
          searchable: false
        },
        lng: {
          title: "Lng",
          type: "string",
          searchable: false
        }
      },
      location-service: {
        url: "http://locations-stg.instedd.org",
        set: "ne"
      }
    },
    encounter: {
      type: "object",
      title: "Encounter",
      properties: {
        id: {
          title: "Id",
          type: "string",
          searchable: false
        },
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        patient_age: {
          title: "Patient Age",
          type: "object",
          class: "duration",
          properties: {
            milliseconds: {
              type: "integer",
              title: "Patient Age milliseconds"
            },
            seconds: {
              type: "integer",
              title: "Patient Age seconds"
            },
            minutes: {
              type: "integer",
              title: "Patient Age minutes"
            },
            hours: {
              type: "integer",
              title: "Patient Age hours"
            },
            days: {
              type: "integer",
              title: "Patient Age days"
            },
            months: {
              type: "integer",
              title: "Patient Age months"
            },
            years: {
              type: "integer",
              title: "Patient Age years"
            }
          },
          searchable: true
        },
        start_time: {
          title: "Start Time",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: true
        },
        end_time: {
          title: "End Time",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: true
        },
        observations: {
          title: "Observations",
          type: "string",
          searchable: false
        },
        diagnosis: {
          title: "Diagnosis",
          type: "array",
          items: {
            type: "object",
            properties: {
              name: {
                title: "Name",
                type: "string",
                searchable: true
              },
              condition: {
                title: "Condition",
                type: "string",
                enum: [
                  "atsc",
                  "cd4_count",
                  "ct",
                  "emb",
                  "hiv",
                  "hiv_1_m_n",
                  "hiv_1_o",
                  "hiv_2",
                  "inh",
                  "lvx",
                  "malaria_pf",
                  "malaria_pv",
                  "mtb",
                  "ng",
                  "pas",
                  "pza",
                  "rif",
                  "stm",
                  "tch"
                ],
                searchable: true
              },
              result: {
                title: "Result",
                type: "string",
                enum: [
                  "positive",
                  "negative",
                  "indeterminate"
                ],
                values: {
                  positive: {
                    name: "Positive",
                    kind: "positive"
                  },
                  negative: {
                    name: "Negative",
                    kind: "negative"
                  },
                  indeterminate: {
                    name: "Indeterminate"
                  }
                },
                searchable: true
              },
              quantitative_result: {
                title: "Quantitative Result",
                type: "string",
                searchable: true
              }
            }
          },
          searchable: true
        }
      }
    }
  }
}

Policies

The reference implementation uses a flexible policy model to set user permissions. It allows a user to grant specific permissions to specific resources.

A policy consists of a list of statements that grants or denies permission to execute a list of actions over a list of resources. The policy can be delegable to other users.

The possible actions are:

  • device:read
  • device:update
  • device:delete
  • device:support
  • device:regenerateKey
  • device:generateActivationToken
  • device:reportMessage

  • deviceModel:read
  • deviceModel:update
  • deviceModel:delete
  • deviceModel:publish

  • encounter:read
  • encounter:update
  • encounter:pii

  • institution:create
  • institution:read
  • institution:update
  • institution:delete
  • institution:createSite
  • institution:registerDevice
  • institution:registerDeviceModel
  • institution:createRole
  • institution:createPatient
  • institution:readUsers

  • patient:read
  • patient:update
  • patient:delete

  • role:read
  • role:update
  • role:delete
  • role:assignUser
  • role:removeUser

  • site:read
  • site:update
  • site:delete
  • site:assignDevice
  • site:createRole
  • site:createEncounter
  • site:readUsers

  • testResult:query
  • testResult:pii
  • testResult:medicalDashboard

  • user:update

The supported resources are:

  • device
  • deviceModel
  • encounter
  • institution
  • patient
  • role
  • site
  • testResult
  • user

A resource can represent all the list of resources or a single one. They follow the URI standard.

A list of resources can be blacklisted using the except statement.

Examples

The following policy will allow the user to perform any action on any institution, device or site that the grantee can access, with the exception of the site 2:

{
  "statement": [
    {
      "action": "*",
      "delegable" : false,
      "resource": [
        "institution",
        "device",
        "site"
      ]
      "except": "site/2"
    }
  ]
}

This policy would only allow control over the institution with id 1, and only its devices and laboratories.

{
  "statement": [
    {
      "delegable" : false,
      "action": "*",
      "resource": [
        "institution/1",
        "device?institution=1",
        "site?institution=1"
      ]
    }
  ]
}

The same way, this one would grant only read access to the site with id 4, but this time the receiver can delegate this rights to another user

{
  "statement": [
    {
      "action": [
        "readInstitution",
        "readLaboratory",
        "readDevice",
        "updateDevice",
        "deleteDevice",
        "assignDeviceLaboratory",
        "regenerateDeviceKey",
        "queryTest",
        "reportMessage"
      ],
      "delegable": true,
      "resource": [
        "institution/1",
        "device?institution=1&site=4",
        "site/4"
      ]
    }
  ]
}

Every user starts with an implicit policy that allows it to create institutions and be its admin:

{
  "statement": [
    {
      "action": "*",
      "delegable": true,
      "resource": [
        "institution",
        "device",
        "site"
      ],
      "condition": {"is_owner" : true}
    }
  ]
}

A superadmin would have then a policy that allows him to do anything to any resource:

{
  "statement": [
    {
      "action": "*",
      "delegable": true,
      "resource": "*"
    }
  ]
}

GET /encounters/schema[.format] Encounters schema

{
  $schema: "http://json-schema.org/draft-04/schema#",
  type: "object",
  title: "en-US",
  properties: {
    institution: {
      type: "object",
      title: "Institution",
      properties: {
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        name: {
          title: "Name",
          type: "string",
          searchable: false
        }
      }
    },
    site: {
      type: "object",
      title: "Site",
      properties: {
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        name: {
          title: "Name",
          type: "string",
          searchable: false
        },
        path: {
          title: "Path",
          type: "string",
          searchable: true
        }
      }
    },
    patient: {
      type: "object",
      title: "Patient",
      properties: {
        id: {
          title: "Id",
          type: "string",
          searchable: false
        },
        name: {
          title: "Name",
          type: "string",
          searchable: false
        },
        dob: {
          title: "Dob",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: false
        },
        gender: {
          title: "Gender",
          type: "string",
          enum: [
            "male",
            "female",
            "other"
          ],
          values: {
            male: {
              name: "Male"
            },
            female: {
              name: "Female"
            },
            other: {
              name: "Other"
            }
          },
          searchable: true
        },
        email: {
          title: "Email",
          type: "string",
          searchable: false
        },
        phone: {
          title: "Phone",
          type: "string",
          searchable: false
        }
      }
    },
    encounter: {
      type: "object",
      title: "Encounter",
      properties: {
        id: {
          title: "Id",
          type: "string",
          searchable: false
        },
        uuid: {
          title: "Uuid",
          type: "string",
          searchable: true
        },
        patient_age: {
          title: "Patient Age",
          type: "object",
          class: "duration",
          properties: {
            milliseconds: {
              type: "integer",
              title: "Patient Age milliseconds"
            },
            seconds: {
              type: "integer",
              title: "Patient Age seconds"
            },
            minutes: {
              type: "integer",
              title: "Patient Age minutes"
            },
            hours: {
              type: "integer",
              title: "Patient Age hours"
            },
            days: {
              type: "integer",
              title: "Patient Age days"
            },
            months: {
              type: "integer",
              title: "Patient Age months"
            },
            years: {
              type: "integer",
              title: "Patient Age years"
            }
          },
          searchable: true
        },
        start_time: {
          title: "Start Time",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: true
        },
        end_time: {
          title: "End Time",
          type: "string",
          format: "date-time",
          resolution: "second",
          searchable: true
        },
        observations: {
          title: "Observations",
          type: "string",
          searchable: false
        },
        diagnosis: {
          title: "Diagnosis",
          type: "array",
          items: {
            type: "object",
            properties: {
              name: {
                title: "Name",
                type: "string",
                searchable: true
              },
              condition: {
                title: "Condition",
                type: "string",
                enum: [
                  "atsc",
                  "cd4_count",
                  "ct",
                  "emb",
                  "hiv",
                  "hiv_1_m_n",
                  "hiv_1_o",
                  "hiv_2",
                  "inh",
                  "lvx",
                  "malaria_pf",
                  "malaria_pv",
                  "mtb",
                  "ng",
                  "pas",
                  "pza",
                  "rif",
                  "stm",
                  "tch"
                ],
                searchable: true
              },
              result: {
                title: "Result",
                type: "string",
                enum: [
                  "positive",
                  "negative",
                  "indeterminate"
                ],
                values: {
                  positive: {
                    name: "Positive",
                    kind: "positive"
                  },
                  negative: {
                    name: "Negative",
                    kind: "negative"
                  },
                  indeterminate: {
                    name: "Indeterminate"
                  }
                },
                searchable: true
              },
              quantitative_result: {
                title: "Quantitative Result",
                type: "string",
                searchable: true
              }
            }
          },
          searchable: true
        }
      }
    }
  }
}

Core Fields

Added fields for the reference implementation

  • sample.uid - In order to disambiguate the sample Id, manifests should ensure that the value in this field uniquely identifies each sample institution-wise. Two tests are considered to have the same sample if and only if they belong to the same institution and have the same sample.uid value. A possible sample uid for an test is a composition of the original sample.id, the month of the test’s start_time (to handle repetitions over time) and the identifier of the laboratory where the test was run (to handle repetitions across labs). This sample.uid is currently provided by the manifest.
  • location.parents - The natural earth geo id of all the the device location’s parents.
  • location.admin_levels - The natural earth parents geo ids tagged by administrative level.
  • location.lat - The latitude of the center of the geo id location.
  • location.lng - The longitude of the center of the geo id location.

POST /devices/[device_uuid]/messages Submit messages

Authentication

The reference implementation allows two ways of authentication:

  • Authentication Token - By passing the parameter authentication_token into the query string, resulting in:

    cdp2.instedd.org/devices/e9KehDBFrN1N/messages?authentication_token=ZF25WdAMu3XA

  • Basic auth - leaving the username as just a space and with the authentication token as password:

    ' :ZF25WdAMu3XA@cdp2.instedd.org/devices/e9KehDBFrN1N/messages'

API Location

API Location

The reference implementation supports the full CDX API in the api endpoint. This means that every endpoint described here will be found in:

<host>/api/<endpoint>

GET /sites[.format] Query Sites

Returns a list of Laboratories

Format

The query can be answered in CSV, XML, and JSON formats.

The CSV format don’t include the total count.

The default response format is JSON.

Filters

The institution Id can be specified, filtering the sites list.

  • institution_uuid - filter sites by institution uuid.

/sites?institution_uuid=2

Response

Status: 200 OK

##JSON

The response will include a total_count and a list of sites

{
  "total_count" : 2,
  "sites" : [
    {
      "uuid" : 1,
      "name" : "First Lab",
      "location" : "ne:CHE_3424",
      "parent_uuid": null,
      "institution_uuid": 2
    },
    {
      "uuid" : 2,
      "name" : "Second Lab",
      "location" : "ne:IND_2428",
      "parent_uuid": 1,
      "institution_uuid": 2
    }
  ]
}

CSV

uuid,name,location,parent_uuid,institution_uuid
1,First Lab,ne:CHE_3424,,2
2,Second Lab,ne:IND_2428,1,2

For error responses, see the response status codes documentation.

GET /institutions[.format] Query Institutions

Returns a list of Laboratories

Format

The query can be answered in CSV, XML, and JSON formats.

The CSV format don’t include the total count.

The default response format is JSON.

Filters

No filter can be specified

Response

Status: 200 OK

##JSON

The response will include a total_count and a list of institutions

{
  "total_count" : 2,
  "institutions" : [
    {
      "uuid" : 1,
      "name" : "First Institution"
    },
    {
      "id" : 2,
      "name" : "Second Institution"
    }
  ]
}

CSV

id,name
1,First Institution
2,Second Institution

For error responses, see the response status codes documentation.

GET [POST] /tests[.format] Query Tests

Returns a list of Tests. All the filters can be specified in the POST request body or in the GET query string.

Format

The query can be answered in CSV, XML, and JSON formats.

The CSV format doesn’t include the total count.

The default response format is JSON.

Filters

Filter parameters allow querying a subset of the available tests.

The data returned will be sorted by default by the test creation date.

Date filters

All the possible dates can be filtered by since and until as in test.end_time.since and test.end_time.until, but as the start_time is the default query, it has a shortened version by only typing since and until The available dates are:

  • test.start_time
  • test.end_time
  • test.reported_time
  • test.updated_time
  • encounter.start_time
  • encounter.end_time

When querying from the query string the + sign must be escaped as %2B. For instance: 2014-08-01T18:10:36+07:00, will be: 2014-08-01T18:10:36%2B07:00

Example

  • since: will retrieve tests started after a specific date time.

/tests?since=2014-04-10T15:22:12+00:00

  • test.end_time.until will retrieve tests ended before a specific date time. Useful to define a time window in combination with “since”.

/tests?test.end_time.until=2014-04-10T15:22:12-0300

Location filters

  • location - filter tests by geo location id

/tests?location=ne:CHE_3424

Institution filters

  • institution.uuid - filter tests by institution UUID

/tests?institution.uuid=1

Device filters

  • device.uuid - filter tests by device UUID

/tests?device.uuid=9d68e8fd-3ebe-a163-2ad6-7a675dac5dde

  • device.model - filter tests by device model name

/tests?device.model=genexpert

  • device.serial_number - filter tests by device serial number

/tests?device.serial_number=A-1234567890

Site filters

  • site.uuid - filter tests by site UUID

/tests?site.uuid=1

  • test.site_user - filter tests for the user that executed the test.

/tests?system_user=jdoe

Demographic filters

  • patient.gender - filter tests by the patient gender

/tests?patient.gender=male

  • encounter.patient_age - filter tests for people by the range specified. This is the age at the moment of the encounter.

/tests?encounter.patient_age=50yo..60yo

Test filters

  • test.assays.result - filter by the outcome of any of the assays: positive / negative / indeterminate / n/a.

/tests?test.assays.result=positive

  • test.assays.condition - filter tests for the condition name of any of the assays. The possible values will be the combination of all the conditions of all the manifests in the system.

/tests?test.assays.condition=mtb

  • test.assays.name - filter tests for the assays name

/tests?test.assays.name=mtb

  • test.name - filter tests for a particular test name

/tests?test.name=MTBDRplus

  • test.uuid - retrieves the test with a particular UUID

/tests?uuid=c4c52784-bfd5-717d-7a91-614acd972d5e

  • test.error_code - filter tests for a particular error code.

/tests?test.error_code=1

  • test.type - filter tests for the type of the test: qc / specimen.

/tests?test.type=qc

  • test.status - filter tests for the status of the test: invalid / error / no_result / success / in_progress.

/tests?test.status=success

Encounter filters

  • encounter.uuid - filter tests by the encounter UUID

/tests?encounter.uuid=c4c52784-bfd5-717d-7a91-614acd972d5e

Sample filters

  • sample.uuid - filter tests by sample id

/tests?sample.uuid=475

Multiple Values

With the exception of since, until, min_age and max_age, all the fields can accept multiple values using a comma as a separator:

/tests?test.error_code=A01,A02

/tests?patient.gender=male,female

Unknown values

If a field value is not specified, the keyword used to represent it in filters is “null”.

If the value is specified, but impossible to determine at the moment of the test, the keyword used to represent it is “unknown”.

/tests?patient.gender=male,unknown,null

The response must include all the unknown and null values.

If you want all the results where the gender is a known value, you can ignore the null using:

/tests?patient.gender=not(null)

When grouping, a bucket must be included for all elements that fall into the unknown or null buckets.

/tests?group_by=patient.gender,test.assays.result

Assuming gender values of male, female, unknown and null, and result values of positive and negative, an expected result could be:

  tests: [
    {
      patient.gender: "male",
      test.assays.result: "positive",
      count: 99
    },
    {
      patient.gender: "male",
      test.assays.result: "negative",
      count: 86
    },
    {
      patient.gender: "female",
      test.assays.result: "positive",
      count: 120
    },
    {
      patient.gender: "female",
      test.assays.result: "negative",
      count: 14
    }
  ],
  total_count: 319
}

Sorting

  • order_by - orders by a given field.
  • Ascending by default. Append a - to sort descending.
  • Comma separated.

Examples

  • order_by=age - orders ascending by age.
  • order_by=age,gender - orders ascending by age and gender.
  • order_by=-age - orders descending by age.
  • order_by=age,-gender - orders ascending by age and descending by gender.

Pagination

Every request will contain the total amount of records that matched the filters, but will only retrieve a portion of them in each request.

  • page_size Specifies the number of element returned in the request.

page_size=20

  • offset - Specifies the starting point of the batch in terms of number of elements that have already been retrieved.

page_size=20&offset=450 will bring the elements 451 to 470.

  • to retrieve only a count of the values that will match certain filters, a page size of zero can be specified. The result will be a record with no tests and the desired count:

{ "total_count" : 1234, "tests" :[] }

The default page size is 50.

Data Aggregation

There are two ways to accomplish data aggregation:

  • through the “group_by” option in the query string
  • sending a JSON in the request body with the “group_by” key in it

Query Parameter

In the query parameter the options are limited to indicate the fields to group by.

/tests?group_by=site,gender,result

The possible groupings are:

  • location
  • institution
  • device
  • site
  • gender
  • condition
  • result
  • error_code
  • system_user
  • test_type
  • assay_name
  • year() | month() | week() | day() - groups the given date field by the time interval specified.

/tests?group_by=year(created_at)

JSON in request body

The JSON allows more complex aggregations, such as:

  • age - groups and filters by age ranges. The tests are skipped if they are outside those ranges.

{ "group_by" : [ { "age" : [ [0, 10], [11, 20] ] }, ... ] }

  • admin_level - groups by administrative level, up to the third level in this case, which is a state level.

{ "group_by" : [ { "admin_level" : 4 }, ... ] }

  • All the groupigs of the query string are also available when querying through the request body.

{ "group_by" : [ "age", "year(created_at)", { "admin_level" : 4 }, ... ] }

Response

Status: 200 OK

##JSON

Without Grouping

Returns an array of tests without any PII and the total count of elements that matched the filter.

  "tests": [
    {
      "test": {
        "start_time": "2015-08-18T00:00:00.000Z",
        "site_user": "Cornelia Kazmaier",
        "name": "MTBDRplus",
        "assays": [
          {
            "condition": "mtb",
            "name": "mtb",
            "result": "positive"
          },
          {
            "condition": "rif",
            "name": "rif",
            "result": "positive"
          },
          {
            "condition": "inh",
            "name": "inh",
            "result": "positive"
          }
        ],
        "type": "specimen",
        "reported_time": "2016-02-16T19:59:09Z",
        "updated_time": "2016-02-16T19:59:09Z",
        "uuid": "7d321113-2b39-3100-08f4-776e2c8cee8b",
        "custom_fields": {
          "clia_waived_test": "n.a.",
          "revision": "038",
          "control": "n.a.",
          "ig_type": "ig_g",
          "control_strip": "patient",
          "bands": "TUB(198,6);rpoB(383,4);rpoBWT1(737,1);rpoBWT2(1341,0);rpoBWT3(1069,0);rpoBWT4(792,4);rpoBWT5(1154,0);rpoBWT6(1224,0);rpoBWT8(818,9);rpoBMUT2B(79,0);katG(82,7);katGMUT1(0,0);inhA(197,2);inhAWT1(524,6);inhAWT2(388,6)"
        }
      },
      "device": {
        "uuid": "K-rz4b3I2X_d",
        "model": "Genoscan",
        "serial_number": "8938490238432",
        "name": "Than Hoa"
      },
      "location": {
        "id": "ne:VNM_456",
        "parents": [
          "ne:VNM",
          "ne:VNM_456"
        ],
        "admin_levels": {
          "admin_level_0": "ne:VNM",
          "admin_level_1": "ne:VNM_456"
        },
        "lat": 19.9556168685236,
        "lng": 105.513240945362
      },
      "institution": {
        "uuid": "417d35a8-ff37-3cd8-dc69-e35e9edd5ce8",
        "name": "CDC"
      },
      "site": {
        "uuid": "595ac805-ff5c-2f7e-e814-f60abfdcce56",
        "path": [
          "595ac805-ff5c-2f7e-e814-f60abfdcce56"
        ],
        "name": "Thanh Hoa Provincial Hospital"
      },
      "sample": {
        "id": "3",
        "custom_fields": {},
        "uuid": [
          "202b8e68-c28a-3550-3c80-392267be4fdc"
        ],
        "entity_id": [
          "3"
        ]
      },
      "encounter": {
        "patient_age": {
          "years": 35,
          "in_millis": 1103760000000
        },
        "start_time": "2015-08-18T00:00:00Z",
        "end_time": "2016-02-16T19:59:09Z",
        "custom_fields": {},
        "uuid": "4f0d2e6f-1162-a853-5685-85da117c6e35"
      },
      "patient": {
        "gender": "female",
        "custom_fields": {},
        "uuid": "5c4f8ad5-59f1-e3d8-6aec-a9a3cbc1b856"
      }
    },
    {
      "test": {
        "start_time": "2015-10-18T00:00:00.000Z",
        "site_user": "Cornelia Kazmaier",
        "name": "MTBDRplus",
        "assays": [
          {
            "condition": "mtb",
            "name": "mtb",
            "result": "positive"
          },
          {
            "condition": "rif",
            "name": "rif",
            "result": "negative"
          },
          {
            "condition": "inh",
            "name": "inh",
            "result": "negative"
          }
        ],
        "type": "specimen",
        "reported_time": "2016-02-16T19:59:09Z",
        "updated_time": "2016-02-16T19:59:09Z",
        "uuid": "e55ef842-1c70-2d29-8a34-6186fba4d196",
        "custom_fields": {
          "clia_waived_test": "n.a.",
          "revision": "038",
          "control": "n.a.",
          "ig_type": "ig_g",
          "control_strip": "patient",
          "bands": "TUB(194,9);rpoB(290,9);rpoBWT1(692,1);rpoBWT2(59,2);rpoBWT3(19,4);rpoBWT4(680,1);rpoBWT5(970,7);rpoBWT6(779,7);rpoBWT7(539,2);rpoBWT8(627,8);katG(206,5);katGWT(469,2);inhA(233,3);inhAWT1(528,9);inhAWT2(385,6)"
        }
      },
      "device": {
        "uuid": "K-rz4b3I2X_d",
        "model": "Genoscan",
        "serial_number": "8938490238432",
        "name": "Than Hoa"
      },
      "location": {
        "id": "ne:VNM_456",
        "parents": [
          "ne:VNM",
          "ne:VNM_456"
        ],
        "admin_levels": {
          "admin_level_0": "ne:VNM",
          "admin_level_1": "ne:VNM_456"
        },
        "lat": 19.9556168685236,
        "lng": 105.513240945362
      },
      "institution": {
        "uuid": "417d35a8-ff37-3cd8-dc69-e35e9edd5ce8",
        "name": "CDC"
      },
      "site": {
        "uuid": "595ac805-ff5c-2f7e-e814-f60abfdcce56",
        "path": [
          "595ac805-ff5c-2f7e-e814-f60abfdcce56"
        ],
        "name": "Thanh Hoa Provincial Hospital"
      },
      "sample": {
        "id": "5",
        "custom_fields": {},
        "uuid": [
          "3b2e77f1-b717-395d-1ded-25d379c3421f"
        ],
        "entity_id": [
          "5"
        ]
      },
      "encounter": {
          "patient_age": {
            "years": 41,
            "in_millis": 1292976000000
          },
        "start_time": "2015-06-18T00:00:00Z",
        "end_time": "2016-02-16T19:59:09Z",
        "custom_fields": {},
        "uuid": "4d8fef0c-80f3-f525-32c6-34b5eab7a9d5"
      },
      "patient": {
        "gender": "male",
        "custom_fields": {},
        "uuid": "51058040-1263-fe1b-406a-5f35cbae4364"
      }
    }
  ],
  "total_count" : 2
}

With Grouping

Returns the quantity of tests matching each combination of aggregated fields and the total count of elements that matched the filter.

test.assays.condition=mtb&group_by=test.assays.result,patient.gender

  "tests": [
    {
      "patient.gender": "male",
      "test.assays.result": "positive",
      "count": 44
    },
    {
      "patient.gender": "male",
      "test.assays.result": "negative",
      "count": 18
    },
    {
      "patient.gender": "female",
      "test.assays.result": "positive",
      "count": 42
    },
    {
      "patient.gender": "female",
      "test.assays.result": "negative",
      "count": 3
    }
  ],
  "total_count": 107
}

CSV

Without Grouping

Test id,Test uuid,Test start time,Test end time,Test reported time,Test updated time,Test error code,Test error description,Test site user,Test name,Test status,Test type,Sample id,Sample type,Sample collection date,Device uuid,Device name,Device model,Device serial number,Institution uuid,Institution name,Site uuid,Site name,Patient gender,Location id,Location lat,Location lng,Encounter id,Encounter uuid,Encounter patient age,Encounter start time,Encounter end time,Location admin levels admin level 0,Location admin levels admin level 1,Test assays name 1,Test assays condition 1,Test assays result 1,Test assays quantitative result 1,Test assays name 2,Test assays condition 2,Test assays result 2,Test assays quantitative result 2,Test assays name 3,Test assays condition 3,Test assays result 3,Test assays quantitative result 3,Sample uuid 1,Encounter diagnosis name 1,Encounter diagnosis condition 1,Encounter diagnosis result 1,Encounter diagnosis quantitative result 1,Encounter diagnosis name 2,Encounter diagnosis condition 2,Encounter diagnosis result 2,Encounter diagnosis quantitative result 2,Encounter diagnosis name 3,Encounter diagnosis condition 3,Encounter diagnosis result 3,Encounter diagnosis quantitative result 3,Encounter diagnosis name 4,Encounter diagnosis condition 4,Encounter diagnosis result 4,Encounter diagnosis quantitative result 4,Test clia waived test,Test control,Test ig type,Test control strip,Test bands,Test revision,Patient pregnancy status 3238-ABC-Positivo-1750-,a71952b3-a1bc-799a-ee78-f4bf1add5ca8,2015-02-10T18:10:28.000Z,2015-02-21T00:00:00.000Z,2016-02-26T18:22:35Z,2016-02-26T18:22:35Z,,,,BACTEC MGIT / tubo 7 mL,success,specimen,3238-ABC,,,dQoLCXoNkF,Epicenter_D,Epicenter M.G.I.T. Spanish,44454,1f5b45d5-81d2-e63a-35ce-378a59acdcbb,WHO Institution,1b998740-4a6d-24c5-fedd-7b109d7b2628,Quilmes Lab,male,ne:ARG_1295,-37.1001929664999,-60.1138534839139,,92e6400b-2794-e165-46a0-c0a0dcac65cd,,2015-02-10T18:10:28Z,2016-02-26T18:22:35Z,ne:ARG,ne:ARG_1295,mycobacterium_tuberculosis,mtb,positive,,,,,,,,,,371c2bce-1b49-6d59-cc68-b27a9efc4315,mycobacterium_tuberculosis,mtb,positive,,,,,,,,,,,,,,,,,,,, AAA-133-Positivo-1967-,81326729-8a3c-5236-649f-625d9bd46cf3,2015-03-03T19:27:36.000Z,2015-03-13T00:00:00.000Z,2016-02-26T18:22:35Z,2016-02-26T18:22:35Z,,,,BACTEC MGIT / tubo 7 mL,success,specimen,AAA-133,,,dQoLCXoNkF,Epicenter_D,Epicenter M.G.I.T. Spanish,44454,1f5b45d5-81d2-e63a-35ce-378a59acdcbb,WHO Institution,1b998740-4a6d-24c5-fedd-7b109d7b2628,Quilmes Lab,,ne:ARG_1295,-37.1001929664999,-60.1138534839139,,991517fd-c189-1e45-d51e-17292107cfe0,,2015-03-03T19:27:36Z,2016-02-26T18:22:35Z,ne:ARG,ne:ARG_1295,mycobacterium_tuberculosis,mtb,positive,,,,,,,,,,402c4c6f-49b7-cd27-c018-3333f9864971,mycobacterium_tuberculosis,mtb,positive,,,,,,,,,,,,,,,,,,,,

With Grouping

positive,male,44
negative,male,18
positive,female,42
negative,female,3

For error responses, see the response status codes documentation.

GET [POST] /encounters[.format] Query Encounters

Returns a list of Encounters. All the filters can be specified in the POST request body or in the GET query string.

Format

The query can be answered in CSV, XML, and JSON formats.

The CSV format doesn’t include the total count.

The default response format is JSON.

Filters

Filter parameters allow querying a subset of the available encounters.

The data returned will be sorted by default by the encounter creation date.

Date filters

All the possible dates can be filtered by since and until as in encounter.end_time.since and encounter.end_time.until The available dates are:

  • encounter.start_time
  • encounter.end_time

When querying from the query string the + sign must be escaped as %2B. For instance: 2014-08-01T18:10:36+07:00, will be: 2014-08-01T18:10:36%2B07:00

Example

  • since: will retrieve tests started after a specific date time.

/encounters?encounter.start_time.since=2014-04-10T15:22:12+00:00

  • encounter.start_time.until will retrieve tests ended before a specific date time. Useful to define a time window in combination with “since”.

/encounters?encounter.start_time.until=2014-04-10T15:22:12-0300

Institution filters

  • institution.uuid - filter tests by institution UUID

/encounters?institution.uuid=1

Site filters

  • site.uuid - filter tests by site UUID

/encounters?site.uuid=1

Demographic filters

  • patient.gender - filter tests by the patient gender

/encounters?patient.gender=male

  • encounter.patient_age - filter tests for people by the range specified. This is the age at the moment of the encounter.

/encounters?encounter.patient_age=50yo..60yo

Encounter filters

  • encounter.uuid - filter tests by the encounter UUID

/encounters?encounter.uuid=c4c52784-bfd5-717d-7a91-614acd972d5e

Response

Status: 200 OK

##JSON

Without Grouping

Returns an array of tests without any PII and the total count of elements that matched the filter. /encounters?encounter.diagnosis.condition=mtb

  encounters: [
  {
    institution: {
      uuid: "1f5b45d5-81d2-e63a-35ce-378a59acdcbb",
      name: "Institution"
    },
    site: {
      uuid: "16e2d7e5-0783-63c2-05da-9ee21ecf6977",
      path: [
      "16e2d7e5-0783-63c2-05da-9ee21ecf6977"
      ],
      name: "Site"
    },
    encounter: {
      diagnosis: [
        {
          condition: "mtb",
          name: "mtb",
          result: "positive"
        },
        {
          condition: "rif",
          name: "rif",
          result: "positive"
        },
        {
          condition: "inh",
          name: "inh",
          result: "positive"
        },
        {
          condition: "hiv_1_m_n",
          name: "HIV-1 M/N",
          result: "positive",
          quantitative_result: 350
        },
        {
          condition: "hiv_1_o",
          name: "HIV-1 O",
          result: "positive",
          quantitative_result: 500
        },
        {
          condition: "hiv_2",
          name: "HIV-2",
          result: "negative"
        }
      ],
      start_time: "2016-03-21T14:28:24Z",
      end_time: "2016-03-21T14:28:24Z",
      custom_fields: { },
      uuid: "39c4c9d6-2b19-bd89-0749-94fe7fd9ac32",
      user_email: null
    },
    patient: {
      gender: "male",
      custom_fields: { },
      uuid: "e64d0d16-5fd1-e12a-eb8b-31b9e68aa623"
    }
  }
  ],
  total_count: 1
}

With Grouping

Returns the quantity of tests matching each combination of aggregated fields and the total count of elements that matched the filter.

/encounters?encounter.diagnosis.condition=mtb&group_by=patient.gender

  encounters: [
    {
      patient.gender: "male",
      count: 7
    },
    {
      patient.gender: "female",
      count: 5
    }
  ],
  total_count: 12
}

CSV

Without Grouping

Institution uuid,Institution name,Site uuid,Site name,Patient gender,Encounter id,Encounter uuid,Encounter user email,Encounter patient age,Encounter start time,Encounter end time,Encounter diagnosis name 1,Encounter diagnosis condition 1,Encounter diagnosis result 1,Encounter diagnosis quantitative result 1,Encounter diagnosis name 2,Encounter diagnosis condition 2,Encounter diagnosis result 2,Encounter diagnosis quantitative result 2,Encounter diagnosis name 3,Encounter diagnosis condition 3,Encounter diagnosis result 3,Encounter diagnosis quantitative result 3,Encounter diagnosis name 4,Encounter diagnosis condition 4,Encounter diagnosis result 4,Encounter diagnosis quantitative result 4,Encounter diagnosis name 5,Encounter diagnosis condition 5,Encounter diagnosis result 5,Encounter diagnosis quantitative result 5,Encounter diagnosis name 6,Encounter diagnosis condition 6,Encounter diagnosis result 6,Encounter diagnosis quantitative result 6 1f5b45d5-81d2-e63a-35ce-378a59acdcbb,WHO Institution,16e2d7e5-0783-63c2-05da-9ee21ecf6977,Buenos Aires Lab,male,,39c4c9d6-2b19-bd89-0749-94fe7fd9ac32,,,2016-03-21T14:28:24Z,2016-03-21T14:28:24Z,mtb,mtb,positive,,rif,rif,positive,,inh,inh,positive,,HIV-1 M/N,hiv_1_m_n,positive,350,HIV-1 O,hiv_1_o,positive,500,HIV-2,hiv_2,negative,

With Grouping

positive,male,44
negative,male,18
positive,female,42
negative,female,3

For error responses, see the response status codes documentation.

Push Notifications

Allows an application to subscribe for new reports of a particular disease.

Setup

The necesary steps for setting this up are:

  • Create a tests query specifying which laboratory and condition you want to monitor.
  • Register a subscriber endpoint to be notified when the tests of the query changes.

The query will be executed in regular intervals depending on the installation. If the result of the query changes, the push notification will be triggered for every new event returned by the query.

The event structure will match the schema.

Parameters

  • Name - to identify the subscriber
  • URL - the endpoint that will be triggered with a post for every new tests
  • User - Optional. Basic auth. The user that must be used to login in the URL provided
  • Password - Optional. Basic auth. The password that must be used to login in the URL provided

GET /tests/[test_uuid]/pii Retrieve PII

Retrieves the private patient information for a given test.

Request Parameters

/tests/[test_uuid]/pii

  • The path must include a valid test_uuid.
  • test_uuid - UUID of the desired test.

Example

/tests/c4c52784-bfd5-717d-7a91-614acd972d5d/pii

Response

Returns the private patient information.

Status: 200 OK

For error responses, see the response status codes documentation.

GET /tests/[test_uuid]/custom_fields Retrieve Custom Fields

Retrieves the not indexed user defined custom fields for a given test.

Request Parameters

/tests/[test_uuid]/custom_fields

  • The path must include a valid test_uuid.
  • test_uuid - UUID of the desired test.

Example

/tests/9aa43890-ed75-11e3-8da1-1231381c1cdd/custom_fields

Response

Returns the custom fields.

Status: 200 OK

For error responses, see the response status codes documentation.

Manifest

Every device manufacturer should provide a manifest specifying the translation from the reporting format for each device to the API standard fields.

The manifest is a json that must include two fields: metadata and field_mapping. And additionally may contain custom_fields.

Metadata

The metadata header must include the version of the manifest, the version of the API, a list of the models that it applies to, the source data type (json, xml or csv), and the list of conditions that the device reports.

  "metadata" : {
    "version" : "1.2.1",
    "device_models" : ["GX4001", "GX4002"],
    "conditions" : ["mtb", "rif", "inh"],
    "source": {"type" : "json"}
  }
}

Version

The current version of the manifest is “1.2.1”.

Source type

Current source types are json, xml, csv, and headless_csv for the cases when the csv contains no header data. For custom CSVs an additional separator can be specified. If no separator is specified, the default is “,”.

In some cases the CSV can include some header that is necessary to skip. The number of lines to skip can be specified using skip_lines_at_top.

Example

"source": { "type" : "csv", "separator" : ";", "skip_lines_at_top" : 1 }

Conditions

The manifest conditions must be encoded using snake notation: underscore_with_dashes

Field Mappings

The field mapping is an object that describes the translation from a reported value to a core field. Each key of the object is a reference to a field, and each value is a source object that represents the required transformations to obtain its value.

A typical manifest may look like this:

  "metadata" : {
    ...
  }
  "custom_fields" : {
    "patient.name" : { "pii" : true },
    "test.temperature": {},
    ...
  },
  "field_mapping" : {
    "patient.name" : { ... },
    "sample.id" : { ... },
    "test.name" : { ... },
    "test.temperature": { ... },
    ...
  }
}

This source object may contain a number of pre-defined functions in order to retrieve and transform the data provided by the device into something that matches the required format. These are:

  • lookup - expects the source path of the reported field, using json path if the source_data_type is json: for multiple elements the [*] notation must be used; for each nesting level, the depth is specified using a period (.). For instance, the element ‘test_result’ has a field named ‘conditions’ that contains an array, and for every element of this array, the element ‘name’ is taken. { "lookup" : "test_result.conditions[*].name" }

    If the source is an xml, the XPath notation is used. In the case described above, the result would be: { "lookup" : "test_result/conditions/name/text()" }

    If the source is a csv, then the path should be the column name, or if it’s a headless_csv, it should be only the column number, 0 based. { "lookup" : "0" }

  • case - expects the element to transform as the first parameter and an array of transformations as the second one. If a match applies, the result will be the output specified. Wildcards are specified as ‘*’. { "case" : [ { "lookup" : "conditions[*].condition" }, [ { "when" : "*MTB*", "then" : "MTB" }, { "when" : "*FLU*", "then" : "H1N1" }, { "when" : "*FLUA*", "then" : "A1N1" } ] ] }

  • lowercase - Converts the parsed field value into a lowercase string { "lowercase" : { "lookup" : "last_name" } }

  • concat - expects two or more parameters and returns a string containing all the parameters joined { "concat" : [ { "lookup" : "patient_information.last_name" }, ", ", { "lookup" : "patient_information.first_name" } ] }

  • strip - removes trailing spaces from the given parameter { "strip" : { "lookup" : "patient_information.last_name" } }

  • convert_time - it will convert a numeric time from a given time unit to another one specified. The source time unit is expected first. Possible units are: years, months, days, hours, minutes, seconds, milliseconds. When reducing the unit precision, no rounding will be made. When converting from days to years, all years will be considered as 365.25 days long. When converting from days to months, all months will be considered as 30 days long. { "convert_time" : [ { "lookup" : "patient_information.age_in_years" }, "years", "days" ] }

  • beginning_of [year, month] - Useful for date related PII, it converts a date into a less specific time span. Expects the value as the first parameter, and the time unit as the second one. { "beginning_of" : [ { "lookup" : "patient_information.age" }, "month" ] }

  • milliseconds_between / hours_between / minutes_between / seconds_between / years_between / months_between / days_between - measures the number of milliseconds, hours, years, etc. between two given dates. Useful to compute ages or test durations. It will always round to the smallest value. { "years_between" : [ { "lookup" : "patient_information.birth_date" }, { "lookup" : "test_information.run_at" } ] }

  • parse_date - parse the field value using the specified format for further processing. Eg: ‘yyyy-mm-dd hh:mm:ss’. All dates must be stored using ISO 8601 format. If the device reports a date using another format, it must be parsed. If the date will be used in another function that expects a date, it must be parsed. { "parse_date" : [ { "lookup" : "patient_information.birth_date" }, "%d-%m-%Y %I:%M:%S %p" ] }

  • duration - It expects a duration object with years, months, days, seconds, and milliseconds. There is no required component, but at least one must be present. { "duration" : { "years" : { "years_between" : [ {"parse_date" : [ {"lookup" : "Birthday"}, "%d.%m.%Y" ]}, {"parse_date" : [ {"lookup" : "DateOfAnalysis"}, "%d.%m.%Y" ]} ] } } }

  • clusterise - given an array of steps and a number, it returns the bucket that contains it. The lower boundary will always be zero and the upper bucket will always contain all the values that are greater or equal the last step value. The step value will always be the greater value of the generated cluster. In the following example, the buckets created will be: “0-5”, “6-15”, “16-45”, “46+” { "clusterise" : [ { "lookup" : "patient_information.age_in_years" }, [ 5, 15, 45 ] ] }

  • substring - it extracts the string in the specified positions. Negative values are counted from the end of the string being -1 the last element. The given example will return the original string untouched. { "substring" : [ { "lookup" : "test_information.assay_code" }, 0, -1 ] }

  • equals - Used inside an if expression. It checks if its two arguments are equal. Returns true or false and allows an if to be executed in return. The order of the arguments is not important. Any of the arguments can be any valid expression or a string. {"equals" : ["A", {"lookup" : "Condition"}]}

  • if - Used with a conditional expression. It receives an array containing a conditional in the first position, and true and false expressions in the second and third positions of the array { "if" : [ { "equals" : [ { "lookup" : "Condition" }, "A" ] }, { "lookup" : "A Column" }, { "lookup" : "B Column" } ] }

  • script - Used when the needed calculation is too complex or you need to access basic CDX elements like the device, its laboratory, institution or location. The only value will be a Javascript that in the last line returns the result to be assigned for that field. The message sent by the device would be accessible through a message object, and its properties would be accessible as a regular Javascript object. { "script" : "message['Condition']" } Or as a simple property { "script": "message.first_name + ' ' + message.last_name" } If the message sent is a csv, then it would be accessed by column header: { "script" : "message['Result']" } If it doesn’t have header, it can be accessed by column number: { "script" : "message['5']" } If the message sent is an xml, then you will have xpath available: {"script": "message.xpath('Patient/@name').first().value"} To access basic CDX elements just reference them: { "script": "device.name + ', ' + device.uuid" } { "script": "location.name + ': ' + parseInt(location.lat) + ',' + parseInt(location.lng)" } { "script": "laboratory.id + '-' + new Date().getFullYear() + '-' + message['SampleId']" }

A sample field may look like this:

{ "test.patient_age" : { "if" : [ { "equals" : [ { "lookup" : "Birthday" }, "n.a." ] }, null, { "duration" : { "years" : { "years_between" : [ { "parse_date" : [ { "lookup" : "Birthday" }, "%d.%m.%Y" ] }, { "parse_date" : [ { "lookup" : "DateOfAnalysis" }, "%d.%m.%Y" ] } ] } } } ] } }

Custom Fields

If the device reports additional information that is necessary for further analysis, it should be included in the manifest definition. Additionally to the field mappings, the manifest may define custom_fields. This custom fields are to be used in the field_mapping section.

Each custom field may contain:

  • pii - boolean. Indicates if the field must be considered PII or not.

A sample custom field would be:

{"patient.telephone_number" : { "pii" : true } }

Personally Identifiable Information

The device can report information that allows the test to be linked with the patient. This information must be kept encrypted and must not be indexed. Therefore, all PII must have “pii” field set to true.

Implementation Specific Field Mapping Metadata

Manifests support implementation specific metadata at the field level. Such metadata is ignored by reference implementations, but could be of use for specific ones.

Implementation specific field mapping metadata can be included simply by adding a non-standard key-value pair to the root level of a field mapping. Implementation specific keys MUST NOT override the standard field mapping elements listed above.

Implementation specific keys can be anything, but we SUGGEST to prefix them with -x–, which makes it easier to distinguish standard field mapping elements from ad hoc ones.

As an example, let’s say there’s a need to treat some fields as Maximum Security, which has certain implications for a particular implementation. An x-max_security field could be added to those fields as shown below:

{
  ...
  pii: false
  valid_values: { ... }
  x-max_security: true
  ...
}

Given this manifest, standard manifest processors MAY ignore x-max_security, but they WON’T fail because of it. It’s then up to each implementation to provide a specific (still compliant) processor that knows what to do when a mapping includes x-max_security.

It’s up to the reference implementations to define this attributes for the core fields, as the manifest will only allow to define them for the custom fields of each device model.

POST /devices/[device_uuid]/messages Submit messages

Allows devices to submit messages.

Request

  • The path must include a valid device UUID.
  • The body can’t be empty and must include a test.

/devices/[device_uuid]/messages

/devices/f862f658-ad89-4fcb-995b-7a4c50554ff6/messages

If the same test is sent more than once (with the same test_id), the result data gets updated and no duplicated record is created.

Response

If it succeeds, it returns the created test.

Status: 201 Created

For error responses, see the response status codes documentation.

GET /tests/schema[.format] Tests schema

Returns the schema of the Tests endpoint

Format

The query can be answered in XML and JSON formats.

The default response format is JSON, and is compliant with JSON Schema, with a few additions.

Input

The schema may vary depending on the following input:

  • assay_name
  • locale

The assay_name may specify different fields or different values for a condition field, for example.

The locale will determine the language of the labels, detailed in the values section of the enum fields.

Response

The response will include a list of the supported fields with the details of each one.

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "MTB_RIF.es_AR",
  "type": "object",
  "properties": {
    "result_group": {...},
    "patient_location": {...},
    "age": {...},
    ...
  },
  "required": ["result_group"]
}

Enum

For the Enum fields we added an extra values field that will include one of the following:

  • uri of the metadata
  • a metadata dictionary

Metadata Schema

When the value provides a uri, the endpoint will provide the values according to the following schema:

{
  “title”: “Results”,
  “additionalProperties”: {
    “title”: “Result”,
    “type”: “object”,
    “properties”: {
      “name”: { “type”: “string” },
      “kind”: { “type”: “string”, “enum”: [“positive”, “negative”, “error”] }
    },
    “required”: [“name”, “kind”]
  }
}

In the case of results, the values metadata will include a name or localized label, and a kind that will categorize the result positivity in 3 possible values: positive, negative, or error.

Other enumerations will include different properties, or the implementation may define “additionalProperties”: true to allow dynamic injection of properties.

{
  “title”: "Conditions",
  “additionalProperties”: {
    “title”: “Condition”,
    “type”: “object”,
    “properties”: {
      “name”: { “type”: “string” }
    },
    "additionalProperties" : true,
    “required”: [“name”]
  }
}

In any case, to obtain the schema of a given endpoint, the uri will be the same as the endpoint with an additional /schema[.format]

Example

"result_group": {
  "title": "Result"
  "description": "Grouped result",
  "type": "string",
  "enum": ["pos_with_RIF", "negative"],
  "values": "http//example.com/values"
}

In this case, the endpoint “http//example.com/values” will provide the possible values, and the endpoint “http//example.com/values/schema.json” will provide the schema of such values.

Metadata Dictionary

The “values” field will include the metadata inlined inside the definition.

"result_group": {
  "title": "Result"
  "description": "Grouped result",
  "type": "string",
  "enum": ["pos_with_RIF", "negative"],
  "values": {
    "pos_with_RIF": {"name": "MTB Detected", "kind": "positive"},
    "negative": {"name": "Negative", "kind": "negative"}
  }
}

The inlined values must follow the default values schema. If a different implementation is needed, it can only be provided with the external uri and schema.

Location

The location fields, such as the laboratory location or the patient location, will include an extra “locations” field. This field can contain a uri of the locations provider, or it can contain the inlined locations.

Locations Schema

{
  “title”: “Location hierarchies”,
  “additionalProperties”: {
    “title”: “Location hierarchy”,
    “type”: “object”,
    “properties”: {
      “name”: { “type”: “string” },
      “lat”: { “type”: “float” },
      “lng”: { “type”: “float” },
      “level”: { “type”: “integer” },
      “boundary”: { “type”: “uri” },
      “parent_id”: { “type”: “string” },
      “geoids”: {
        "additionalProperties": {
          "type": "string"
        }
      }
    },
    “required”: [“name”]
  }
}

Example

"patient_location": {
  "title": "Patient Location",
  "description": "Location of the patient",
  "type": "string",
  “locations”: “uri”
}

Locations Dictionary

Locations inlined inside the field definition. This will be feasible only for small number of locations, and should be avoided for a hierarchy with over a hundred nodes. The inlined locations must follow the default locations schema. If a different implementation is needed, it can only be provided with the external uri and schema.

Example

"patient_location": {
  "title": "Patient Location",
  "description": "Location of the patient",
  "type": "string",
  "locations": {
    “LOC001”: {
      name: "Argentina",
      lat: -34.6,
      lng: -58.38,
      level: 0,
      boundary: “http://example.com/boundaries.json#LOC001”,
      geoids: { iso_code: "ARG" }
    },
    “LOC002”: {
      name: "Buenos Aires",
      lat: -34.6,
      lng: -58.38,
      level: 1,
      parent_id: "LOC001",
      geoids: { fips_code: "1212" }
    },
    “LOC003”: {
      name: "Chaco",
      lat: -34.6,
      lng: -58.38,
      level: 1,
      parent_id: "LOC001"
    }
  }
}

Integer

The integer fields will follow the JSON Schema convention.

For convenience and simplicity, the implementations should avoid the use of “exclusiveMinimum” and “exclusiveMaximum” and asume the defaults.

Example

"age": {
  "title": "Age",
  "type": "integer",
  "minimum": 0,
  "maximum": 199
}

Date

The date field has some differences with the specifyed by JSON Schema:

  • The full ISO 8601 standard is adopted, not only a subset. This allows the usage of 2014-W34 to report a test that occured in that week if the client has no permission to see the exact date.
  • A “resolution” field is added, specifying the maximum level of granularity of the date-time value provided. Possible values are:
    • second
    • minute
    • hour
    • day
    • week
    • month
    • year

For resolution values of second, minute, and hour, a timezone must be included in the date-time value.

Example

"created_at": {
  "title": "Creation date",
  "type": "string",
  "format":"date-time",
  "resolution": "day"
}

Test

The Test resource contains the fields related to the test reported by the device. No personal identifiable information is contained in this resource.

{
  "sample" : {
    "id" : "",
    "uid" : "9d68e8fd-3ebe-a163-2ad6-7a675dac5dde",
    "uuid" : "",
    "type" : "",
    "collection_date" : "",
    "custom_fields" : {}
  },
  "test" : {
    "id" : "12345",
    "uuid" : "570254af-eb74-367b-3b0e-0b1f1029ba73",
    "start_time" : "2014-09-26T22:09:05Z",
    "reported_time" : "2014-09-26T22:09:05Z",
    "updated_time" : "2014-09-26T22:09:05Z",
    "error_code" : 0,
    "error_description" : null,
    "patient_age" : "24",
    "name" : "Mycobacterium Tuberculosis",
    "status" : "success",
    "assays: [
      {
        "name" : "MTB Scan",
        "condition" : "mtb",
        "result" : "negative",
        "quantitative_result" : 0
      }
    ]
    "type" : "qc",
    "custom_fields" : {}
  },
  "device" : {
    "uuid" : "e9KehDBFrN1N",
    "name" : "Test device 1",
    "lab_user" : "jdoe",
    "serial_number" : "1234567890-098765432",
    "custom_fields" : {}
  },
  "patient" : {
    "id" : "1234",
    "uuid" : "1234",
    "gender" : "male",
    "custom_fields" : {}
  },
  "encounter" : {
    "id" : "",
    "uuid" : "",
    "custom_fields" : {}
  }
  "institution" : {
    "id" : "",
    "name" : ""
  },
  "laboratory" : {
    "id" : "",
    "name" : ""
  },
  "location" : {
    "id" : "",
    "parents" : "",
    "admin_levels" : "",
    "lat" : "",
    "lng" : ""
  }
}`
  "start_time": "",
  "test_id": "",
  "uuid": "570254af-eb74-367b-3b0e-0b1f1029ba73",
  "device_uuid": "",
  "system_user": "jdoe",
  "error_code": null,
  "error_description": null,
  "laboratory_id": 11,
  "institution_id": 13,
  "location": {
      "admin_level_0": "0",
      "admin_level_1": "0000000US",
      "admin_level_2": "0400000US08",
      "admin_level_3": "0500000US08021"
  },
  "age": 20,
  "assay_name": "MTB Assay",
  "gender": "male",
  "ethnicity": null,
  "race": null,
  "race_ethnicity": null,
  "status": null,
  "results": [
      {
          "result": "positive_with_rif",
          "condition": "mtb",
          "custom_fields" : {
            ...
          }
      }
  ],
  "test_type": "specimen",
  "created_at": "2014-09-26T22:09:05Z",
  "updated_at": "2014-09-26T22:09:05Z",
  "location_id": "0500000US08021",
  "parent_locations": [
      "0",
      "0000000US",
      "0400000US08",
      "0500000US08021"
  ],
}

Non standard fields reported by the device will be included inside the “custom_fields” section.

The default “test_id” will be the same as the uuid. If the device reports its own id, it will be stored there.

Personally Identifiable Information

{
  "pii" : {
    "patient_id" : 2,
    "patient_name" : "Lorem Ipsum",
    "patient_telephone_number" : "12345678",
    "patient_zip_code" : "1234",
  },
  "uuid" : "c4c52784-bfd5-717d-7a91-614acd972d5e"
}

Core Fields

The core fields are:

  • sample.id - This field represents the identifier of the sample exactly as entered by the lab user on the diagnostics machine. Its purpose is to keep track of the originally reported sample identifier. Note that the values in this field may not be unique across different laboratories or even over time.
  • sample.uuid - The internal id in CDX. An automatically generated UUID to unequivocally identify the sample in CDX.
  • sample.type - The type of the sample. String.
  • sample.collection_date - The date when the sample was collected.
  • test.id - The id given by the device. If the device reports two tests with the same test.id, then the first one will be updated.
  • test.uuid - The internal id in CDX. an automatically generated UUID to unequivocally identify the test in CDX.
  • test.start_time - The timestamp when the test started running in the device.
  • test.end_time - The timestamp when the test finished running in the device.
  • test.reported_time - The creation timestamp in CDX.
  • test.updated_time - The last update timestamp in CDX (if two tests are reported with the same test.id, this field will be updated).
  • test.error_code - A numeric result code. This will follow the manufacturer’s own coding and won’t be standardized.
  • test.error_description - User friendly error description.
  • test.site_user - The user that ran the test. This is not necessarily a cdx user.
  • test.name - The name of the test as provided by the device.
  • test.status - An enumerated global result of the test.
    • The possible values are:
      • invalid
      • error
      • no_result
      • success
      • in_progress
  • test.assays - A single test can run multiple assays.
  • test.assays.name - The code name of the assay used in the test as provided by the device.
  • test.assays.condition - The condition that this particular assay tests. It must be one of the conditions listed in the manifest’s metadata, with the same notation.
  • test.assays.result - An enumerated result of the assay, if available.
    • The possible values are:
      • positive
      • negative
      • indeterminate
      • n/a
  • test.assays.quantitative_result - The result of the test, if measurable, in a numeric scale. This scale will follow the manufacturer’s convention.
  • test.type - If the test is from a real sample or if it’s just a quality control test.
    • The possible values are:
      • specimen
      • qc
  • device.uuid - The internal id that identifies the device in CDX.
  • device.name - The name of the device in CDX.
  • device.lab_user - The name of the user running the tests.
  • device.serial_number - The serial number of the device, identifiable in any external system.
  • institution.uuid - The internal CDX id of the Institution that owns the device.
  • institution.name - The name of that Institution.
  • site.uuid - The internal CDX id of the Laboratory where the device is located.
  • site.name - The name of that Laboratory.
  • patient.id - The id of the patient in CDX.
  • patient.name - The patient’s name.
  • patient.dob - The patient’s date of birth.
  • patient.gender - The birth gender of the patient.
    • The possible values are:
      • male
      • female
      • other
  • patient.email - The patient’s email.
  • patient.phone - The patient’s phone.
  • location.id - The Natural Earth geo id of the device location at the moment of the test.
  • location.parents - The list of parents, in Natural Earth geo ids, of the device location at the moment of the test.
  • location.admin_levels - The list of parents, in Natural Earth geo ids indexed by administrative level, of the device location at the moment of the test.
  • location.lat - The latitude of the device location at the moment of the test.
  • location.lng - The longitude of the device location at the moment of the test.
  • encounter.id - This field represents the identifier of the encounter exactly as entered by the lab user on the diagnostics machine.
  • encounter.uuid - The internal id in CDX. an automatically generated UUID to unequivocally identify the test in CDX.
  • encounter.patient_age - The age of the patient at the moment the test order was generated.
  • encounter.start_time - The start time of the first test of the test order.
  • encounter.end_time - The end time of the last test of the test order.
  • encounter.observations - A field for the technician to write down any observations about the test order.
  • encounter.diagnosis - A single Encounter can have multiple diagnosis, one per condition.
  • encounter.diagnosis.name - The code name of the assay used in the test as provided by the device.
  • encounter.diagnosis.condition - The condition that this particular assay tests. It must be one of the conditions listed in the manifest’s metadata, with the same notation.
  • encounter.diagnosis.result - An enumerated result of the assay, if available.
    • The possible values are:
      • positive
      • negative
      • indeterminate
      • n/a
  • encounter.diagnosis.quantitative_result - The result of the test, if measurable, in a numeric scale. This scale will follow the manufacturer’s convention.

All location, site and institution information will be automatically filled by CDX when a test is reported using the information already available in the system.