Skip to content

JSON Schemas for custom EMS data objects

About this page

This page was written by the New Horizons engineering team, which operated until December 2025. First-person references ("we", "our") are the original authors'. A number of those authors are now working with Phoenix GH, which maintains this material and stands behind it. Questions and comments are welcome — please get in touch.

Overview

The EMS Data Standards (WHO/PQS/E006/DS01.x) define an extensive set of CCE-related data objects that can be included in an EMS dataset. Beyond those well-defined data objects, Clause 4.5 of DS01.x allows manufacturers to include their own custom data objects in an EMS dataset. This document provides additional guidance to manufacturers to define such custom data objects and document them using JSON Schema.

Existing requirements

As foundational context, Clause 4.5 of DS01.2, is reproduced below.

4.5 Manufacturer-specific data objects

Legal manufacturers may define and include data objects in the JSON raw data files that are not in the data objects list defined in Annex 1, provided compliance with the following:

  • If a data object already exists in the EMS data object list for the measured value, the legal manufacturer must use the defined data object.
  • Custom data object field names should be lower case, prefixed with the character ‘z’. For example, “ztpcm” could be a phase change material temperature data object created by a legal manufacturer.
  • Custom JSON objects should be kept to a reasonable character length and frequency to limit data transmission costs. A JSON schema definition must be documented and made publicly available (e.g. code repository or website) for all custom objects that as a minimum describes the custom field name, description, and type (string, number, array, Boolean).

Implementing JSON schema for custom objects

What is JSON Schema?

JSON Schema is a standard for defining the structure, content, and semantics of JSON documents. It allows for both human-readable documentation and automated validation of data. JSON Schema is valuable in the EMS context for ensuring the custom data fields can be validated consistently across different systems. Although the low-level details of JSON schema are beyond the scope of this document, full documentation is available on the JSON Schema website.

Building a schema for custom objects

This document takes a step-by-step approach to build (and explain) a JSON Schema that describes two hypothetical data objects: zplug and ztpcm. Note that both of these objects start with the letter “z”, which satisfies the EMS naming requirements for custom data objects.

Simplest representation

The schema below is the most basic representation.

// Basic JSON Schema for zplug and ztpcm
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.org/schema/ems-custom-v01.json",
  "properties": {
    "zplug": {
      "type": "string"
    },
    "ztpcm": {
      "type": "number"
    }
  }
}

We will explore this schema line-by-line.

$schema property

"$schema": "https://json-schema.org/draft/2020-12/schema"

The $schema property declares the version of the JSON Schema specification, in URL format, that should be used to interpret your schema syntax. The latest version, 2020-12, is referenced in this example and is a good choice for most applications. Although the $schema property is not strictly required, it is a best practice to include in your document. You can find more information: here.

$id property

"$id": "https://example.org/schema/ems-custom-v01.json",

The $id property defines a unique identifier for the schema, represented as a URI. The value of this property is commonly formatted as a URL on a domain that is controlled by the authoring organization.

$id is an identifier rather than a network location, and there is no obligation to make the JSON document retrievable at that URI. A URN such as urn:my-organization:schemas:ems-custom:v01 is an equally valid choice, and underscores that point.

Choosing between a URL and a URN. Clause 5.3.3 of DS01.3 allows a supplier to deliver a custom schema in either of two ways: inline, embedded in the meta.customDataSchema property of the transmission envelope, or by reference, as a versioned and immutable URL in that same property. The by-reference value must be a real, fetchable URL. The inline $id need not be, and one of the attractions of delivering inline is precisely that it removes any obligation to operate and maintain a public endpoint — so if you deliver only inline, a URN is the honest choice, because it does not imply an endpoint that does not exist.

The one case that argues for a URL is a supplier who delivers the same schema both ways. There, use the retrieval URL as the $id, character-for-character, so that a consumer receiving the inline copy can recognise it as the schema it may already have fetched, and cache or de-duplicate accordingly. Whichever form you choose, keep it stable: two transmissions carrying the same $id must carry the same definitions.

