Specification scope: Core with the Relations companion specification.

A property named customerId suggests a relation but does not declare its target or resolution rules.

The string might identify a customer, an account, or a CRM import row. It might be unique in this document, unique in a database, or not unique at all. The name suggests a foreign key while leaving its target and resolution rules in prose.

The relations extension gives the schema vocabulary for those rules: identity, relations, targettype, cardinality, and scope.

Identity starts at the target

An identity belongs to an object or tuple type. Its value is an ordered array of property names. One property gives a simple identity; several give a composite identity.

The complete example below declares Customer.id as the customer identity and Order.id as the order identity. The customer relation on Order targets Customer and resolves within the document’s customers collection.

{
  "$schema": "https://json-structure.org/meta/extended/v0/#",
  "$id": "https://example.com/schemas/commerce-document",
  "name": "CommerceDocumentSchema",
  "$root": "#/definitions/CommerceDocument",
  "definitions": {
    "Customer": {
      "name": "Customer",
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string" }
      },
      "required": ["id", "name"],
      "additionalProperties": false,
      "identity": ["id"]
    },
    "Order": {
      "name": "Order",
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "total": { "type": "decimal" }
      },
      "relations": {
        "customer": {
          "targettype": { "$ref": "#/definitions/Customer" },
          "cardinality": "single",
          "scope": "#/definitions/CommerceDocument/properties/customers"
        }
      },
      "required": ["id", "total"],
      "additionalProperties": false,
      "identity": ["id"]
    },
    "CommerceDocument": {
      "name": "CommerceDocument",
      "type": "object",
      "properties": {
        "customers": {
          "type": "array",
          "items": { "type": { "$ref": "#/definitions/Customer" } }
        },
        "orders": {
          "type": "array",
          "items": { "type": { "$ref": "#/definitions/Order" } }
        }
      },
      "required": ["customers", "orders"],
      "additionalProperties": false
    }
  }
}

A corresponding document contains the relation as an ordinary JSON property:

{
  "$schema": "https://example.com/schemas/commerce-document",
  "customers": [
    { "id": "C-1042", "name": "Ada Lovelace" }
  ],
  "orders": [
    {
      "id": "O-9001",
      "total": "149.50",
      "customer": { "identity": "C-1042" }
    }
  ]
}

The familiar conceptual link Order.customerId -> Customer.id is present, but it is not modeled as a bare customerId property. The draft gives relation instances their own shape. A single relation is an object whose identity member matches the target type’s identity.

Target and cardinality are explicit

Every relation declaration requires targettype and cardinality.

targettype is a schema containing a $ref to a type with an identity declaration. That rule prevents a relation from pointing vaguely at an object shape that has no declared matching key.

cardinality is either single or multiple. single means exactly one target instance and is represented by one relation object. multiple means zero or more targets and is represented by an array of relation objects.

Cardinality is not inferred from an English plural or a property name. The schema says it.

Scope says where to look

scope is a JSON Pointer, or an array of JSON Pointers, to schema locations for collections in the same document. A target collection must be an array, set, or map compatible with targettype. Map resolution searches values, not keys.

In the example, the resolver follows #/definitions/CommerceDocument/properties/customers, then finds the customer whose declared identity equals C-1042.

Omit scope and the meaning changes: the target exists outside the document. The application must resolve it through an external database, service, or other source. The absence of scope is therefore not shorthand for “search everywhere in this JSON document.”

Composite identities preserve order

If a target declares "identity": ["isbn", "edition"], a relation instance uses an array in that same order:

{ "identity": ["978-0-123456-78-9", 2] }

That order is part of the contract. It is the relation equivalent of a composite primary key.

Resolution remains a processor task

identity and relations make reference intent machine-readable, but they do not prescribe an external query protocol. A core-only validator need not enforce uniqueness across a collection or dereference an external service. A relations-aware processor must perform those checks.