xRegistry Service - Version 1.0-rc1

Abstract

A Registry Service exposes Resources and their metadata, for the purpose of enabling discovery of those Resources for either end-user consumption or automation and tooling.

Table of Contents

Overview

A Registry Service is one that manages metadata about Resources. At its core, the management of an individual Resource is simply a REST-based interface for creating, modifying, and deleting the Resource. However, many Resource models share a common pattern of grouping Resources and can optionally support versioning of those Resources. This specification aims to provide a common interaction pattern for these types of services with the goal of providing an interoperable framework that will enable common tooling and automation to be created.

This document is meant to be a framework from which additional specifications can be defined that expose model-specific Resources and metadata.

As of today, this specification only specifies an HTTP-based interaction model. This is not meant to imply that other protocols cannot be supported, and other protocols will likely be added in the future. When that happens, this specification will be restructured to have clean separation between a protocol-agnostic core and protocol-specific requirements.

A Registry consists of two main types of entities: Groups and Resources.

Groups, as the name implies, is a mechanism by which related Resources are arranged together under a single collection - the Group. The reason for the grouping is not defined by this specification, so the owners of the Registry can choose to define (or enforce) any pattern they wish. In this sense, a Group is similar to a "directory" on a filesystem.

Resources represent the main data of interest for the Registry. In the filesystem analogy, these would be the "files". All Resources exist under a single Group and, similar to Groups, have a set of Registry metadata. However, unlike a Group which only has Registry metadata, each Resource can also have a "document" associated with it. For example, a "schema" Resource might have a "schema document" as its "document". This specification places no restriction on the type of content stored in the Resource's document.

This specification defines a set of common metadata that can appear on both Groups and Resources, and allows for domain-specific extensions to be added.

The following 3 diagrams show (from left to right):
1 - The core concepts of the Registry in its most abstract form.
2 - A Registry concept model with multiple types of Groups/Resources.
3 - A concrete sample usage of Registry that includes the use of an attribute on "Message Definition" that is a reference to a "Schema" document - all within the same Registry instance.

      

For easy reference, the JSON serialization of a Registry adheres to this form:

{
  "specversion": "STRING",
  "registryid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",

  "capabilities": {                     # Supported capabilities/options
    "flags": [                          # Query parameters
      "collections",? "doc",? "epoch",? "filter",? "inline",?
      "nodefaultversionid",? "nodefaultversionsticky",? "noepoch",?
      "noreadonly",?  "offered",? "schema",? "setdefaultversionid",?
      "specversion",?
      "STRING" *
    ],
    "mutable": [                        # What is mutable in the Registry
      "capabilities",? "entities",? "model",? "STRING"*
    ], ?
    "pagination": BOOLEAN, ?
    "schemas": [ "xRegistry-json/1.0-rc1", "STRING" * ], ?
    "shortself": BOOLEAN, ?
    "specversions": [ "1.0-rc1", "STRING"* ], ?
    "sticky": BOOLEAN, ?

    "STRING": ... *                     # Extension capabilities
  }, ?

  "model": {                            # Only if inlined
    "labels": { "STRING": "STRING" * }, ?
    "attributes": {                     # Registry level attributes/extensions
      "STRING": {                       # Attribute name (case-sensitive)
        "name": "STRING",               # Same as attribute's key
        "type": "TYPE",                 # string, decimal, array, object, ...
        "target": "STRING", ?           # If "type" is "xid"
        "namecharset": "STRING", ?      # If "type" is "object"
        "description": "STRING", ?
        "enum": [ VALUE * ], ?          # Array of scalar values of type "TYPE"
        "strict": BOOLEAN, ?            # Just "enum" values? Default=true
        "readonly": BOOLEAN, ?          # From client's POV. Default=false
        "immutable": BOOLEAN, ?         # Once set, can't change. Default=false
        "required": BOOLEAN, ?          # Default=false
        "default": VALUE, ?             # Scalar attribute's default value

        "attributes": { ... }, ?        # If "type" above is object
        "item": {                       # If "type" above is map,array
          "type": "TYPE", ?             # map value type, or array type
          "namecharset": "STRING", ?    # If this item "type" is object
          "attributes": { ... }, ?      # If this item "type" is object
          "item": { ... } ?             # If this item "type" is map,array
        } ?

        "ifvalues": {                   # If "type" is scalar
          "VALUE": {                    # Possible attribute value
            "siblingattributes": { ... } # See "attributes" above
          } *
        } ?
      } *
    },

    "groups": {
      "STRING": {                       # Key=plural name, e.g. "endpoints"
        "plural": "STRING",             # e.g. "endpoints"
        "singular": "STRING",           # e.g. "endpoint"
        "description": "STRING", ?
        "modelversion": "STRING", ?     # Version of the group model
        "compatiblewith": "URI", ?      # Statement of compatibility with model spec
        "labels": { "STRING": "STRING" * }, ?
        "attributes": { ... }, ?        # Group level attributes/extensions

        "resources": {
          "STRING": {                   # Key=plural name, e.g. "messages"
            "plural": "STRING",         # e.g. "messages"
            "singular": "STRING",       # e.g. "message"
            "description": "STRING", ?
            "maxversions": UINTEGER, ?  # Num Vers(>=0). Default=0, 0=unlimited
            "setversionid": BOOLEAN, ?  # vid settable? Default=true
            "setdefaultversionsticky": BOOLEAN, ? # sticky settable? Default=true
            "hasdocument": BOOLEAN, ?     # Has separate document. Default=true
            "singleversionroot": BOOLEAN, ? # Default=false"
            "typemap": MAP, ?             # contenttype mappings
            "modelversion": "STRING", ?   # Version of the resource model
            "compatiblewith": "URI", ?    # Statement of compatibility with model spec
            "labels": { "STRING": "STRING" * }, ?
            "attributes": { ... }, ?      # Version attributes/extensions
            "metaattributes": { ... } ?   # Resource attributes/extensions
          } *
        } ?
      } *
    } ?
  }, ?

  # Repeat for each Group type
  "GROUPSurl": "URL",                              # e.g. "endpointsurl"
  "GROUPScount": UINTEGER,                         # e.g. "endpointscount"
  "GROUPS": {                                      # Only if inlined
    "KEY": {                                       # Key=the Group id
      "GROUPid": "STRING",                         # The Group ID
      "self": "URL",
      "shortself": "URL", ?
      "xid": "XID",
      "epoch": UINTEGER,
      "name": "STRING", ?
      "description": "STRING", ?
      "documentation": "URL", ?
      "labels": { "STRING": "STRING" * }, ?
      "createdat": "TIMESTAMP",
      "modifiedat": "TIMESTAMP",

      # Repeat for each Resource type in the Group
      "RESOURCESurl": "URL",                       # e.g. "messagesurl"
      "RESOURCEScount": UINTEGER,                  # e.g. "messagescount"
      "RESOURCES": {                               # Only if inlined
        "KEY": {                                   # The Resource id
          "RESOURCEid": "STRING",
          "versionid": "STRING",                   # Default Version's ID
          "self": "URL",                           # Resource URL, not Version
          "shortself": "URL", ?
          "xid": "XID",                            # Resource XID, not Version
          "epoch": UINTEGER,                       # Start of default Ver attrs
          "name": "STRING", ?
          "isdefault": true,
          "description": "STRING", ?
          "documentation": "URL", ?
          "labels": { "STRING": "STRING" * }, ?
          "createdat": "TIMESTAMP",
          "modifiedat": "TIMESTAMP",
          "ancestor": "STRING",
          "contenttype": "STRING, ?                # Add default Ver extensions

          "RESOURCEurl": "URL", ?                  # If not local
          "RESOURCE": ... Resource document ..., ? # If local & inlined & JSON
          "RESOURCEbase64": "STRING", ?            # If local & inlined & ~JSON
                                                   # End of default Ver attrs
          # Resource level helper attributes
          "metaurl": "URL",
          "meta": {                                # Only if inlined
            "RESOURCEid": "STRING",
            "self": "URL",                         # URL to "meta" object
            "shortself": "URL", ?
            "xid": "XID",
            "xref": "URL", ?                       # xid of linked Resource
            "epoch": UINTEGER,                     # Resource's epoch
            "createdat": "TIMESTAMP",              # Resource's
            "modifiedat": "TIMESTAMP",             # Resource's
            "readonly": BOOLEAN,                   # Default=false
            "compatibility": "STRING",             # Default=none
            "compatibilityauthority": "STRING", ?  # Default=external
            "deprecated": {
              "effective": "TIMESTAMP", ?
              "removal": "TIMESTAMP", ?
              "alternative": "URL", ?
              "docs": "URL"?
            }, ?

            "defaultversionid": "STRING",
            "defaultversionurl": "URL",
            "defaultversionsticky": BOOLEAN        # Default=false
          }, ?
          "versionsurl": "URL",
          "versionscount": UINTEGER,
          "versions": {                            # Only if inlined
            "KEY": {                               # The Version's versionid
              "RESOURCEid": "STRING",              # The Resource id
              "versionid": "STRING",               # The Version id
              "self": "URL",                       # Version URL
              "shortself": "URL", ?
              "xid": "XID",
              "epoch": UINTEGER,                   # Version's epoch
              "name": "STRING", ?
              "isdefault": BOOLEAN,                # Default=false
              "description": "STRING", ?
              "documentation": "URL", ?
              "labels": { "STRING": "STRING" * }, ?
              "createdat": "TIMESTAMP",
              "modifiedat": "TIMESTAMP",
              "ancestor": "STRING",                    # Ancestor's versionid
              "contenttype": "STRING", ?

              "RESOURCEurl": "URL", ?                  # If not local
              "RESOURCE": ... Resource document ..., ? # If inlined & JSON
              "RESOURCEbase64": "STRING" ?             # If inlined & ~JSON
            } *
          } ?
        } *
      } ?
    } *
  } ?
}

Notations and Terminology

Notational Conventions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

For clarity, OPTIONAL attributes (specification-defined and extensions) are OPTIONAL for clients to use, but the servers' responsibility will vary. Server-unknown extension attributes MUST be silently stored in the backing datastore. Specification-defined, and server-known extension attributes MUST generate an error if the corresponding feature is not supported or enabled. However, as with all attributes, if accepting the attribute results in a bad state (such as exceeding a size limit, or results in a security issue), then the server MAY choose to reject the request.

In the pseudo JSON format snippets ? means the preceding item is OPTIONAL, * means the preceding item MAY appear zero or more times, and + means the preceding item MUST appear at least once. The presence of the # character means the remaining portion of the line is a comment. Whitespace characters in the JSON snippets are used for readability and are not normative.

When HTTP query parameters are discussed, they are presented as ?NAME where NAME is the name of the query parameter.

Use of the words GROUP and RESOURCE are meant to represent the singular name of a Group and Resource type being used. While GROUPS and RESOURCES are the plural name of those respective types. Use of the word SINGULAR represents the singular name of the entity being referenced. For example, for a "schema document" Resource type where its plural name is defined as schemas and its singular name is defined as schema, the SINGULAR value would be schema.

Use of acronyms and words in all capital letters (e.g. KEY) typically represent a field that will be replaced by its real value at runtime. Additionally, the following acronyms are defined:

The following are used to denote an instance of one of the associated data types (see Attributes and Extensions for more information about each data type):

Terminology

This specification defines the following terms:

Group

An entity that acts as a collection of related Resources.

Registry

An implementation of this specification. Typically, the implementation would include model-specific Groups, Resources and extension attributes.

Resource

A Resource is typically the main entity that is stored within a Registry Service. A Resource MUST exist within the scope of a Group and it MAY be versioned.

Registry Attributes and APIs

This section defines common Registry metadata attributes and APIs. It is an explicit goal for this specification that metadata can be created and managed in files in a file system, for instance in a Git repository, and also managed in a Registry service that implements the API described here.

For instance, during development of a module, the metadata about the events raised by the modules will best be managed in a file that resides alongside the module's source code. When the module is ready to be deployed into a concrete system, the metadata about the events will be registered in a Registry service along with the endpoints where those events can be subscribed to or consumed from, and which allows discovery of the endpoints and all related metadata by other systems at runtime.

Therefore, the hierarchical structure of the Registry model is defined in such a way that it can be represented in a single file, including but not limited to JSON, or via the entity graph of a REST API.

If the processing of a request fails then an error MUST be generated and the entire request MUST be undone. See the Error Processing section for more information.

In the remainder of this specification, in particular when defining the attributes of the Registry entities, the terms "document view" or "API view" will be used to indicate whether the serialization of the entity in a response is meant for use as a stand-alone document or as part of a REST API message exchange. The most notable differences are that in document view:

Most of these differences are to make it easier for tooling to use the "stand-alone" document view of the Registry. For a complete list of the differences in "document view" see the Doc Flag flag and the Exporting section.

Note that "document view" only refers to response messages. There is no "document view" concept for requests. However, "document view" responses are designed such that they can be used in request messages as they still convey the same information as an "API view" response.

Implementation Customizations

This specification only defines the core APIs, and their semantics, of a Registry service. It does not address many of the details that would need to be added for a live instance of a service; as often times these aspects are very specific to the environment in which the service is running. For example, this specification does not address authentication or authorization levels of users, nor how to securely protect the APIs (aside from the implied use of https), clients or servers from attacks. Implementations of this specification are expected to add these various features as needed.

Additionally, implementation MAY choose to customize the data and behavior on a per-user basis as needed. For example, the following customizations might be implemented:

The goal of these customizations is not to allow for implementation to violate the specification, rather it is to allow for real-world requirements to be met while maintaining the interoperability goals of the specification.

Implementations are encouraged to contact the xRegistry community if it is unclear if certain customizations would violate the specification.

Attributes and Extensions

Unless otherwise noted, all attributes and extensions MUST be mutable and MUST be one of the following data types:

The 6 variants of URI/URL are provided to allow for strict type adherence when needed. However, for attributes that are simply "pointers" that might in practice be any of those 6 types it is RECOMMENDED that uri be used.

Attributes that are defined to be relative URIs or URLs MUST state what they are relative to and any constraints on their values, if any.