properties

The properties section defines two custom fields:

  • zplug, which is a ”string", expected to contain textual data
  • ztpcm, which is a “number”, which accepts numeric values

Removing structural definitions from the schema

As noted previously, a JSON Schema can define both the content and structure of a JSON document. For example, the basic schema (above) provides definitions for zplug and ztpcm, but the location of these definitions relative to the properties keyword indicates that those properties should show up (if provided) at the root of a JSON object. Therefore, under the “basic” schema, the following object would be deemed invalid; although ztpcm is located at the expected location, is the wrong type (null, rather than number).

// A simple JSON object that includes two custom properties
{
  "zplug": "Type A",
  "ztpcm": null       // not a number, fails validation
}

The following object, however, would be valid according to the “basic” schema, despite the fact that the value of ztpcm is null. The explanation is that the definition of the ztpcm property places it at the root of an object, rather than inside the records array. From the perspective of the JSON Schema, the fact that ztpcm is located inside the records array makes it a different property, so the schema validation process disregards it. In this case, only zplug would be evaluated (successfully).

// A JSON object that is similar to an (abridged) EMS data record
{
  "zplug": "Type A",
  "records": [
      {
           "ABST": "20250321T082835Z",
           "TVC": 21.2,
            "ztpcm": null
       }
  ]
}

An improved example schema, below, restructures the schema to focus entirely on the content definitions of zplug and ztpcm without expressing any structural expectations or constraints. For a CCE manufacturer, the following example is an improvement over the “basic” schema because it allows the manufacturer to focus only on defining the details of their custom data objects, rather than their location within any downstream JSON representation. Countries (and other downstream users) must take responsibility for defining the structural details.

// An improved JSON Schema that eliminates *structural* definitions
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.org/schema/ems-custom-v01.json",
  "$defs": {
    "zplug": {
      "type": "string"
    },
    "ztpcm": {
      "type": "number"
    }
  }
} 

$defs keyword

In the example above, the $defs keyword provides a standardized place to keep reusable definitions of individual properties or more complex “sub-schemas”. The use of $defs in this example decouples the definitions of zplug and ztpcm from any structural implication about where these properties might be located within a larger JSON object. Note, also, that this example drops the properties keyword that was present in the “basic” example.

Final schema

The schema below expands on the previous examples by adding clarifying details to the zplug and ztpcm definitions, including the use of special keywords to constrain the valid values of these properties. This schema is similar to the one for the Demo EMS Logger's custom or manufacturer-specific data objects.

// A complete JSON Schema example for our two custom properties
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.org/schema/ems-custom-v01.json",
  "$defs": {
    "zplug": {
      "type": "string",
      "title": "Electrical plug type",
      "description": "IEC Type of the appliance's electrical plug",
      "enum": [
        "Type A",
        "Type B",
        "Type C",
        "Type D",
        "Type E",
        "Type F",
        "Type G",
        "Type H",
        "Type I",
        "Type J",
        "Type K",
        "Type L",
        "Type M",
        "Type N",
        "Type O"
      ]
    },
    "ztpcm": {
      "type": "number",
      "title": "Phase Change Material temperature",
      "description": "Temperature of phase change material at coldest location in °C",
      "minimum": -55,
      "maximum": 60
    }
  }
}

title and description properties

The title and description fields provide concise and expanded descriptions of a property, respectively. It is good practice to include both of these fields for any defined property.

enum field

The enum keyword restricts the value of a property to a specific list of predefined values. In this example, the enum field constrains the value of zplug to a list of IEC electrical plug types.

minimum and maximum properties

The minimum and maximum properties provide inclusive lower and upper limits for numeric data types. In this example, these keywords restrict the valid values of ztpcm to the range -55°C to +60°C.

In addition to enum, minimum, and maximum, the JSON Schema specification includes other “validation keywords” to constrain the valid values of a property. These keywords are documented on the JSON Schema Validation: A Vocabulary for Structural Validation of JSON webpage.