The root path of a Registry service MAY be at the root of a host or have a PATH portion in its URL (e.g. http://example.com/myregistry).

The "scalar" data types are: boolean, decimal, integer, string, timestamp, uinteger, uri, uriabsolute, urirelative, uritemplate, url, urlabsolute, urlrelative, xid. Note that any is not a "scalar" type as its runtime value could be a complex type such as object.

All attributes (specification-defined and extensions) MUST adhere to the following rules:

Implementations of this specification MAY define additional (extension) attributes. However, they MUST adhere to the following rules:

Common Attributes

The following attributes are used by one or more entities defined by this specification. They are defined here once rather than repeating them throughout the specification.

For easy reference, the JSON serialization of these attributes adheres to this form:

The definition of each attribute is defined below:

SINGULARid (id) Attribute

While SINGULARid can be something like a UUID, when possible, it is RECOMMENDED that it be something human friendly as these values will often appear in user-facing situations such as URLs or as command-line parameters. And, in cases where name is absent, it might be used as the display name.

Note, since SINGULARid is immutable, in order to change its value, a new entity would need to be created with the new SINGULARid that is a deep-copy of the existing entity. Then the existing entity would be deleted.

self Attribute
shortself Attribute
xid Attribute
epoch Attribute
name Attribute
description Attribute
documentation Attribute
labels Attribute
createdat Attribute
modifiedat Attribute

Registry APIs

This specification defines the following API patterns:

/                                               # Access the Registry
/capabilities                                   # Access available features
/model                                          # Access the model definitions
/export                                         # Retrieve Registry as a doc
/GROUPS                                         # Access a Group Type
/GROUPS/gID                                     # Access a Group
/GROUPS/gID/RESOURCES                           # Access a Resource Type
/GROUPS/gID/RESOURCES/rID                       # Default Version of a Resource
/GROUPS/gID/RESOURCES/rID/versions              # Versions of a Resource
/GROUPS/gID/RESOURCES/rID/versions/vID          # Access a Version of a Resource

While these APIs are shown to be at the root path of a host, implementations MAY choose to prefix them as necessary. However, the same prefix MUST be used consistently for all APIs in the same Registry instance.

Support for any particular API defined by this specification is OPTIONAL, however, it is STRONGLY RECOMMENDED that server-side implementations support at least the "read" (e.g. HTTP GET) operations. Implementations MAY choose to incorporate authentication and/or authorization mechanisms for the APIs.

If an OPTIONAL HTTP path is not supported by an implementation, then any use of that API MUST generate an error (api_not_found).

If an HTTP method is not supported for a supported HTTP path, then an error (method_not_allowed) MUST be generated.

Implementations MAY support extension APIs, however, the following rules MUST apply:

For example, a new API with an HTTP path of /my-api is allowed, but APIs with /model/my-api or /name HTTP paths are not.

This specification attempts to follow a standard REST/HTTP processing model. The following key aspects are called out to help understand the overall pattern of the APIs:

In general, if a server is unable to retrieve all of the data intended to be sent in a response, then an error (data_retrieval_error) MUST be generated and the request rejected without any changes being made. However, it is permissible for a server to attempt some creative processing. For example, if while processing a GET the server can only retrieve half of the entities to be returned at the current point in time, then it could return those with an indication of there being more (via the pagination specification). Then during the next GET request it could return the remainder of the data - or an error if it is still not available. Note that if an entity is to be sent, then it MUST be serialized in its entirety (all attributes, and requested child entities) or an error MUST be generated.

There might be situations where someone will do a GET to retrieve data from a Registry, and then do an update operation to a Registry with that data. Depending on the use case, they might not want some of the retrieved data to be applied during the update - for example, they might not want the epoch validation checking to occur. Rather than forcing the user to edit the data to remove the potentially problematic attributes, the following query parameters MAY be included on write operations to control certain aspects of the processing:

Any JSON xRegistry metadata message that represents a single entity (i.e. not a map) MAY include a top-level "$schema" attribute that points to a JSON Schema document that describes the message contents. These notations can be used or ignored by receivers of these messages. There is no requirement for implementation of this specification to persist these values, to include them in responses or to use this information.

No-Code Servers

One of the goals of xRegistry is to be as broadly supported as possible. Requiring all xRegistry endpoints to support the full range of APIs defined in this specification might not be feasible in all cases. In particular, there might be cases where someone wishes to host a read-only xRegistry server to only expose their documents (and metadata) and therefore the write operations or advanced features (such as inlining or filtering) might not be needed. In those cases, simple file serving HTTP servers, such as blob stores, ought to be sufficient, and in those cases requiring support for query parameters and other advanced features (that could require code) might not always be possible.

To support these simple (no-code) scenarios, this specification is written such that all of the APIs are OPTIONAL, and all of the query parameters on the read operations are OPTIONAL (typically specified by saying that they SHOULD be supported). However, it is STRONGLY RECOMMENDED that full API servers support the query parameters when possible to enable a better user experience, and increase interoperability.

Note that simple file servers SHOULD support exposing Resources where the HTTP body response contains the Resource's associated "document" as well as the case where the HTTP response body contains a JSON serialization of the Resource via the $details suffix on the URL path. This can be achieved by creating a secondary sibling file on disk with $details at the end of its filename.


The remainder of this specification mainly focuses on the successful interaction patterns of the APIs. For example, most examples will show an HTTP "200 OK" as the response. Each implementation MAY choose to return a more appropriate response based on the specific situation. For example, in the case of an authentication error the server could return 401 Unauthorized.

The following sections define the APIs in more detail.


Registry Collections

Registry collections (GROUPS, RESOURCES and versions) that are defined by the Registry Model MUST be serialized according to the rules defined below.

The serialization of a collection is done as 3 attributes and they MUST adhere to their respective forms as follows:

"COLLECTIONSurl": "URL",
"COLLECTIONScount": UINTEGER,
"COLLECTIONS": {
  # Map of entities in the collection, key is the "SINGULARid" of the entity
}

Where:

When the COLLECTIONS attribute is expected to be present in the serialization, but the number of entities in the collection is zero, it MUST still be included as an empty map (e.g. {}).

The set of entities that are part of the COLLECTIONS attribute is a point-in-time view of the Registry. There is no guarantee that a future GET to the COLLECTIONSurl will return the exact same collection since the contents of the Registry might have changed. This specification makes no statement as to whether a subsequent GET that is missing previously returned entities is an indication of those entities being deleted or not.

Since collections could be too large to retrieve in one request, when retrieving a collection, the client MAY request a subset by using the pagination specification. Likewise, the server MAY choose to return a subset of the collection using the same mechanism defined in that specification even if the request didn't ask for pagination. The pagination specification MUST only be used when the request is directed at a collection, not at its owning entity (such as the root of the Registry, or at an individual Group or Resource).

In the remainder of the specification, the presence of the Link HTTP header indicates the use of the pagination specification MAY be used for that API.

The requirements on the presence of the 3 COLLECTIONS attributes varies between document and API views, and is defined below:

Collections in Document View

In document view:

Collections in API View

In API view:

Updating Nested Registry Collections

When updating an entity that can contain Registry collections, the request MAY contain the 3 collection attributes. The COLLECTIONSurl and COLLECTIONScount attributes MUST be silently ignored by the server.

If the COLLECTIONS attribute is present, the server MUST process each entity in the collection map as a request to create or update that entity according to the semantics of the HTTP method used. An entry in the map that isn't a valid entity (e.g. is null) MUST generate an error (bad_request).

For example:

PUT https://example.com/endpoints/ep1

{
  "endpointid": "ep1",
  "name": "A cool endpoint",

  "messages": {
    "mymsg1": { ... },
    "mymsg2:" { ... }
  }
}

will not only create/update an endpoint Group with an endpointid of ep1 but will also create/update its message Resources (mymsg1 and mymsg2).

Any error while processing a nested collection entity MUST result in the entire request being rejected.

An absent COLLECTIONS attribute MUST be interpreted as a request to not modify the collection at all.

If a client wishes to replace an entire collection, rather than just add new entities, the client MUST use one of the DELETE operations on the collection first.

In cases where an update operation includes attributes meant to be applied to the "default" Version of a Resource, and the incoming inlined versions collections includes that "default" Version, the Resource's default Version attributes MUST be silently ignored. This is to avoid any possible conflicting data between the two sets of data for that Version. In other words, the Version attributes in the incoming versions collection wins.

To better understand this scenario, consider the following HTTP request to update a Message where the defaultversionid is v1:

PUT http://example.com/endpoints/ep1/messages/msg1

{
  "messageid": "msg1",
  "versionid": "v1",
  "name": "Blob Created"

  "versions": {
    "v1": {
      "messageid": "msg1",
      "versionid": "v1",
      "name": "Blob Created Message Definition"
    }
  }
}

If the versions collection were not present with the v1 entity then the top-level attributes would be used to update the default Version (v1 in this case). However, because it is present, the request to update v1 becomes ambiguous because it is not clear if the server is meant to use the top-level attributes or if it is to use the attributes under the v1 entity of the versions collection. When both sets of attributes are the same, then it does not matter. However, in these cases the name attributes have different values. The paragraph above mandates that in these potentially ambiguous cases the entity in the versions collection is to be used and the top-level attributes are to be ignored - for the purposes of updating the "default" Version's attributes. So, in this case the name of the default (v1) Version will be Blob Created Message Definition.


Entity Processing Rules

Rather than repeating the processing rules for each type of xRegistry entity or Registry collection, the overall pattern is defined once in this section and any entity-, or collection-specific rules will be detailed in the appropriate section in the specification.

Creating or Updating Entities

This defines the general rules for how to update entities.

Creating or updating entities MAY be done using HTTP PUT, PATCH or POST methods:

Based on the entity being processed, the OPTIONS available will vary.

The PUT variant MUST adhere to the following:

The POST variant MUST adhere to the following:

The PATCH variant when directed at a single entity, MUST adhere to the PUT semantics defined above with the following exceptions:

The PATCH variant when directed at an xRegistry collection, MUST adhere to the following:

The processing of each individual entity follows the same set of rules:

A successful response MUST return the same response as a GET to the entity (or entities) processed, showing their current representation, with the following exceptions:

Otherwise an HTTP 200 OK without an HTTP Location header MUST be returned.

Note that the response MUST be generated applying the semantics of any query parameters specified on the request URL (e.g. ?inline). If an error occurs while generating the response (e.g. invalid ?filter), then an error MUST be generated and the entire operation MUST be undone.

Retrieving a Registry Collection

To retrieve a Registry collection, an HTTP GET MAY be used. The request MUST be of the form:

GET PATH-TO-COLLECTION

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <URL>;rel=next;count=UINTEGER ?

{
  "KEY": {                                           # SINGULARid value
    "SINGULARid": "STRING",
    ... remaining entity attributes ...
  } *
}
Retrieving an Entity from a Registry Collection

To retrieve an entity, an HTTP GET MAY be used. The request MUST be of the form:

GET PATH-TO-COLLECTION/ID-OF-ENTITY

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "SINGULARid": "STRING",
  ... remaining entity attributes ...
}
Deleting Entities in a Registry Collection

There are two ways to delete entities from a Registry collection:

  1. to delete a single entity, an HTTP DELETE MAY be used. The request MUST be of the form:
DELETE PATH-TO-COLLECTION/ID-OF-ENTITY[?epoch=UINTEGER]

Where:

The following query parameter SHOULD be supported by servers:

  1. to delete multiple entities within a Registry collection, the request MUST be in one of two forms:

For non-Resource entities:

DELETE PATH-TO-COLLECTION

{
  "KEY": {                                            # SINGULARid of entity
    "epoch": UINTEGER ?
  } *
} ?

or

For Resource entities (see below for more details):

DELETE PATH-TO-COLLECTION

{
  "KEY": {                                            # SINGULARid of entity
    "meta": {
      "epoch": UINTEGER ?
    } ?
  } *
} ?

Where:

Whether the request is to delete a single entity or multiple, deleting an entity MUST delete all children entities as well - meaning, any entities within any nested Registry collections.

Any error MUST result in the entire request being rejected.

A successful response MUST return either:

HTTP/1.1 204 No Content

with an empty HTTP body, or:

HTTP/1.1 200 OK

if, as an extension, the server chooses to return additional data in the HTTP body.


Registry Entity

The Registry entity represents the root of a Registry and is the main entry-point for traversal and discovery.

The serialization of the Registry entity adheres to this form:

{
  "specversion": "STRING",
  "registryid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",

  "capabilities": { Registry capabilities }, ?   # Only if inlined
  "model": { Registry model }, ?                 # Only if inlined

  # Repeat for each Group type
  "GROUPSurl": "URL",                            # e.g. "endpointsurl"
  "GROUPScount": UINTEGER,                       # e.g. "endpointscount"
  "GROUPS": { GROUPS collection } ?              # Only if inlined
}

The Registry entity includes the following common attributes:

and the following Registry level attributes:

specversion Attribute
model Attribute
GROUPS Collections

Retrieving the Registry

To retrieve the Registry, its metadata attributes, and Groups, an HTTP GET MAY be used.

The request MUST be of the form:

GET /[?specversion=...]

The following query parameter SHOULD be supported by servers:

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "specversion": "STRING",
  "registryid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",

  "capabilities": { Registry capabilities }, ?   # Only if inlined
  "model": { Registry model }, ?                 # Only if inlined

  # Repeat for each Group type
  "GROUPSurl": "URL",                 # e.g. "endpointsurl"
  "GROUPScount": UINTEGER,            # e.g. "endpointscount"
  "GROUPS": { GROUPS collection } ?   # Only if inlined
}

Examples:

Retrieve a Registry that has 2 types of Groups (endpoints and schemagroups):

GET /
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "specversion": "1.0-rc1",
  "registryid": "myRegistry",
  "self": "https://example.com/",
  "xid": "/",
  "epoch": 1,
  "createdat": "2024-04-30T12:00:00Z",
  "modifiedat": "2024-04-30T12:00:01Z",

  "endpointsurl": "https://example.com/endpoints",
  "endpointscount": 42,

  "schemagroupsurl": "https://example.com/schemagroups",
  "schemagroupscount": 1
}

Another example where:

GET /?inline=schemagroups,model

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "specversion": "1.0-rc1",
  "registryid": "myRegistry",
  "self": "https://example.com/",
  "xid": "/",
  "epoch": 1,
  "createdat": "2024-04-30T12:00:00Z",
  "modifiedat": "2024-04-30T12:00:01Z",

  "model": {
    ... xRegistry spec-defined attributes excluded for brevity ...
    "groups": {
      "endpoints": {
        "plural": "endpoints",
        "singular": "endpoint",
        "attributes": {
          ... xRegistry spec-defined attributes excluded for brevity ...
          "shared": {
            "name": "shared",
            "type": "boolean"
          }
        },

        "resources": {
          "messages": {
            "plural": "messages",
            "singular": "message",
            "attributes": {
              ... xRegistry spec-defined attributes excluded for brevity ...
              "*": {
                type: "any"
              }
            },
            "maxversions": 1
          }
        }
      },
      "schemagroups": {
        "plural": "schemagroups",
        "singular": "schemagroup",
        ... xRegistry spec-defined attributes excluded for brevity ...

        "resources": {
          "schemas": {
            "plural": "schemas",
            "singular": "schema",
            ... xRegistry spec-defined attributes excluded for brevity ...
            "maxversions": 1
          }
        }
      }
    }
  },

  "endpointsurl": "https://example.com/endpoints",
  "endpointscount": 42,

  "schemagroupsurl": "https://example.com/schemagroups",
  "schemagroupscount": 1,
  "schemagroups": {
    "mySchemas": {
      "schemaid": "mySchemas",
      # Remainder of schemagroup is excluded for brevity
    }
  }
}