How will countries use these schemas

By providing JSON Schema definitions for custom data objects, manufacturers enable countries (and other downstream data consumers) to apply these schemas for automated data validation purposes.

To use these definitions effectively, the country will need to add structural details to the schema, defining where the custom properties will appear within the expected JSON objects. It’s important to recognize that data-processing pipelines frequently change the structure of data and it is the pipeline developers (not equipment manufacturers) who are responsible for understanding the data structure at different points in the pipeline.

It is our expectation that countries will perform data-validation in multiple steps. For example:

  1. Initial validation using the EMS Data Delivery schema, which was developed during a UNICEF consultative process in Q1 2025.
  2. Additional validation steps, only if needed, for custom manufacturer data objects.

This approach minimizes complexity for a data consumer, insofar as it avoids the need to create a single, unified JSON Schema that merges any global schemas (e.g., from PQS) with a variety of manufacturer-specific schemas. By keeping validation schemas separate, each schema becomes easier to maintain.

As a concrete example, let’s consider how a country could use a manufacturer’s definitions for zplug and ztpcm to validate those properties in the following example JSON object.

// A JSON object that is similar to an (abridged) EMS data record
{
  "zplug": "Type A",
  "records": [
      {
           "ABST": "20250321T082835Z",
           "TVC": 21.2,
            "ztpcm": null
       }
  ]
}

The JSON Schema below has been extended to be aware of the structure of the object above. A more detailed explanation is provided below the example.

// The country's structural schema, built on the manufacturer's definitions
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://moh.example.gov/schemas/ems-custom-structural-v01.json",
  "properties": {
    "zplug": { "$ref": "#/$defs/zplug"},
    "records": {
        "type": "array",
        "title": "Records array",
        "description": "Contains objects representing collection events",
        "items": {
            "type": "object",
            "properties": {
                "ztpcm": { "$ref": "#/$defs/ztpcm" }
            }
        }
    }
  },
  "$defs": {
    "zplug": {
      "type": "string",
      "title": "Electrical plug type",
      "description": "IEC Type of the appliance's electrical plug",
      "enum": [
        "Type A",
        "Type B",
        "Type C",
        "Type D",
        "Type E",
        "Type F",
        "Type G",
        "Type H",
        "Type I",
        "Type J",
        "Type K",
        "Type L",
        "Type M",
        "Type N",
        "Type O"
      ]
    },
    "ztpcm": {
      "type": "number",
      "title": "Phase Change Material temperature",
      "description": "Temperature of phase change material at coldest location in °C",
      "minimum": -55,
      "maximum": 60
    }
  }
}

The example above restores the properties field to the schema, with the following attributes.

$id: Note that this schema carries a different $id from the manufacturer's, on a domain the country controls. This is a distinct schema — the manufacturer defines what zplug and ztpcm mean, while the country defines where they appear — and giving two different schemas the same $id would break the one guarantee $id offers, that the same identifier always denotes the same definitions.

properties.zplug: Defines the zplug property and places it at the root of the evaluated object. Note that the definition of the zplug property is not duplicated here. Rather, the $ref keyword defines the property by reference to the complete zplug definition in the $defs section.

properties.records: Defines the records property as an array (type: array) and uses the items keyword to define the characteristics of each item in the array. In this case, the schema expresses that each item will be an object (type: object) and each object may include the ztpcm property. As with zplug, the definition of ztpcm is a reference to the $defs section.

It's worth repeating that the Final schema in this example is only intended to define the manufacturer's custom data objects. The standard EMS properties the appear in the records object (e.g., ABST, TVC, etc.) would be validated in a separate process using the canonical EMS JSON Schema(s).

Final thoughts

This document highlights how manufacturers can use JSON Schema to document custom data objects. Manufacturers are encouraged to modify the example provided in the Final schema section as necessary for their own purposes. These schema examples are very simple, but they satisfy the letter and the intent of the Clause 4.5 of the WHO/PQS/E006/DS01 standard.