Updating the Registry Entity

To update the Registry entity, an HTTP PUT or PATCH MAY be used.

The request MUST be of the form:

PUT /
or
PATCH /
Content-Type: application/json; charset=utf-8
If-Match: "UINTEGER|*" ?

{
  "registryid": "STRING", ?
  "epoch": UINTEGER, ?
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP", ?
  "modifiedat": "TIMESTAMP", ?

  "model": { Registry model }, ?

  # Repeat for each Group type
  "GROUPS": { GROUPS collection } ?
}

Where:

A successful response MUST include the same content that an HTTP GET on the Registry would return, and be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "specversion": "STRING",
  "registryid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",

  # Repeat for each Group type
  "GROUPSurl": "URL",
  "GROUPScount": UINTEGER
}

Note that the response MUST NOT include the model attribute, nor any inlined GROUPS collections.

Examples:

Updating a Registry's metadata

PUT /
Content-Type: application/json; charset=utf-8

{
  "registryid": "myRegistry",
  "name": "My Registry",
  "description": "An even cooler registry!"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "specversion": "1.0-rc1",
  "registryid": "myRegistry",
  "self": "https://example.com/",
  "xid": "/",
  "epoch": 2,
  "name": "My Registry",
  "description": "An even cooler registry!",
  "createdat": "2024-04-30T12:00:00Z",
  "modifiedat": "2024-04-30T12:00:01Z",

  "endpointsurl": "https://example.com/endpoints",
  "endpointscount": 42,

  "schemagroupsurl": "https://example.com/schemagroups",
  "schemagroupscount": 1
}

Registry Capabilities

In order to programmatically discover which capabilities are supported by an implementation, servers MUST support exposing this information via a "capabilities" map that lists each supported feature along with any related configuration detail that will help in successful usage of that feature.

The "key" of the capabilities-map is the "name" of each feature, and the "value" is a feature specific set of configuration values. With the most basic being a BOOLEAN value of true to indicate support for the feature.

The capabilities-map MAY be retrieved via two mechanisms:

Regardless of the retrieval mechanism, the format of the capabilities-map MUST be of the form:

{
  "flags": [ "STRING" * ], ?
  "mutable": [ "STRING" * ], ?
  "pagination": BOOLEAN, ?
  "schemas": [ "STRING" * ], ?
  "shortself": BOOLEAN, ?
  "specversions": [ "STRING" ], ?
  "sticky": BOOLEAN, ?

  "STRING": ... capability configuration ... *   // Extension capabilities
}

Where:

All capability values, including extensions, MUST be defined as one of the following:

Absence of a capability in the capability map is an indication of that feature not being supported. All supported extensions MUST be included in the list.

Absence, presence, or configuration values of a feature in the map MAY vary based on the authorization level of the client making the request.

The following defines the specification-defined capabilities:

flags

mutable

pagination

schemas

shortself

specversions

sticky

The list of values for the arrays MUST be case-insensitive and MAY include extension values.

For clarity, servers MUST include all known capabilities in the serialization, even if they are set to their default values or have empty lists.

Updating the Capabilities of a Server

If supported, updates to the server's capabilities MAY be done via an HTTP PUT, or PATCH, to the /capabilities API, or by updating the capabilities attribute on the root of the Registry. As with other APIs, a PUT MUST be interpreted as a request to update the entire set of capabilities and any missing capability MUST be interpreted as a request to reset it to its default value. If a PATCH is used then each capability included MUST be fully specified, and fully replaced by the incoming value. In other words, PATCH is done at a capability level not any deeper within the JSON structure.

The request to the /capabilities API MUST be of the form:

PUT /capabilities
Content-Type: application/json; charset=utf-8

{ ... Capabilities map ...  }

or

PATCH /capabilities
Content-Type: application/json; charset=utf-8

{ ... Capabilities map ...  }

Where:

A successful response MUST include a full representation of all of the capabilities of the Registry and be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{ ... Capabilities map ... }

Updates via the capabilities attribute follows the same attribute update semantics as the other Registry level attributes. Note that using an HTTP PATCH to update the Registry's attributes MAY include the capabilities attribute, however, it MUST be processed with the PATCH semantics as well.

During the processing of a request to update the capabilities, the semantic change MUST NOT take effect until after the processing of the current request. Note that if the response includes the serialization of the Registry's capabilities, then the changes MUST appear in that serialization.

For any capability that is an array of strings, a value of "*" MAY be used to indicate that the server MUST replace "*" with the full set of items that are available. An error (capability_error) MUST be generated if "*" appears with any other value in the list. "*" MUST NOT appear in the serialization in any server's response.

Regardless of the mechanism used to update the capabilities, the Registry's epoch value MUST be incremented.

In order for a client to discover the list of available values for each capability, an HTTP GET MAY be sent to the /capabilities API with the ?offered query parameter and the response MUST adhere to the following (which borrows many of the same structure from the /model API):

GET /capabilities?offered

{
  "STRING": {
    "type": "TYPE",
    "item": {
      "type": "TYPE"
    }, ?
    "enum": [ VALUE, * ], ?
    "min": VALUE, ?
    "max": VALUE, ?
    "documentation": "URL" ?
  }, *
}

Where:

For example:

GET /capabilities?offered

{
  "flags": {
    "type": "string",
    "enum": [ "collections", "doc", "epoch", "filter", "inline",
      "nodefaultversionid", "nodefaultversionsticky", "noepoch", "noreadonly",
      "offered", "schema", "setdefaultversionid", "specversion" ]
  },
  "pagination": {
    "type": "boolean",
    "enum": [ false, true ]
  },
  "schemas": {
    "type": "string",
    "enum": [ "xRegistry-json/1.0-rc1" ]
  },
  "shortself": {
    "type": "boolean",
    "enum": [ false, true ]
  },
  "specversions": {
    "type": "string",
    "enum": [ "xRegistry-json/1.0-rc1" ]
  },
  "sticky": {
    "type": "boolean",
    "enum": [ true ]
  }
}

The enum of values allows for some special cases:

A request to update a capability with a value that is compliant with the output of the /capabilities?offered MAY still generate an error (capability_error) if the server determines it cannot support the request. For example, due to authorization concerns or the value, while syntactically valid, isn't allowed in certain situations.

For clarity, even in cases where there is no variability allowed with certain capabilities they SHOULD still be listed in both the /capabilities API and the /capabilities?offered API to maximize discoverability. For example, if pagination is not supported, then a server SHOULD still include:

  "pagination": false

in the /capabilities output, and

  "pagination": {
    "type": "boolean",
    "enum": [ false ]
  }

in the /capabilities?offered output (assuming both APIs are supported).

Registry Model

The Registry model defines the Groups, Resources, extension attributes and changes to specification-defined attributes. This information is intended to be used by tooling that does not have knowledge of the structure of the Registry in advance and therefore will need to dynamically discover it.

To enable support for a wide range of use cases, but to also ensure interoperability across implementations, the following rules have been defined with respect to how models are defined or updated:

Any specification attributes not included in a request to define, or update, a model MUST be included in the resulting model. In other words, the Registry's model consists of the specification-defined attributes overlaid with the attributes that are explicitly-defined as part of a model create/update request.

Note: there is no mechanism defined to delete specification-defined attributes from the model.

Registries MAY support extension attributes to the model language (meaning, new attributes within the model definitions themselves), but only if the server supports them. Servers MUST generate an error (model_error) if a model definition includes unknown model language attributes.

Once a Registry has been created, changes to the model MAY be supported by server implementations. This specification makes no statement as to what types of changes are allowed beyond the following requirements:

Any request to update the model that does not adhere to those requirements MUST generate an error (model_compliance_error).

How the server guarantees that all entities in the Registry are compliant with the model is an implementation detail. For example, while it is NOT RECOMMENDED, it is valid for an implementation to modify (or even delete) existing entities to ensure model compliance. Instead, it is RECOMMENDED that the model update requests generate an error (model_compliance_error) if existing entities are not compliant.

Additionally, is it STRONGLY RECOMMENDED that model updates be limited to backwards compatible changes.

Implementations MAY choose to limit the types of changes made to the model, or not support model updates at all.

The xRegistry schema for an empty Registry can be found here, while a schema for a sample xRegistry (with Groups and Resources) can be found here.

The Registry model can be retrieved two ways:

  1. as a stand-alone entity via an HTTP GET to the /model API. This is useful when management of the Registry's model is needed independent of the entities within the Registry. See Retrieving the Registry Model for more information.
  2. as part of the Registry contents by requesting the model attribute be inlined. This is useful when it is desirable to view the entire Registry as a single document - such as an "export" type of scenario. See the Retrieving the Registry section for more information on this option.

Regardless of how the model is retrieved, the overall format is as follows:

{
  "labels": { "STRING": "STRING" * }, ?
  "attributes": {                      # Registry level extensions
    "STRING": {                        # Attribute name
      "name": "STRING",                # Same as attribute's key
      "type": "TYPE",                  # boolean, string, array, object, ...
      "target": "STRING", ?            # If "type" is "xid"
      "namecharset": "STRING", ?       # If "type" is "object"
      "description": "STRING",
      "enum": [ VALUE * ], ?           # Array of values of type "TYPE"
      "strict": BOOLEAN, ?             # Just "enum" values or not. Default=true
      "readonly": BOOLEAN, ?           # From client's POV. Default=false
      "immutable": BOOLEAN, ?          # Once set, can't change. Default=false
      "required": BOOLEAN, ?           # Default=false
      "default": VALUE, ?              # Scalar attribute's default value

      "attributes": { ... }, ?         # If "type" above is object
      "item": {                        # If "type" above is map,array
        "type": "TYPE", ?              # map value type, or array type
        "namecharset": "STRING", ?     # If this item "type" is object
        "attributes": { ... }, ?       # If this item "type" is object
        "item": { ... } ?              # If this item "type" is map,array
      } ?

      "ifvalues": {                    # If "type" is scalar
        "VALUE": {
          "siblingattributes": { ... } # Siblings to this "attribute"
        } *
      } ?
    } *
  },

  "groups": {
    "STRING": {                        # Key=plural name, e.g. "endpoints"
      "plural": "STRING",              # e.g. "endpoints"
      "singular": "STRING",            # e.g. "endpoint"
      "description": "STRING", ?
      "modelversion": "STRING", ?      # Version of the group model
      "compatiblewith": "URI", ?       # Statement of compatibility with model spec
      "labels": { "STRING": "STRING" * }, ?
      "attributes": { ... }, ?         # See "attributes" above

      "resources": {
        "STRING": {                    # Key=plural name, e.g. "messages"
          "plural": "STRING",          # e.g. "messages"
          "singular": "STRING",        # e.g. "message"
          "description": "STRING", ?
          "maxversions": UINTEGER, ?   # Num Vers(>=0). Default=0, 0=unlimited
          "setversionid": BOOLEAN, ?   # vid settable? Default=true
          "setdefaultversionsticky": BOOLEAN, ? # sticky settable? Default=true
          "hasdocument": BOOLEAN, ?     # Has separate document. Default=true
          "singleversionroot": BOOLEAN, ? # enforce single root. Default=false
          "typemap": MAP, ?             # contenttype mappings
          "modelversion": "STRING", ?   # Version of the resource model
          "compatiblewith": "URI"`, ?   # Statement of compatibility with model spec
          "labels": { "STRING": "STRING" * }, ?
          "attributes": { ... }, ?      # Version attributes/extensions
          "metaattributes": { ... } ?   # Resource attributes/extensions
        } *
      } ?
    } *
  } ?
}

The following describes the attributes of Registry model:

Retrieving the Registry Model

To retrieve the Registry Model as a stand-alone entity, an HTTP GET MAY be used.

Registries MAY support exposing the model in a variety of well-defined schema formats. The schemas capabilities attribute MUST expose the set of schema formats available.

The resulting schema document MUST include the full Registry model - meaning all specification-defined attributes, extension attributes, Group types, and Resource types.

For the sake of brevity, this specification doesn't include the full definition of the specification-defined attributes as part of the snippets of output. However, the full model definition of the Registry level attributes can be found in model.json, and the Group and Resource level attributes can be found in this sample sample-model.json.

The request MUST be of the form:

GET /model[?schema=NAME[/VERSION]]

Where:

Implementations of this specification MUST support xRegistry-json/1.0-rc1.

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: ...

... xRegistry model in a schema specific format ...

Where:

If the specified schema format is not supported then an error (invalid_data) MUST be generated.

When the schema is xRegistry-json/1.0-rc1 then the response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "labels": { "STRING": "STRING" * }, ?
  "attributes": {
    "STRING": {
      "name": "STRING",
      "type": "TYPE",
      "target": "STRING", ?
      "namecharset": "STRING", ?
      "description": "STRING", ?
      "enum": [ VALUE * ], ?
      "strict": BOOLEAN, ?
      "readonly": BOOLEAN, ?
      "immutable": BOOLEAN, ?
      "required": BOOLEAN, ?
      "default": VALUE, ?

      "attributes": { ... }, ?
      "item": { ... }, ?

      "ifvalues": {
        "VALUE": {
          "siblingattributes": { ... }
        } *
      } ?
    } *
  },

  "groups": {
    "STRING": {
      "plural": "STRING",
      "singular": "STRING",
      "description": "STRING", ?
      "modelversion": "STRING", ?
      "compatiblewith": "URI", ?
      "labels": { "STRING": "STRING" * }, ?
      "attributes": { ... }, ?

      "resources": {
        "STRING": {
          "plural": "STRING",
          "singular": "STRING",
          "description": "STRING", ?
          "maxversions": UINTEGER, ?
          "setversionid": BOOLEAN, ?
          "setdefaultversionsticky": BOOLEAN, ?
          "hasdocument": BOOLEAN, ?
          "singleversionroot": BOOLEAN, ?
          "typemap": MAP, ?
          "modelversion": "STRING", ?
          "compatiblewith": "URI", ?
          "labels": { "STRING": "STRING" * }, ?
          "attributes": { ... }, ?
          "metaattributes": { ... } ?
        } *
      } ?
    } *
  } ?
}

Examples:

Retrieve a Registry model that has one extension attribute on the endpoints Group, and supports returning the schema of the Registry as JSON Schema:

GET /model
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "attributes": {
    ... xRegistry spec-defined attributes excluded for brevity ...
  },
  "groups": {
    "endpoints": {
      "plural": "endpoints",
      "singular": "endpoint",
      "attributes": {
        ... xRegistry spec-defined attributes excluded for brevity ...
        "shared": {
          "name": "shared",
          "type": "boolean"
        }
      },

      "resources": {
        "messages": {
          "plural": "messages",
          "singular": "message",
            ... xRegistry spec-defined attributes excluded for brevity ...
            "*": {
              type: "any"
            }
          },
          "metaattributes": {
            ... xRegistry spec-defined attributes excluded for brevity ...
            "*": {
              type: "any"
            }
          }
        }
      }
    }
  }
}

Updating the Registry Model

To update the Registry model, the new full representation of the model MAY be included on an HTTP PUT to the Registry Entity in the model attribute, or a PUT MAY be done to the /model API. Note that PATCH is not supported via the /model API.

While the remainder of this section is presented within the scope of the /model API, the processing rules of the model definition MUST also apply when it is updated via the model attribute on the Registry entity.

The request MUST be of the form:

PUT /model
Content-Type: application/json; charset=utf-8

{
  "labels": { "STRING": "STRING" * }, ?
  "attributes": {
    "STRING": {
      "name": "STRING",
      "type": "TYPE",
      "target": "STRING", ?
      "namecharset": "STRING", ?
      "description": "STRING", ?
      "enum": [ VALUE * ], ?
      "strict": BOOLEAN, ?
      "readonly": BOOLEAN, ?
      "immutable": BOOLEAN, ?
      "required": BOOLEAN, ?
      "default": VALUE, ?

      "attributes": { ... }, ?               # For nested object
      "item": { ... }, ?                     # For nested map, array

      "ifvalues": {
        "VALUE": {
          "siblingattributes": { ... }
        } *
      } ?
    } *
  },

  "groups": {
    "STRING": {
      "plural": "STRING",
      "singular": "STRING",
      "description": "STRING", ?
      "modelversion": "STRING", ?
      "compatiblewith": "URI", ?
      "labels": { "STRING": "STRING" * }, ?
      "attributes": { ... }, ?               # See "attributes" above

      "resources": {
        "STRING": {
          "plural": "STRING",
          "singular": "STRING",
          "description": "STRING", ?
          "maxversions": UINTEGER, ?
          "setversionid": BOOLEAN, ?
          "setdefaultversionsticky": BOOLEAN, ?
          "hasdocument": BOOLEAN, ?
          "singleversionroot": BOOLEAN, ?
          "typemap": MAP, ?
          "modelversion": "STRING", ?
          "compatiblewith": "URI", ?
          "labels": { "STRING": "STRING" * }, ?
          "attributes": { ... }, ?           # Version attributes/extensions
          "metaattributes": { ... } ?        # Resource attributes/extensions
        } *
      } ?
    } *
  } ?
}

Where:

See Registry Model for more details about the list of allowable changes to a model.

A successful response MUST include a full representation of the Registry model and be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "labels": { "STRING": "STRING" * }, ?
  "attributes": {
    "STRING": {
      "name": "STRING",
      "type": "TYPE",
      "target": "STRING", ?
      "namecharset": "STRING", ?
      "description": "STRING", ?
      "enum": [ VALUE * ], ?
      "strict": BOOLEAN, ?
      "readonly": BOOLEAN, ?
      "immutable": BOOLEAN, ?
      "required": BOOLEAN, ?
      "default": VALUE, ?

      "attributes": { ... }, ?
      "item": { ... }, ?

      "ifvalues": {
        "VALUE": {
          "siblingattributes": { ... }
        } *
      } ?
    } *
  },

  "groups": {
    "STRING": {
      "plural": "STRING",
      "singular": "STRING",
      "description": "STRING", ?
      "modelversion": "STRING", ?
      "compatiblewith": "URI", ?
      "labels": { "STRING": "STRING" * }, ?
      "attributes": { ... }, ?

      "resources": {
        "STRING": {
          "plural": "STRING",
          "singular": "STRING",
          "description": "STRING", ?
          "maxversions": UINTEGER, ?
          "setversionid": BOOLEAN, ?
          "setdefaultversionsticky": BOOLEAN, ?
          "hasdocument": BOOLEAN, ?
          "singleversionroot": BOOLEAN, ?
          "typemap": MAP, ?
          "modelversion": "STRING", ?
          "compatiblewith": "URI", ?
          "labels": { "STRING": "STRING" * }, ?
          "attributes": { ... }, ?
          "metaattributes": { ... } ?
        } *
      } ?
    } *
  } ?
}

Examples:

Update a Registry's model to add a new Group type:

PUT /model
Content-Type: application/json; charset=utf-8

{
  "groups": {
    "endpoints": {
      "plural": "endpoints",
      "singular": "endpoint",
      "attributes": {
        "shared": {
          "name": "shared",
          "type": "boolean"
        }
      },

      "resources": {
        "messages": {
          "plural": "messages",
          "singular": "message",
          "attributes": {
            "*": {
              type: "any"
            }
          }
        }
      }
    },
    "schemagroups": {
      "plural": "schemagroups",
      "singular": "schemagroup",

      "resources": {
        "schemas": {
          "plural": "schemas",
          "singular": "schema"
        }
      }
    }
  }
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "attributes": {
    ... xRegistry spec-defined attributes excluded for brevity ...
  },
  "groups": {
    "endpoints" {
      "plural": "endpoints",
      "singular": "endpoint",
      "attributes": {
        ... xRegistry spec-defined attributes excluded for brevity ...
        "shared": {
          "name": "shared",
          "type": "boolean"
        }
      },

      "resources": {
        "messages": {
          "plural": "messages",
          "singular": "message",
          ... xRegistry spec-defined attributes excluded for brevity ...
          "attributes": {
            "*": {
              type: "any"
            }
          }
          "maxversions": 1
        }
      }
    },
    "schemagroups": {
      "plural": "schemagroups",
      "singular": "schemagroup",

      "resources": {
        "schemas": {
          "plural": "schemas",
          "singular": "schema"
          ... xRegistry spec-defined attributes excluded for brevity ...
        }
      }
    }
  }
}
Reuse of Resource Definitions

When a Resource type definition is to be shared between Groups, rather than creating a duplicate Resource definition, the ximport mechanism MAY be used instead. When defining the Resources of a Group, a special Resource "plural" name MAY be used to reference other Resource definitions from within the same Registry. For example, the following abbreviated model definition defines one Resource type (messages) under the messagegroups Group, that is also used by the endpoints Group.

"model": {
  "groups": {
    "messagegroups": {
      "plural": "messagegroups",
      "singular": "messagegroup",
      "resources": {
        "messages": {
          "plural": "messages",
          "singular": "message"
        }
      }
    },
    "endpoints": {
      "plural": "endpoints",
      "singular": "endpoint",
      "resources": {
        "ximport": [ "messagegroups/messages" ]
      }
    }
  }
}

The format of the ximport specification is:

"ximport": [ "GROUPS/RESOURCES", * ]

where:

Since the resources attribute is a map, use of the ximport feature MUST only be used once per Group definition.

Additional locally defined Resources MAY be defined within a Group that uses the ximport feature, however, Resource plural and singular values MUST be unique across all imported and locally defined Resources. Locally defined Resources MUST NOT use ximport as a plural or singular name.

See Cross Referencing Resources for more additional information.

Includes in the xRegistry Model Data

There might be times when it is necessary for an xRegistry model to reuse portions of another xRegistry model defined elsewhere. Rather than forcing the duplication of the model definitions, an "include" type of JSON directive MAY be used.

The general formats of the include are:

"$include": "PATH-TO-DOCUMENT#JSON-POINTER-IN-DOC"

or

"$includes": [ "PATH-TO-DOCUMENT#JSON-POINTER-IN-DOC" * ]

where the first form specifies a single reference to be included, and the second form specifies multiple. The fragment (#...) portion is OPTIONAL.

For example:

"$include": "http://example.com/xreg-model.json#/groups/mygroup/attributes"

is asking for the attributes of a GROUP called mygroup to be included at this location of the current model definition.

These directives MAY be used in any JSON Object or Map entity in an xRegistry model definition. The following rules apply for how to process the include directive:

When the directives are used in a request to update the model, the server MUST resolve all includes prior to processing the request and MUST return the expanded model in response to the request for the model. The includes MUST NOT be processed again at a later time. A request to re-evaluate the includes can be done via a subsequent model update operation. Note, this means that when the model is subsequently retrieved (e.g. via an HTTP GET) the include directives MUST NOT be in the response.

Examples:

A model definition that includes xRegistry attributes from a file on a remote server, and adds the definition of one attribute to a GROUP named mygroups from an external Group named group1 in another xRegistry.

{
  "attributes": {
    "$include": "http://example.com/someattributes",
    "myattribute": {
      "name": "myattribute",
      "type": "string"
    }
  }
  "groups": {
    "mygroups": {
      "plural": "mygroups",
      "singular": "mygroup",
      "attributes": {
        "attr1": {
          "$include": "http://example.com/model#/groups/group1/attributes/attr1"
        }
        ... remainder of model excluded for brevity ...
      }
    }
  }
}

where http://example.com/someattributes might look like:

{
  "myattr": {
    "name": "myattr",
    "type": "string"
  }
}

and the second include target might look like:

{
  "name": "attr1",
  "type": "string"
}

Exporting

The /export API MUST be an alias for GET /?doc&inline=*,model,capabilities". If supported, it MUST only support the GET` HTTP method. This API was created:

Query parameters MAY be included on the request and any ?inline flag specified MUST override the default value defined above.


Groups

Groups represent entities that typically act as a collection mechanism for related Resources. However, it is worth noting that Groups do not have to have Resources associated with them. It is possible to have Groups be the main (or only) entity of a Registry. Each Group type MAY have any number of Resource types within it. This specification does not define how the Resources within a Group type are related to each other.

The serialization of a Group entity adheres to this form:

{
  "GROUPid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",

  # Repeat for each Resource type in the Group
  "RESOURCESurl": "URL",                    # e.g. "messagesurl"
  "RESOURCEScount": UINTEGER,               # e.g. "messagescount"
  "RESOURCES": { RESOURCES collection } ?   # If inlined
}

Groups include the following common attributes:

and the following Group level attributes:

RESOURCES Collections

Retrieving a Group Collection

To retrieve a Group collection, an HTTP GET MAY be used.

The request MUST be of the form:

GET /GROUPS

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <URL>;rel=next;count=UINTEGER ?

{
  "KEY": {                                     # GROUPid
    "GROUPid": "STRING",
    "self": "URL",
    "shortself": "URL", ?
    "xid": "XID",
    "epoch": UINTEGER,
    "name": "STRING", ?
    "description": "STRING", ?
    "documentation": "URL", ?
    "labels": { "STRING": "STRING" * }, ?
    "createdat": "TIMESTAMP",
    "modifiedat": "TIMESTAMP",

    # Repeat for each Resource type in the Group
    "RESOURCESurl": "URL",                    # e.g. "messagesurl"
    "RESOURCEScount": UINTEGER,               # e.g. "messagescount"
    "RESOURCES": { RESOURCES collection } ?   # If inlined
  } *
}

Examples:

Retrieve all entities in the endpoints Group:

GET /endpoints
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <https://example.com/endpoints&page=2>;rel=next;count=100

{
  "ep1": {
    "endpointid": "ep1",
    "self": "https://example.com/endpoints/ep1",
    "xid": "/endpoints/ep1",
    "epoch": 1,
    "name": "A cool endpoint",
    "createdat": "2024-04-30T12:00:00Z",
    "modifiedat": "2024-04-30T12:00:01Z",

    "messagesurl": "https://example.com/endpoints/ep1/messages",
    "messagescount": 5
  },
  "ep2": {
    "endpointid": "ep2",
    "self": "https://example.com/endpoints/ep2",
    "xid": "/endpoints/ep2",
    "epoch": 3,
    "name": "Redis Queue",
    "createdat": "2024-04-30T12:00:00Z",
    "modifiedat": "2024-04-30T12:00:01Z",

    "messagesurl": "https://example.com/endpoints/ep2/messages",
    "messagescount": 1
  }
}

Notice that the Link HTTP header is present, indicating that there is a second page of results that can be retrieved via the specified URL, and that there are total of 100 items in this collection.

Creating or Updating Groups

Creating or updating Groups via HTTP MAY be done by using the HTTP PUT, PATCH or POST methods:

The processing of the above APIs is defined in the Creating or Updating Entities section.

This API is very similar to the POST /GROUPS above except that the HTTP body MUST be a map of Group types as shown below:

{
  "endpoints": {
    "endpoint1": { ... Group endpoint1's xRegistry metadata ... },
    "endpoint2": { ... Group endpoint2's xRegistry metadata ... }
  },
  "schemagroups": {
    "schemagroup1": { ... Group schemagroup1's xRegistry metadata ... },
    "schemagroup2": { ... Group schemagroup2's xRegistry metadata ... }
  }
}

Notice the format is almost the same as what a PUT / would look like if the request wanted to update the Registry's attributes and define a set of Groups, but without the Registry's attributes. This allows for an update of the specified Groups without modifying the Registry's attributes.

The response in this case MUST be a map of the Group types with just the Groups that were processed as part of the request.

Each individual Group definition MUST adhere to the following:

{
  "GROUPid": "STRING", ?
  "self": "URL", ?
  "shortself": "URL", ?
  "xid": "XID", ?
  "epoch": UINTEGER, ?
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP", ?
  "modifiedat": "TIMESTAMP", ?

  # Repeat for each Resource type in the Group
  "RESOURCESurl": "URL",                     # e.g. "messagesurl"
  "RESOURCEScount": UINTEGER,                # e.g. "messagescount"
  "RESOURCES": { RESOURCES collection } ?
}

Each individual Group in a successful response MUST adhere to the following:

{
  "GROUPid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",

  # Repeat for each Resource type in the Group
  "RESOURCESurl": "URL",                    # e.g. "messagesurl"
  "RESOURCEScount": UINTEGER                # e.g. "messagescount"
}

Examples:

Targeted request to create a specific Group by GROUPid:

PUT /endpoints/ep1
Content-Type: application/json; charset=utf-8

{
  "endpointid": "ep1",
  ... remainder of Endpoint 'ep1' definition ...
}
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: https://example.com/endpoints/ep1

{
  "endpointid": "ep1",
  ... remainder of Endpoint 'ep1' definition ...
}

Multiple Groups specified in the HTTP body:

POST /endpoints
Content-Type: application/json; charset=utf-8

{
  "ep1": {
    "endpointid": "ep1",
    ... remainder of ep1 definition ...
  },
  "ep2": {
    "endpointid": "ep2",
    ... remainder of ep2 definition ...
  }
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "ep1": {
    "endpointid": "ep1",
    ... remainder of ep1 definition ...
  },
  "ep2": {
    "endpointid": "ep2",
    ... remainder of ep2 definition ...
  }
}

Retrieving a Group

To retrieve a Group, an HTTP GET MAY be used.

The request MUST be of the form:

GET /GROUPS/gID

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "GROUPid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",

  # Repeat for each Resource type in the Group
  "RESOURCESurl": "URL",                     # e.g. "messagesurl"
  "RESOURCEScount": UINTEGER,                # e.g. "messagescount"
  "RESOURCES": { RESOURCES collection } ?    # If inlined
}

Examples:

Retrieve a single endpoints Group:

GET /endpoints/ep1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "GROUPid": "ep1",
  "self": "https://example.com/endpoints/ep1",
  "xid": "/endpoints/ep1",
  "epoch": 1,
  "name": "myEndpoint",
  "createdat": "2024-04-30T12:00:00Z",
  "modifiedat": "2024-04-30T12:00:01Z",

  "messagesurl": "https://example.com/endpoints/ep1/messages",
  "messagescount": 5
}

Deleting Groups

To delete one or more Groups, an HTTP DELETE MAY be used:

The processing of these two APIs is defined in the Deleting Entities in a Registry Collection section.


Resources

Resources typically represent the main entity that the Registry is managing. Each Resource is associated with a Group to aid in their discovery and to show a relationship with Resources in that same Group. Resources appear within the Group's RESOURCES collection.

Resources, like all entities in the Registry, can be modified but Resources can also have a version history associated with them, allowing for users to retrieve previous Versions of the Resource. In this respect, Resources have a 2-layered definition. The first layer is the Resource entity itself, and the second layer is its versions collection - the version history of the Resource.

The Resource entity serves three purposes: 1 - It represents the collection for the historical Versions of the data being managed. This is true even if the Resource type is defined to not use versioning, meaning the number of Versions allowed is just one. The Versions will appear as a nested entity under the versions attribute.
2 - It is an alias for the "default" Version of the Resource. And most operations directed at the URL of the Resource will act upon that Version, not the Resource itself. See Default Version of a Resource and Versions for more details.
3 - It has a set of attributes for Resource level metadata - data that is not specific to one Version of the Resource but instead applies to the Resource in general. These attributes appear under a meta attribute/sub-object so as to keep them separate from any Version level attributes. Note that these attributes do not appear on the Versions.

The URL of a Resource can be thought of as an alias for the "default" Version of the Resource, and as such, most of the attributes shown when processing the Resource will be mapped from the "default" Version.

However, there a few exceptions:

The remainder of this section discusses the processing rules for Resources and Versions. While it mainly uses the term "Resource" for ease of reading, in most cases it can be assumed that the same applies for "Versions". When this is not the case, it will be explicitly called out.

Resource Metadata vs Resource Document

Unlike Groups, which consist entirely of xRegistry managed metadata, Resources typically have their own domain-specific data and document format that needs to be kept distinct from the xRegistry Resource metadata. As discussed previously, the model definition for Resource has a hasdocument attribute indicating whether a Resource type defines its own separate document or not.

This specification does not define any requirements for the contents of this separate document, and it doesn't even need to be stored within the Registry. The Resource MAY choose to simply store a URL reference to the externally managed document instead. When the document is stored within the Registry, it can be managed as an opaque array of bytes.

When a Resource does have a separate document, HTTP interactions to the URL for the Resource MUST include this document in the HTTP body as it is typically the data of interest for end users. As a convenance, the simple (mainly scalar) xRegistry metadata of the Resource will appear as HTTP headers.

To change this view such that the xRegistry metadata becomes the data of interest, the request URLs MUST have $details appended to them. In these cases, the HTTP body of the requests and responses MUST have a JSON serialization of the entity's xRegistry metadata, and the separate document MAY appear as an attribute within that metadata based on the specific operation being done.

For example:

GET https://example.com/schemagroups/mygroup/schemas/myschema

will retrieve the schema document associated with the myschema Resource, while:

GET https://example.com/schemagroups/mygroup/schemas/myschema$details

will retrieve the xRegistry metadata information for the myschema Resource.

When the Resource's path is appended with $details, the Resource's document becomes available via a set of RESOURCE* attributes within that metadata:

When accessing a Resource's metadata with $details, often it is to view or update the xRegistry metadata and not the document, as such, including the potentially large amount of data from the Resource's document in request and response messages could be cumbersome. To address this, the RESOURCE and RESOURCEbase64 attributes do not appear by default as part of the serialization of the Resource. Rather, they MUST only appear in responses when the ?inline=RESOURCE query parameter is used. Likewise, in requests, these attributes are OPTIONAL and would only need to be used when a change to the document's content is needed at the same time as updates to the Resource's metadata. However, the RESOURCEurl attribute MUST always appear if it has a value.

Note that the serialization of a Resource MUST only use at most one of these 3 attributes at a time.

Resource Attributes

Resource attributes are non-versioned attributes associated with a Resource. In a sense they can be considered to be global to the Resource and its Versions, but they are not part of, or serialized in, any Version. Instead, they are serialized in two different ways:

1 - some will appear within a meta attribute/sub-object to the Resource. This keeps them separate from the default Version attributes that might appear. However, the meta attribute itself will appear as a sibling to the default Version attributes. Note that meta will only be serialized when requested by the client.

2 - some will appear as siblings to the default Version attributes within the Resource serialization. These appear here, rather than under meta, because they are specifically designed to help with the traversal of the Resource's hierarchy and putting them "one level down" would reduce their usefulness.

When the Resource is serialized as a JSON object, the serialization of the Resource attribute MUST adhere to the following:

{
  "RESOURCEid": "STRING",
  "versionid": "STRING",
  "self": "URL",                           # URL to Resource, not Version
  "shortself": "URL", ?
  "xid": "XID",                            # Relative URI to Resource
  # Default Version attributes appear here

  "metaurl": "URL",
  "meta": {                                # Only if inlined
    "RESOURCEid": "STRING",
    "self": "URL",                         # Absolute Meta URL, not Version
    "shortself": "URL", ?
    "xid": "XID",                          # Relative Meta URI, not Version
    "xref": "URL", ?                       # Ptr to linked Resource
    "epoch": UINTEGER,                     # Resource's epoch
    "createdat": "TIMESTAMP",              # Resource's
    "modifiedat": "TIMESTAMP",             # Resource's
    "readonly": BOOLEAN,                   # Default=false
    "compatibility": "STRING",             # Default=none
    "compatibilityauthority": "STRING", ?  # Default=external
    "deprecated": { ... }, ?

    "defaultversionid": "STRING",
    "defaultversionurl": "URL",
    "defaultversionsticky": BOOLEAN        # Default=false
  }, ?
  "versionsurl": "URL",
  "versionscount": UINTEGER,
  "versions": { map of Versions }          # Only if inlined
}

Note that the meta and versions attributes MUST only appear when requested by the client - for example, via the ?inline flag.

When the Resource is serialized with its domain-specific document in the HTTP body, then Resource level attributes SHOULD appear as HTTP headers and adhere to the following:

xRegistry-RESOURCEid: STRING
xRegistry-versionid: STRING
xRegistry-self: URL
xRegistry-xid: URI
# Default Version attributes, and other HTTP headers, appear here
xRegistry-metaurl: URL
xRegistry-versionsurl: URL
xRegistry-versionscount: UINTEGER

Notice the meta and versions attributes are not included since they are not complex data types.

The Resource level attributes include the following common attributes:

and the following Resource level attributes:

xref Attribute
readonly Attribute
compatibility Attribute

compatibilityauthority Attribute

deprecated

defaultversionid Attribute
defaultversionurl Attribute
defaultversionsticky Attribute
meta Attribute/Sub-Object

Note: doing a PUT to a Resource, or a POST to an xRegistry Collection, as a mechanism to update the meta sub-object MUST include the Resource default Version attributes in the request. When not specified, the server will interpret it as a request to delete the default Version attributes. If possible, an update request to the metaurl directly would be a better choice, or use PATCH instead and only include the meta sub-object.

During a write operation, the absence of the meta attribute indicates that no changes are to be made to the meta sub-object.

metaurl Attribute
versions Collection

Serializing Resources

Serializing Resources requires some special processing due to Resources not representing just a single set of data. In particular, the following aspects need to be taken into account:

To address these aspects, the serialization of a Resource will vary based on whether it is defined to have a domain-specific document and whether the client wishes to focus on managing its xRegistry metadata or that secondary document.

As discussed above, there are two ways to serialize a Resource in an HTTP message's body:

Which variant is used is controlled by the use of $details on the URL path. The following sections go into more details about these two serialization options.

Serializing Resource Documents

When a Resource is serialized as its underlying domain-specific document, in other words $details is not appended to its URL path, the HTTP body of requests and responses MUST be the exact bytes of the document. If the document is empty, or there is no document, then the HTTP body MUST be empty (zero length).

In this serialization mode, it might be useful for clients to have access to Resource's xRegistry metadata. To support this, some of the Resource's xRegistry metadata will appear as HTTP headers in response messages.

On responses, unless otherwise stated, all top-level scalar attributes of the Resource SHOULD appear as HTTP headers where the header name is the name of the attribute prefixed with xRegistry-. Note, the optionality of this requirement is not to allow for servers to decide whether or not to do so, rather it is to allow for No-Code Servers servers than might not be able to control the HTTP response headers.

Certain attributes do not follow this rule if a standard HTTP header name is to be used instead (e.g. contenttype MUST use Content-Type, not xRegistry-contenttype). Each attribute that falls into this category will be identified as part of its definition.

Top-level map attributes whose values are of scalar types MUST also appear as HTTP headers (each key having its own HTTP header) and in those cases the HTTP header names will be of the form: xRegistry-ATTRIBUTENAME-KEYNAME. Note that map keys MAY contain the - character, so any - after the 2nd - is part of the key name. See HTTP Header Values for additional information and labels for an example of one such attribute.

Complex top-level attributes (e.g. arrays, objects, non-scalar maps) MUST NOT appear as HTTP headers.

On update requests, similar serialization rules apply. However, rather than these headers being REQUIRED, the client would only need to include those top-level attributes that they would like to change. But, including unchanged attributes MAY be done. Any attributes not included in request messages MUST be interpreted as a request to leave their values unchanged. Using a value of null (case-sensitive) indicates a request to delete that attribute.

Any top-level map attributes that appear as HTTP headers MUST be included in their entirety and any missing keys MUST be interpreted as a request to delete those keys from the map.

Since only some types of attributes can appear as HTTP headers, $details MUST be used to manage the others. See the next section for more details.

When a Resource (not a Version) is serialized with the Resource document in the HTTP body, it MUST adhere to this form:

Content-Type: STRING ?
xRegistry-RESOURCEid: STRING               # ID of Resource, not default Version
xRegistry-versionid: STRING                # ID of the default Version
xRegistry-self: URL                        # Resource URL, not default Version
xRegistry-xid: URI                         # Relative Resource URI
xRegistry-epoch: UINT                      # Start default Version's attributes
xRegistry-name: STRING ?
xRegistry-isdefault: true
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-ancestor: STRING
xRegistry-RESOURCEurl: URL ?               # End of default Version attributes
xRegistry-metaurl: URL                     # Resource level attributes
xRegistry-versionsurl: URL
xRegistry-versionscount: UINTEGER
Location: URL
Content-Location: URL ?
Content-Disposition: STRING ?

... Resource document ... ?

Where:

Version serialization will look similar, but the set of xRegistry HTTP headers will be slightly different (to exclude Resource level attributes). See the next sections for more information.

Scalar default Version extension attributes MUST also appear as xRegistry- HTTP headers.

Serializing Resource Metadata

Appending $details to a Resource or Version's URL path modifies the serialization of the entity such that rather than the HTTP body containing the entity's domain-specific "document" and the xRegistry metadata being in HTTP headers, all of them are instead within the HTTP body as one JSON object. If the entity's "document" is included within the object then it'll appear under a RESOURCE* attribute (as discussed above).

The advantage of this format is that the HTTP body will contain all of the xRegistry metadata and not just the scalar values - as is the case when they appear as HTTP headers. This allows for management of all metadata as well as any possible domain-specific document at one time.

Note that in the case of a reference to a Resource (not a Version), the metadata will be from the default Version, plus the extra meta and versions related attributes.

When serialized as a JSON object, a Resource (not a Version) MUST adhere to this form:

{
  "RESOURCEid": "STRING",                  # ID of Resource, not default Version
  "versionid": "STRING",                   # ID of default Version
  "self": "URL",                           # URL of Resource,not default Version
  "shortself": "URL", ?
  "xid": "XID",                            # Relative URI of Resource
  # These are inherited from the default Version
  "epoch": UINTEGER,
  "name": "STRING", ?
  "isdefault": true,
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",
  "ancestor": "STRING",
  "contenttype": "STRING", ?

  "RESOURCEurl": "URL", ?                  # If not local
  "RESOURCE": ... Resource document ..., ? # If inlined & JSON
  "RESOURCEbase64": "STRING", ?            # If inlined & ~JSON

  # Resource level helper attributes
  "metaurl": "URL",
  "meta": {                                # If inlined
    "RESOURCEid": "STRING",
    "self": "URL",                         # URL to "meta"
    "shortself": "URL", ?
    "xid": "XID",                          # Relative URI to "meta"
    "xref": "URL", ?                       # Ptr to linked Resource
    "epoch": UINTEGER,                     # Resource's epoch
    "createdat": "TIMESTAMP",              # Resource's
    "modifiedat": "TIMESTAMP",             # Resource's
    "readonly": BOOLEAN,                   # Default=false
    "compatibility": "STRING",             # Default=none
    "compatibilityauthority": "STRING", ?  # Default=external
    "deprecated": { ... },

    "defaultversionid": "STRING",
    "defaultversionurl": "URL",
    "defaultversionsticky": BOOLEAN        # Default=false
  }, ?
  "versionsurl": "URL",
  "versionscount": UINTEGER,
  "versions": { Versions collection } ?    # If inlined
}

The serialization of a Version will look similar except the meta and versions related Resource level attributes MUST NOT be present. More on this in the next sections.

Cross Referencing Resources

Typically, Resources exist within the scope of a single Group, however there might be situations where a Resource needs to be related to multiple Groups. In these cases, there are two options. First, a copy of the Resource could be made into the second Group. The obvious downside to this is that there's no relationship between the two Resources and any changes to one would need to be done in the other - running the risk of them getting out of sync.

The second, and better, option is to create a cross-reference from one (the "source" Resource) to the other ("target" Resource). This is done by setting the xref attribute on the source Resource to be the xid of the target Resource.

The xref attribute is defined in the model as:

"xref": {
  "name": "xref",
  "type": "xid",
  "target": "/GROUPS/RESOURCES"
}

where /GROUPS/RESOURCES will be the actual Group and Resource plural names of this Resource.

Looking at a specific example, a Group/Resource model definition of:

{
  "groups" : {
    "schemagroups": {
      "plural": "schemagroups",
      "singular": "schemagroup",

      "resources": {
        "plural": "schemas",
        "singular": "schema",
        "attributes": {
          "xref": {
            "name": "xref",
            "type": "xid",
            "target": "/schemagroups/schemas"
          }
        }
      }
    }
  }
}

Means that schemas can be cross-referenced to other /schemagroups/schema Resource. Notice that target is a xid template to itself.

For example: a schema Resource instance defined as (HTTP body of PUT /schemagroups/group1/schemas/mySchema$details):

{
  "schemaid": "mySchema",
  "meta": {
    "xref": "/schemagroups/group2/schemas/sharedSchema"
  }
}

means that mySchema references sharedSchema, which exists in group2. When this source Resource (mySchema) is retrieved, all of the target Resource's attributes (except its RESOURCEid) will appear as if they were locally defined.

So, if the target Resource (sharedSchema) is defined as:

{
  "resourceid": "sharedSchema",
  "versionid": "v1",
  "self": "http://example.com/schemagroups/group2/schemas/sharedSchema",
  "xid": "/schemagroups/group2/schemas/sharedSchema",
  "epoch": 2,
  "isdefault": true,
  "createdat": "2024-01-01-T12:00:00",
  "modifiedat": "2024-01-01-T12:01:00",
  "ancestor": "v1",

  "metaurl": "http://example.com/schemagroups/group2/schemas/sharedSchema/meta",
  "versionscount": 1,
  "versionsurl": "http://example.com/schemagroups/group2/schemas/sharedSchema/versions"
}

then the resulting serialization of the source Resource would be:

{
  "resourceid": "mySchema",
  "versionid": "v1",
  "self": "http://example.com/schemagroups/group1/schemas/mySchema",
  "xid": "/schemagroups/group1/schemas/mySchema",
  "epoch": 2,
  "isdefault": true,
  "createdat": "2024-01-01-T12:00:00",
  "modifiedat": "2024-01-01-T12:01:00",
  "ancestor": "v1",

  "metaurl": "http://example.com/schemagroups/group1/schemas/mySchema/meta",
  "meta": {
    "resourceid": "mySchema",
    "self": "http://example.com/schemagroups/group1/schemas/mySchema/meta",
    "xid": "/schemagroups/group1/schemas/mySchema/meta",
    "xref": "/schemagroups/group2/schemas/sharedSchema",
    "createdat": "2024-01-01-T12:00:00",
    "modifiedat": "2024-01-01-T12:01:00",
    "readonly": false,
    "compatibility": "none"
  },
  "versionscount": 1,
  "versionsurl": "http://example.com/schemagroups/group1/schemas/mySchema/versions"
}

Note:

From a consumption (read) perspective, aside from the presence of the xref attribute, the Resource appears to be a normal Resource that exists within group1. All of the specification-defined features (e.g. ?inline, ?filter) MAY be used when retrieving the Resource.

However, from a write perspective it is quite different. In order to update the target Resource's attributes (or nested entities), a write operation MUST be done on the appropriate target Resource entity directly. Write operations on the source MAY be done, however, the changes are limited to converting it from a "cross-reference" Resource back into a "normal" Resource. See the following for more information:

When converting a "normal" Resource into a cross-reference Resource (adding an xref value), or creating a new Resource that will be a cross-reference Resource, the following MUST be adhered to:

When converting a cross-reference Resource back into a "normal" Resource, the following MUST be adhered to:

If the target Resource itself is a cross-reference Resource, then including the target Resource's attributes MUST NOT be done when serializing the source Resource. Recursive, or transitively, following of xref URLs is not done.

Both the source and target Resources MUST be of the same Resource model type, simply having similar Resource type definitions is not sufficient. This implies that use of the ximport feature in the model to reference a Resource type from another Group type definition MUST be used if the same Resource type is to exist in different Group types. See ximport for more information.

An xref value that points to a non-existing Resource, either because it was deleted or never existed, is not an error and is not a condition that a server is REQUIRED to detect. In these "dangling xref" situations, the serialization of the source Resource will not include any target Resource attributes or nested collections. Rather, it will only show the RESOURCEid and xref attributes.


Resource and Version APIs

For convenience, the Resource and Version create, update and delete APIs can be summarized as:

POST /GROUPS/gID

POST /GROUPS/gID/RESOURCES
PATCH /GROUPS/gID/RESOURCES

PUT /GROUPS/gID/RESOURCES/rID[$details]
PATCH /GROUPS/gID/RESOURCES/rID[$details]

POST /GROUPS/gID/RESOURCES/rID[$details]
PATCH /GROUPS/gID/RESOURCES/rID[$details]

PUT /GROUPS/gID/RESOURCES/rID/meta
PATCH /GROUPS/gID/RESOURCES/rID/meta

POST /GROUPS/gID/RESOURCES/rID/versions
PATCH /GROUPS/gID/RESOURCES/rID/versions

PUT /GROUPS/gID/RESOURCES/rID/versions/vID[$details]
PATCH /GROUPS/gID/RESOURCES/rID/versions/vID[$details]

And the delete APIs are summarized as:

DELETE /GROUPS/gID/RESOURCES

DELETE /GROUPS/gID/RESOURCES/rID

DELETE /GROUPS/gID/RESOURCES/rID/versions

DELETE /GROUPS/gID/RESOURCES/rID/versions/vID

The following sections go into more detail about each API.


Retrieving a Resource Collection

To retrieve all Resources in a Resource Collection, an HTTP GET MAY be used.

The request MUST be of the form:

GET /GROUPS/gID/RESOURCES

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <URL>;rel=next;count=UINTEGER ?

{
  "KEY": {                                    # The Resource ID
    "RESOURCEid": "STRING",                   # The Resource ID
    "versionid": "STRING",                    # Default Version ID
    "self": "URL",                            # URL to the Resource
    "shortself": "URL", ?
    "xid": "XID",                             # Relative URI to the Resource
    "epoch": UINTEGER,                        # Start of Default Ver attribs
    "name": "STRING", ?
    "isdefault": true,
    "description": "STRING", ?
    "documentation": "URL", ?
    "labels": { "STRING": "STRING" * }, ?
    "createdat": "TIMESTAMP",
    "modifiedat": "TIMESTAMP",
    "ancestor": "STRING",
    "contenttype": "STRING", ?

    "RESOURCEurl": "URL", ?                  # If not local
    "RESOURCE": ... Resource document ..., ? # If inlined & JSON
    "RESOURCEbase64": "STRING", ?            # If inlined & ~JSON

    # Resource level helper attributes
    "metaurl": "URL",
    "meta": {                                # If inlined
      "RESOURCEid": "STRING",                # Resource ID
      "self": "URL",                         # URL to "meta"
      "shortself": "URL", ?
      "xid": "XID",                          # Relative URI to "meta"
      "xref": "URL", ?                       # Ptr to linked Resource
      "epoch": UINTEGER,                     # Resource's epoch
      "createdat": "TIMESTAMP",              # Resource's
      "modifiedat": "TIMESTAMP",             # Resource's
      "readonly": BOOLEAN,                   # Default=false
      "compatibility": "STRING",             # Default=none
      "compatibilityauthority": "STRING", ?  # Default=external
      "deprecated": { ... }, ?

      "defaultversionid": "STRING",
      "defaultversionurl": "URL",
      "defaultversionsticky": BOOLEAN
    }
    "versionsurl": "URL",
    "versionscount": UINTEGER,
    "versions": { Versions collection } ?    # If inlined
  } *
}

Where:

Examples:

Retrieve all messages of an endpoint whose RESOURCEid is ep1:

GET /endpoints/ep1/messages
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <https://example.com/endpoints/ep1/messages&page=2>;rel=next;count=100

{
  "msg1": {
    "messageid": "msg1",
    "versionid": "1.0",
    "self": "https://example.com/endpoints/ep1/messages/msg1",
    "xid": "/endpoints/ep1/messages/msg1",
    "epoch": 1,
    "name": "Blob Created",
    "isdefault": true,
    "createdat": "2024-04-30T12:00:00Z",
    "modifiedat": "2024-04-30T12:00:01Z",
    "ancestor": "1.0",

    "metaurl": "https://example.com/endpoints/ep1/messages/msg1/meta",
    "versionsurl": "https://example.com/endpoints/ep1/messages/msg1/versions",
    "versionscount": 1
  }
}

Creating or Updating Resources and Versions

These APIs follow the overall pattern described in the Creating or Updating Entities section. Any variations will be called out.

Creating and updating of Resources via HTTP MAY be done using the HTTP POST, PUT or PATCH methods as described below:

POST /GROUPS/gID

Where:

For example:

{
  "schemas": {
    "schema1": { ... Resource schema1's xRegistry metadata ... },
    "schema2": { ... Resource schema1's xRegistry metadata ... }
  },
  "messages": {
    "message1": { ... Resource message1's xRegistry metadata ... },
    "message2": { ... Resource message2's xRegistry metadata ... }
  }
}

Notice the format is almost the same as what a PUT /GROUPS/gID would look like if the request wanted to update the Group's attributes and define a set of Resources, but without the Group's attributes. This allows for an update of the specified Resources without modifying the Group's attributes.

The response in this case MUST be a map of the Resource types with just the Resources that were processed as part of the request.

POST /GROUPS/gID/RESOURCES
PATCH /GROUPS/gID/RESOURCES

Where:

PUT /GROUPS/gID/RESOURCES/rID[$details]
PATCH /GROUPS/gID/RESOURCES/rID[$details]

Where:

POST /GROUPS/gID/RESOURCES/rID[$details][?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID[$details][?setdefaultversionid=vID]

Where:

PUT /GROUPS/gID/RESOURCES/rID/meta
PATCH /GROUPS/gID/RESOURCES/rID/meta Where:

POST /GROUPS/gID/RESOURCES/rID/versions[?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID/versions[?setdefaultversionid=vID]

Where:

See Default Version of a Resource for more information about the ?setdefaultversionid query parameter.

PUT /GROUPS/gID/RESOURCES/rID/versions/vID[$details][?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID/versions/vID[$details][?setdefaultversionid=vID]

Where:

See Default Version of a Resource for more information about the ?setdefaultversionid query parameter.


To reduce the number of interactions needed, these APIs are designed to allow for the implicit creation of all parent entities specified in the PATH. And each entity not already present with the specified SINGULARid MUST be created with that value. Note: if any of those entities have REQUIRED attributes, then they cannot be implicitly created, and would need to be created directly.

When specified as an xRegistry JSON object, each individual Resource or Version in the request MUST adhere to the following:

{
  "RESOURCEid": "STRING", ?
  "versionid": "STRING", ?
  "epoch": UINTEGER,
  "name": "STRING", ?                      # Version-level attributes
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP", ?
  "modifiedat": "TIMESTAMP", ?
  "ancestor": "STRING",
  "contenttype": "STRING", ?

  "RESOURCEurl": "URL", ?                  # If not local
  "RESOURCE": ... Resource document ..., ? # If inlined & JSON
  "RESOURCEbase64": "STRING", ?            # If inlined & ~JSON

  "meta": {                                # Resource-only attributes
    "RESOURCEid": "STRING", ?
    "xref": "URL", ?
    "epoch": UINTEGER, ?
    "createdat": "TIMESTAMP", ?
    "modifiedat": "TIMESTAMP", ?
    "compatibility": "STRING", ?
    "compatibilityauthority": "STRING", ?
    "deprecated": { ... }, ?

    "defaultversionid": "STRING",
    "defaultversionsticky": BOOLEAN
  }, ?
  "versions": { Versions collection } ?
}

When the HTTP body contains the Resource's (or Version's) document, then any xRegistry scalar metadata MUST appear as HTTP headers and the request MUST adhere to the following:

[METHOD] [PATH]
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING ?
xRegistry-versionid: STRING ?
xRegistry-epoch: UINTEGER ?
xRegistry-name: STRING ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIMESTAMP ?
xRegistry-modifiedat: TIMESTAMP ?
xRegistry-ancestor: STRING ?
xRegistry-RESOURCEurl: URL ?

... entity document ... ?

Where:

A successful response MUST include the current representation of the entities created or updated and be in the same format ($details variant or not) as the request.

If the request used the PUT or PATCH variants directed at a single entity, and a new Version was created, then a successful response MUST include a Content-Location HTTP header to the newly created Version entity, and if present, it MUST be the same as the Version's self attribute.

Note that the response MUST NOT include any inlinable attributes (such as RESOURCE, RESOURCEbase64 or nested objects/collections).

Examples:

Create a new Resource:

PUT /endpoints/ep1/messages/msg1
Content-Type: application/json; charset=utf-8
xRegistry-name: Blob Created

{
  # Definition of a "Blob Created" event (document) excluded for brevity
}
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
xRegistry-messageid: msg1
xRegistry-versionid: 1.0
xRegistry-self: https://example.com/endpoints/ep1/messages/msg1
xRegistry-xid: /endpoints/ep1/messages/msg1
xRegistry-epoch: 1
xRegistry-name: Blob Created
xRegistry-isdefault: true
xRegistry-metaurl: https://example.com/endpoints/ep1/messages/msg1/meta
xRegistry-versionsurl: https://example.com/endpoints/ep1/messages/msg1/versions
xRegistry-versionscount: 1
Location: https://example.com/endpoints/ep1/messages/msg1
Content-Location: https://example.com/endpoints/ep1/messages/msg1/versions/1.0
Content-Disposition: msg1

{
  # Definition of a "Blob Created" event (document) excluded for brevity
}

Updates the default Version of a Resource as xRegistry metadata:

PUT /endpoints/ep1/messages/msg1$details
Content-Type: application/json; charset=utf-8

{
  "epoch": 1,
  "name": "Blob Created",
  "description": "a cool event",

  "message": {
    # Updated definition of a "Blob Created" event excluded for brevity
  }
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Location: https://example.com/endpoints/ep1/messages/msg1/versions/1.0

{
  "messageid": "msg1",
  "versionid": "1.0",
  "self": "https://example.com/endpoints/ep1/messages/msg1",
  "xid": "/endpoints/ep1/messages/msg1",
  "epoch": 2,
  "name": "Blob Created",
  "isdefault": true,
  "description": "a cool event",
  "createdat": "2024-04-30T12:00:00Z",
  "modifiedat": "2024-04-30T12:00:01Z",
  "ancestor": "1.0",

  "message": {
    # Updated definition of a "Blob Created" event excluded for brevity
  },

  "metaurl": "https://example.com/endpoints/ep1/messages/msg1/meta",
  "versionsurl": "https://example.com/endpoints/ep1/messages/msg1/versions",
  "versionscount": 1
}

Update several Versions (adding a label):

PATCH /endpoints/ep1/messages/msg1/versions
Content-Type: application/json; charset=utf-8

{
  "1.0": {
    "labels": { "customer": "abc" },
  },
  "2.0": {
    "labels": { "customer": "abc" },
  },
  "3.0": {
    "labels": { "customer": "abc" },
  }
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "1.0": {
    "messageid": "msg1",
    "versionid": "1.0",
    "labels": { "customer": "abc" },
    # Remainder of xRegistry metadata excluded for brevity
  },
  "2.0": {
    "messageid": "msg1",
    "versionid": "2.0",
    "labels": { "customer": "abc" },
    # Remainder of xRegistry metadata excluded for brevity
  },
  "3.0": {
    "messageid": "msg1",
    "versionid": "3.0",
    "labels": { "customer": "abc" },
    # Remainder of xRegistry metadata excluded for brevity
  }
]

Note that in this case, the new "label" replaces all existing labels, it is not a "merge" operation because all attributes need to be specified in their entirety.

Retrieving a Resource

To retrieve a Resource, an HTTP GET MAY be used.

The request MUST be of the form:

GET /GROUPS/gID/RESOURCES/rID

This MUST retrieve the default Version of a Resource. Note that rID will be the SINGULARid of the Resource and not the versionid of the underlying Version (see Resources).

A successful response MUST either be:

In both cases the Resource's default Version attributes, along with the meta and versions related scalar attributes, MUST be serialized as HTTP xRegistry- headers when the Resource's hasdocument model attribute has a value of true.

Note that if the Resource's hasdocument model attribute has a value of false then the "Resource document" will be the xRegistry metadata for the default Version - same as in the Retrieving a Resource as Metadata section but without the explicit usage of $details.

When hasdocument is true, the response MUST be of the form:

HTTP/1.1 200 OK|303 See Other
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING
xRegistry-versionid: STRING
xRegistry-self: URL
xRegistry-xid: URI
xRegistry-epoch: UINT
xRegistry-name: STRING ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-ancestor: STRING
xRegistry-RESOURCEurl: URL ?    # If Resource is not in body
xRegistry-metaurl: URL
xRegistry-versionsurl: URL
xRegistry-versionscount: UINTEGER
Location: URL ?                 # If Resource is not in body
Content-Location: URL ?
Content-Disposition: STRING

... Resource document ...       # If RESOURCEurl is not set

Where:

Retrieving a Resource as Metadata

When a Resource has the hasdocument model attribute set to true, to retrieve a Resource's metadata (Resource attributes) as a JSON object, an HTTP GET with $details appended to its URL path MAY be used.

The request MUST be of the form:

GET /GROUPS/gID/RESOURCES/rID[$details]

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Location: URL ?

{
  "RESOURCEid": "STRING",
  "versionid": "STRING",
  "self": "URL",                           # URL to Resource, not default Ver
  "shortself": "URL", ?
  "xid": "XID",                            # Relative URI to Resource
  "epoch": UINTEGER,                       # Start of Default Ver attribs
  "name": "STRING", ?
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",
  "ancestor": "STRING",
  "contenttype": "STRING", ?

  "RESOURCEurl": "URL", ?                  # If not local
  "RESOURCE": ... Resource document ..., ? # If inlined & JSON
  "RESOURCEbase64": "STRING", ?            # If inlined & ~JSON

  "metaurl": "URL",
  "meta": {
    "RESOURCEid": "STRING", ?
    "self": "URL",                         # URL to "meta" sub-object
    "shortself": "URL", ?
    "xid": "XID",                          # Relative URI to "meta" sub-object
    "xref": "URL", ?
    "epoch": UINTEGER,
    "createdat": "TIMESTAMP",
    "modifiedat": "TIMESTAMP",
    "readonly": BOOLEAN,
    "compatibility": "STRING",
    "compatibilityauthority": "STRING", ?
    "deprecated": { ... }, ?

    "defaultversionid": "STRING",
    "defaultversionurl": "URL",
    "defaultversionsticky": BOOLEAN
  },
  "versionsurl": "URL",
  "versionscount": UINTEGER,
  "versions": { Versions collection } ?    # If inlined
}

Where:

Examples:

Retrieve a message Resource as xRegistry metadata:

GET /endpoints/ep1/messages/msg1$details
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Location: https://example.com/endpoints/ep1/messages/msg1/versions/1.0

{
  "messageid": "msg1",
  "versionid": "1.0",
  "self": "https://example.com/endpoints/ep1/messages/msg1","
  "xid": "/endpoints/ep1/messages/msg1",
  "epoch": 1,
  "name": "Blob Created",
  "isdefault": true,
  "createdat": "2024-04-30T12:00:00Z",
  "modifiedat": "2024-04-30T12:00:01Z",
  "ancestor": "1.0",

  "metaurl": "https://example.com/endpoints/ep1/messages/msg1/meta",
  "versionsurl": "https://example.com/endpoints/ep1/messages/msg1/versions",
  "versionscount": 1
}

Deleting Resources

To delete one or more Resources, and all of their Versions, an HTTP DELETE MAY be used:

The processing of these two APIs is defined in the Deleting Entities in a Registry Collection section.

Deleting a Resource MUST delete all Versions within the Resource.


Versions

Versions represent historical instances of a Resource. When a Resource is updated, there are two actions that might take place. First, the update can completely replace an existing Version of the Resource. This is most typically done when the previous state of the Resource is no longer needed, and there is no reason to allow people to reference it. The second situation is when both the old and new Versions of a Resource are meaningful and both might need to be referenced. In this case, the update will cause a new Version of the Resource to be created and will have a unique versionid within the scope of the owning Resource.

For example, updating the data of Resource without creating a new Version would make sense if there is a typo in the description field. But, adding additional data to the document of a Resource might require a new Version and a new versionid (e.g. changing it from "1.0" to "1.1").

This specification does not mandate a particular versioning algorithm or Version identification (versionid) scheme.

When serialized as a JSON object, the Version entity adheres to this form:

{
  "RESOURCEid": "STRING",                  # SINGULARid of Resource
  "versionid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "isdefault": BOOLEAN,
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",
  "ancestor": "STRING",
  "contenttype": "STRING", ?

  "RESOURCEurl": "URL", ?                  # If not local
  "RESOURCE": ... Resource document ..., ? # If inlined & JSON
  "RESOURCEbase64": "STRING" ?             # If inlined & ~JSON
}

Version extension attributes would also appear as additional top-level JSON attributes.

Versions include the following common attributes:

and the following Version level attributes:

as defined below:

versionid Attribute
isdefault Attribute
ancestor Attribute
contenttype Attribute
RESOURCEurl Attribute
RESOURCE Attribute
RESOURCEbase64 Attribute

Version IDs

If a server does not support client-side specification of the versionid of a new Version (see the setversionid attribute in the Registry Model), or if a client chooses to not specify the versionid, then the server MUST assign new Version an versionid that is unique within the scope of its owning Resource.

Servers MAY have their own algorithm for the creation of new Version versionid values, but the default algorithm is as follows:

With this default versioning algorithm, when semantic versioning is needed, it is RECOMMENDED to include a major version identifier in the Resource RESOURCEid, like "com.example.event.v1" or "com.example.event.2024-02", so that incompatible, but historically related Resources can be more easily identified by users and developers. The Version's versionid then functions as the semantic minor version identifier.

Default Version of a Resource

As Versions of a Resource are added or removed, there needs to be a mechanism by which the "default" one is determined. There are two options for how this might be done:

  1. Newest = Default. The newest Version created (based on createdat timestamp) is always the "default" Version. This is the default choice. If more than one Version has the same "newest" createdat timestamp, then the choice the Version with the highest alphabetical versionid value using case-insensitive compares MUST be chosen. For example, if there are 3 Versions with versionid values of v10, z1 and V2, then ordering of the Versions from oldest to newest would be: v10, V2, z1.

  2. Client explicitly chooses the "default". In this option, a client has explicitly chosen which Version is the "default" and it will not change until a client chooses another Version, or that Version is deleted (in which case the server MUST revert back to option 1 (newest = default), if the client did not use ?setdefaultversionid to choose the next "default" Version - see below). This is referred to as the default Version being "sticky" as it will not change until explicitly requested by a client.

If supported (as determined by the setdefaultversionsticky model aspect), a client MAY choose the "default" Version two ways:

  1. Via the Resource defaultversionsticky and defaultversionid attributes in its meta sub-object. See Resource Attributes for more information about these attributes.
  2. Via the ?setdefaultversionid query parameter that is available on certain APIs, as defined below.

The ?setdefaultversionid query parameter is defined as:

...?setdefaultversionid=vID

Where:

Any use of this query parameter on a Resource that has the setdefaultversionsticky aspect set to false MUST generate an error (bad_flag).

Updating a Resource's defaultversionid, regardless of the mechanism used to do so, MUST adhere to the following rules:

Retrieving all Versions

To retrieve all Versions of a Resource, an HTTP GET MAY be used.

The request MUST be of the form:

GET /GROUPS/gID/RESOURCES/rID/versions

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <URL>;rel=next;count=UINTEGER ?

{
  "KEY": {                                    # The versionid
    "RESOURCEid": "STRING",                   # ID of Resource
    "versionid": "STRING",
    "self": "URL",
    "shortself": "URL", ?
    "xid": "XID",
    "epoch": UINTEGER,
    "name": "STRING", ?
    "isdefault": BOOLEAN,
    "description": "STRING", ?
    "documentation": "URL", ?
    "labels": { "STRING": "STRING" * }, ?
    "createdat": "TIMESTAMP",
    "modifiedat": "TIMESTAMP",
    "ancestor": "STRING",
    "contenttype": "STRING", ?

    "RESOURCEurl": "URL", ?                  # If not local
    "RESOURCE": ... Resource document ..., ? # If inlined & JSON
    "RESOURCEbase64": "STRING" ?             # If inlined & ~JSON
  } *
}

Where:

Examples:

Retrieve all Version of a message Resource:

GET /endpoints/ep1/messages/msg1/versions
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <https://example.com/endpoints/ep1/messages/msg1/versions&page=2>;rel=next;count=100

{
  "1.0": {
    "messageid": "msg1",
    "versionid": "1.0",
    "self": "https://example.com/endpoints/ep1/messages/msg1",
    "xid": "/endpoints/ep1/messages/msg1",
    "epoch": 1,
    "name": "Blob Created",
    "isdefault": true,
    "createdat": "2024-04-30T12:00:00Z",
    "modifiedat": "2024-04-30T12:00:01Z",
    "ancestor": "1.0"
  }
}

Creating or Updating Versions

See Creating or Updating Resources and Versions.

Retrieving a Version

To retrieve a particular Version of a Resource, an HTTP GET MAY be used.

The request MUST be of the form:

GET /GROUPS/gID/RESOURCES/rID/versions/vID

A successful response MUST either return the Version or an HTTP redirect to the RESOURCEurl value if set.

In the case of returning the Version's document, the response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING
xRegistry-versionid: STRING
xRegistry-self: URL
xRegistry-xid: URI
xRegistry-epoch: UINT
xRegistry-name: STRING ?
xRegistry-isdefault: BOOLEAN ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-ancestor: STRING
Content-Disposition: STRING

... Version document ...

Where:

In the case of a redirect, the response MUST be of the form:

HTTP/1.1 303 See Other
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING
xRegistry-versionid: STRING
xRegistry-self: URL
xRegistry-xid: URI
xRegistry-epoch: UINT
xRegistry-name: STRING ?
xRegistry-isdefault: BOOLEAN ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-ancestor: STRING
xRegistry-RESOURCEurl: URL
Location: URL
Content-Disposition: STRING

Where:

Examples:

Retrieve a specific Version (1.0) of a message Resource:

GET /endpoints/ep1/messages/msg1/versions/1.0
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
xRegistry-messageid: msg1
xRegistry-versionid: 1.0
xRegistry-self: https://example.com/endpoints/ep1/messages/msg1/versions/1.0
xRegistry-xid: /endpoints/ep1/messages/msg1/versions/1.0
xRegistry-epoch: 2
xRegistry-name: Blob Created
xRegistry-isdefault: true
xRegistry-createdat: TIMESTAMP
xRegistry-modifiedat: TIMESTAMP
xRegistry-ancestor: 1.0
Content-Disposition: msg1

{
  # Definition of a "Blob Created" event excluded for brevity
}

Retrieving a Version as Metadata

To retrieve a particular Version's metadata, an HTTP GET with $details appended to its RESOURCEid MAY be used. Note that in cases where the Resource's hasdocument is false then the $details suffix is OPTIONAL.

The request MUST be of the form:

GET /GROUPS/gID/RESOURCES/rID/versions/vID[$details]

A successful response MUST be of the form:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "RESOURCEid": "STRING",
  "versionid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",
  "epoch": UINTEGER,
  "name": "STRING", ?
  "isdefault": BOOLEAN,
  "description": "STRING", ?
  "documentation": "URL", ?
  "labels": { "STRING": "STRING" * }, ?
  "createdat": "TIMESTAMP",
  "modifiedat": "TIMESTAMP",
  "ancestor": "STRING",
  "contenttype": "STRING", ?

  "RESOURCEurl": "URL", ?                  # If not local
  "RESOURCE": ... Resource document ..., ? # If inlined & JSON
  "RESOURCEbase64": "STRING" ?             # If inlined & ~JSON
}

Where:

Examples:

Retrieve a specific Version of a message Resource as xRegistry metadata:

GET /endpoints/ep1/messages/msg1/versions/1.0$details
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "messageid": "msg1",
  "versionid": "1.0",
  "self": "https://example.com/endpoints/ep1/messages/msg1/versions/1.0",
  "xid": "/endpoints/ep1/messages/msg1/versions/1.0",
  "epoch": 2,
  "name": "Blob Created",
  "isdefault": true,
  "createdat": "2024-04-30T12:00:00Z",
  "modifiedat": "2024-04-30T12:00:01Z",
  "ancestor": "1.0"
}

Deleting Versions

To delete one or more Versions of a Resource, an HTTP DELETE MAY be used:

The processing of these two APIs is defined in the Deleting Entities in a Registry Collection section. For more information about the ?setdefaultversionid query parameter see the Default Version of a Resource section.

If as a result of one of these operations a Resource has no Versions, then the Resource MUST also be deleted.

A successful response MUST return either:

HTTP/1.1 204 No Content

with an empty HTTP body, or:

HTTP/1.1 200 OK

If, as an extension, the server chooses to return additional data in the HTTP body.

Examples:

Delete a single Version of a message Resource:

DELETE /endpoints/ep1/messages/msg1/versions/1.0
HTTP/1.1 204 No Content

Configuring Responses

Any request MAY include a set of query parameters (flags) to control how the response is to be generated. The following sections will define the following flags:

Implementations of this specification SHOULD support all 3 flags.

Note: If the Registry cannot return all expected data in one response because it is too large then it MUST generate an error (too_large). In those cases, the client will need to query the individual inlinable attributes in isolation so the Registry can leverage pagination of the response.

Collections Flag

The ?collections query parameter (flag) MAY be used on requests directed to the Registry itself or to Group instance to indicate that the response message MUST NOT include any attributes from the top-level entity (Registry or Group), but instead MUST include only all of the nested Collection maps that are defined at that level. Specifying it on a request directed to some other part of the Registry MUST generate an error (bad_flag). Use of this flag MUST implicitly turn on inlining - ?inline=*.

Servers MAY choose include, or exclude, the sibling COLLECTIONSurl and COLLECTIONScount attributes for those top-level collections.

Note that this feature only applies to the root entity of the response and not to any nested entities/collections.

This feature is meant to be used when the Collections of the Registry, or Group, are of interest but not the top-level metadata. For example, this could be used to export one or more Group types from a Registry where the resulting JSON document is then used to import them into another Registry. If the Registry-level attributes were present in the output then they would need to be removed prior to the import, otherwise they would override the target Registry's values.

The resulting JSON, when using this feature, is designed to be used on a future POST / operation to either a Registry entity or to a Group instance as appropriate.

Doc Flag

The ?doc query parameter (flag) MAY be used to indicate that the response MUST use "document view" when serializing entities and MUST be modified to do the following:

All of the relative URLs mentioned in the last bullet MUST begin with # and be followed by a JSON Pointer reference to the entity within the response, e.g. #/endpoints/e1. This means that they are relative to the root of the document (response) generated, and not necessarily relative to the root of the Registry. Additionally, when those URLs are relative and reference a Resource or Version, the $details suffix MUST NOT be present despite the semantics of the suffix being applied (as noted below).

For clarity, if a Registry has a Schema Resource at /schemagroups/g1/schemas/s1, then this entity's self URL (when serialized in document view) would change based on the path specified on the GET request:

GET Path self URL
http://example.com/myreg #/schemagroups/g1/schemas/s1
http://example.com/myreg/schemagroups #/g1/schemas/s1
http://example.com/myreg/schemagroups/g1/ #/schemas/s1
http://example.com/myreg/schemagroups/g1/schemas #/s1
http://example.com/myreg/schemagroups/g1/schemas/s1 #/

This feature is useful when a client wants to minimize the amount of data returned by a server because the duplication of that data (typically used for human readability purposes) isn't necessary. For example, if tooling would ignore the duplication, or if the data will be used to populate a new Registry, then this feature might be used. It also makes the output more of a "stand-alone" document that minimizes external references.

For clarity, the serialization of a Resource in document view will adhere to the following:

{
  "RESOURCEid": "STRING",
  "self": "URL",
  "shortself": "URL", ?
  "xid": "XID",

  "metaurl": "URL",
  "meta": {
    "RESOURCEid": "STRING",
    "self": URL",
    "shortself": "URL", ?
    "xid": "XID",
    "xref": "URL" ?
    # The following attributes are absent if 'xref' is set
    "epoch": UINTEGER",
    "createdat": "TIMESTAMP",
    "modifiedat": "TIMESTAMP",
    "ancestor": "STRING",
    "readonly": BOOLEAN,
    "compatibility": "STRING",
    "compatibilityauthority": "STRING", ?
    "deprecated": { ... }, ?

    "defaultversionid": "STRING",
    "defaultversionurl": "URL"
    "defaultversionsticky": BOOLEAN
  }
}

Note that the attributes epoch through defaultversionsticky MUST be excluded if xref is set because those would be picked-up from the target Resource's meta sub-object.

If ?doc is used on a request directed to a Resource, or Version, that has the hasdocument model aspect set to true, then the processing of the request MUST take place as if the $details suffix was specified in the URL. Meaning, the response MUST be the xRegistry metadata view of the Resource and not the Resource's "document".

If ?doc is used on a request directed to a Resource's versions collection, or to one of its Versions, but the Resource is defined as an xref to another Resource, then the server MUST generate an error (cannot_doc_xref) and SHOULD indicate that using ?doc on this part of the hierarchy is not valid - due to it not technically existing in document view.

Filter Flag

The ?filter query parameter (flag) on a request indicates that the response MUST include only those entities that match the specified filter criteria. This means that any Registry Collection's attributes MUST be modified to match the resulting subset. In particular:

The format of the ?filter query parameter is:

filter=EXPRESSION[,EXPRESSION]

Where:

The abstract processing logic would be:

The format of EXPRESSION is one of:

[PATH.]ATTRIBUTE
[PATH.]ATTRIBUTE=null
[PATH.]ATTRIBUTE=[VALUE]
[PATH.]ATTRIBUTE!=[VALUE]

Where:

When comparing an ATTRIBUTE to the specified VALUE the following rules MUST apply for an entity to be considered a match of the filter expression:

If the request references an entity (not a collection), and the EXPRESSION references an attribute in that entity (i.e. there is no PATH), then if the EXPRESSION does not match the entity, that entity MUST NOT be returned. In other words, a 404 Not Found would be generated in the HTTP protocol case.

Examples:

Request PATH Filter query Commentary
/ ?filter=endpoints.description=*cool* Only endpoints with the word cool in the description
/endpoints ?filter=description=*CooL* Similar results as previous, with a different request URL
/ ?filter=endpoints.messages.versions.versionid=1.0 Only versions (and their owning parents) that have a versionid of 1.0
/ ?filter=endpoints.name=myendpoint,endpoints.description=*cool*& filter=schemagroups.labels.stage=dev Only endpoints whose name is myendpoint and whose description contains the word cool, as well as any schemagroups with a label name/value pair of stage/dev
/ ?filter=description=no-match Returns a 404 if the Registry's description doesn't equal no-match
/ ?filter=endpoints.messages.meta.readonly=true Only messages that are readonly

Specifying a filter does not imply inlining. However, inlining can be used at the same time but MUST NOT result in additional entities being included in the results unless they are children of a matching leaf entity.

For example, in the following entity URL paths representing a Registry:

mygroups/g1/myresources/r1/versions/v1
mygroups/g1/myresources/r1/versions/v2
mygroups/g1/myresources/r2/versions/v1
mygroups/g2/myresources/r3/versions/v1

This request:

GET /?filter=mygroups.myresources.myresourceid=r1&inline=*

would result in the following entities (and their parents along the specified paths) being returned:

mygroups/g1/myresources/r1/versions/v1  # versions are due to inlining
mygroups/g1/myresources/r1/versions/v2

However, this request:

GET /?filter=mygroups.mygroupid=g2&filter=mygroups.myresources.myresourceid=r1&inline=*

would result in the following returned:

mygroups/g1/myresources/r1/versions/v1   # from 2nd ?filter
mygroups/g1/myresources/r1/versions/v2   # from 2nd ?filter
mygroups/g2/myresources/r3/versions/v1   # from 1nd ?filter

And, this request:

GET /?filter=mygroups.mygroupid=g1&filter=mygroups.myresources.myresourceid=r1&inline=*

would result in the following being returned:

mygroups/g1/myresources/r1/versions/v1   # from 2nd ?filter
mygroups/g1/myresources/r1/versions/v2   # from 2nd ?filter
mygroups/g1/myresources/r2/versions/v1   # from 1st ?filter

And, finally this request:

GET /?filter=mygroups.mygroupid=g1,mygroups.myresources.myresourceid=r1&inline=*

would result in the following being returned:

mygroups/g1/myresources/r1/versions/v1
mygroups/g1/myresources/r1/versions/v2

Notice the first part of the ?filter expression (to the left of the "and" (,)) has no impact on the results because the list of resulting leaves in that subtree is not changed by that search criteria.

Inline Flag

The ?inline query parameter (flag) MAY be used on requests to indicate whether nested collections/objects, or certain (potentially large) attributes, are to be included in the response message.

The ?inline query parameter on a request indicates that the response MUST include the contents of all specified inlinable attributes. Inlinable attributes include:

While the RESOURCE and RESOURCEbase64 attributes are defined as two separate attributes, they are technically two separate "views" of the same underlying data. As such, the usage of each will be based on the content type of the Resource, specifying RESOURCE in the ?inline query parameter MUST be interpreted as a request for the appropriate attribute. In other words, RESOURCEbase64 is not a valid inlinable attribute name.

Use of this feature is useful for cases where the contents of the Registry are to be represented as a single (self-contained) document.

Some examples:

The format of the ?inline query parameter is:

?inline[=PATH[,...]]

Where PATH is a string indicating which inlinable attributes to show in the response. References to nested attributes are represented using a dot (.) notation where the xRegistry collections names along the hierarchy are concatenated. For example: endpoints.messages.versions will inline all Versions of Messages. Non-leaf parts of the PATH MUST only reference xRegistry collection names and not any specific entity IDs since PATH is meant to be an abstract traversal of the model.

To reference an attribute with a dot as part of its name, the JSONPath escaping mechanism MUST be used: ['my.name']. For example, prop1.my.name.prop2 would be specified as prop1['my.name'].prop2 if my.name is the name of an attribute.

There MAY be multiple PATHs specified, either as comma separated values on a single ?inline query parameter or via multiple ?inline query parameters.

The * value MAY be used to indicate that all nested inlinable attributes at that level in the hierarchy (and below) MUST be inlined - except model and capabilities at the root of the Registry. These two are excluded since the data associated with them are configuration related. To include their data the request MUST include PATH values of model or capabilities. Use of * MUST only be used as the last part of the PATH (in its entirety). For example, foo* and *.foo are not valid PATH values, but * and endpoints.* are.

An ?inline query parameter without any value MAY be supported and if so it MUST have the same semantic meaning as ?inline=*.

The specific value of PATH will vary based on where the request is directed. For example, a request to the root of the Registry MUST start with a GROUPS name, while a request directed at a Group would start with a RESOURCES name.

For example, given a Registry with a model that has endpoints as a Group and messages as a Resource within endpoints, the table below shows some PATH values and a description of the result:

HTTP GET Path Example ?inline=PATH values Comment
/ ?inline=endpoints Inlines the endpoints collection, but just one level of it, not any nested inlinable attributes
/ ?inline=endpoints.messages.versions Inlines the versions collection of all messages. Note that this implicitly means the parent attributes (messages and endpoints would also be inlined - however any other GROUPS or RESOURCEs types would not be
/endpoints ?inline=messages Inlines just messages and not any nested attributes. Note we don't need to specify the parent GROUP since the URL already included it
/endpoints/ep1 ?inline=messages.versions Similar to the previous endpoints.messages.version example
/endpoints/ep1 ?inline=messages.message Inline the Resource itself
/endpoints/ep1 ?inline=endpoints Invalid, already in endpoints and there is no RESOURCE called endpoints
/ ?inline=endpoints.messages.meta Inlines the meta attributes/sub-object of each message returned.
/ ?inline=endpoints.* Inlines everything for all endpoints.

Note that asking for an attribute to be inlined will implicitly cause all of its parents to be inlined as well, but just the parent's collections needed to show the child. In other words, just the collection in the parent in which the child appears, not all collections in the parent.

When specifying a collection to be inlined, it MUST be specified using the plural name for the collection in its defined case.

A request to inline an unknown, or non-inlinable, attribute MUST generate an error (invalid_data).

HTTP Header Values

Some attributes can contain arbitrary UTF-8 string content, and per RFC7230, section 3, HTTP headers MUST only use printable characters from the US-ASCII character set, and are terminated by a CRLF sequence with OPTIONAL whitespace around the header value.

When encoding an attribute's value as an HTTP header, it MUST be percent-encoded as described below. This is compatible with RFC3986, section 2.1 but is more specific about what needs encoding. The resulting string SHOULD NOT be further encoded. (Rationale: quoted string escaping is unnecessary when every space and double-quote character is already percent-encoded.)

When decoding an HTTP header into an attribute's value, any HTTP header value MUST first be unescaped with respect to double-quoted strings, as described in RFC7230, section 3.2.6. A single round of percent-decoding MUST then be performed as described below. HTTP headers for attribute values do not support parenthetical comments, so the initial unescaping only needs to handle double-quoted values, including processing backslash escapes within double-quoted values. Header values produced via the percent-encoding described here will never include double-quoted values, but they MUST be supported when receiving events, for compatibility with older versions of this specification which did not require double-quote and space characters to be percent-encoded.

Percent encoding is performed by considering each Unicode character within the attribute's canonical string representation. Any character represented in memory as a [Unicode surrogate pair][surrogate-pair] MUST be treated as a single Unicode character. The following characters MUST be percent-encoded:

Space and double-quote are encoded to avoid requiring any further quoting. Percent is encoded to avoid ambiguity with percent-encoding itself.

Steps to encode a Unicode character:

Percent-encoding SHOULD be performed using upper-case for values A-F, but decoding MUST accept lowercase values.

When performing percent-decoding, values that have been unnecessarily percent-encoded MUST be accepted, but encoded byte sequences which are invalid in UTF-8 MUST generate an error (header_decoding_error). For example, "%C0%A0" is an overlong encoding of U+0020, and would be rejected.

Example: a header value of "Euro € 😀" SHOULD be encoded as follows:

Error Processing

If an error occurs during the processing of a request, even if the error was during the creation of the response (e.g. an invalid ?inline value was provided), then an error MUST be generated and the entire request MUST be undone.

In general, when an error is generated, it SHOULD be sent back to the client. However, this MAY not happen if the server determines there is a good reason to not do so - such as due to security concerns.

Most of the error conditions mentioned in this specification will include a reference to one of the errors listed in this section. While it is RECOMMENDED that implementations use those errors (for consistency), they MAY choose to use a more appropriate one (or a custom one).

When an error is transmitted back to clients, it SHOULD adhere to the format specified in this section - which references the Problem Details for HTTP APIs specification, and when used MUST be of the following form:

HTTP/1.1 CODE
Content-Type: application/json; charset=utf-8

{
  "type": "URI",
  "instance": "URL",
  "title": "STRING",
  "detail": "STRING", ?
  ... error specific fields ...
}

Where:

CODE, "type", "instance" and title fields are REQUIRED. All other fields are OPTIONAL unless overwise stated as part of the error definition. Any substitutable information defined as part of an error MUST be populated appropriately.

HTTP response codes and status text are defined in the HTTP Semantics specification.

In the following list of errors, the Code, Type and Instance values MUST be as specified. The other field values are recommendations and MAY be modified as appropriate, including being specified in a language other than English.

ancestor_circular_reference

api_not_found

bad_flag

bad_request

This error is purposely generic and can be used when there isn't a more condition-specific error that would be more appropriate. Implementations SHOULD attempt to use a more specific error when possible.

cannot_doc_xref

capability_error

compatibility_violation

data_retrieval_error

details_required

extra_xregistry_headers

header_decoding_error

invalid_character

invalid_data

invalid_data_type

method_not_allowed

mismatched_epoch

mismatched_id

misplaced_epoch

missing_versions

model_compliance_error

model_error

multiple_roots

not_found

readonly

required_attribute_missing

server_error

This error MAY be used when it appears that the incoming request was valid but something unexpected happened in the server that caused an error condition.

too_large

too_many_versions

unknown_attribute

unknown_id

unsupported_specversion