Early developmentNot everything described here has shipped yet.

Skip to content

Subject JSON Format ​

Subject data is stored as JSON. The REST API returns and accepts the same shapes; the fields each endpoint adds or ignores are under REST API.

For Subject, Statement, and Value, see the Glossary.

Top-level structure ​

A page holds one optional main Subject and zero or more other Subjects (ADR 007), all in one subjects map with mainSubject pointing at the main one.

json
{
  "mainSubject": "<subject-id>",
  "subjects": {
    "<subject-id>": { ... }
  }
}
FieldTypeRequiredDescription
mainSubjectstringNoID of the page's main Subject. Omitted or null when the page has none.
subjectsobjectNoMap of Subject ID to Subject object. Omitted or empty when the page has no Subjects.

Subject object ​

json
{
  "label": "Professional Wiki GmbH",
  "schema": "Company",
  "statements": {
    "<property-name>": { ... }
  }
}
FieldTypeRequiredDescription
labelstringNoHuman-readable label for the Subject. Omitted when the Subject has none.
schemastring or objectYesReference to the Schema the Subject follows. A string for a Schema of this wiki: its page title in the Schema namespace.
statementsobjectNoMap of property name to Statement object. Omitted when the Subject has none.

A property mapped to null instead of a Statement object is skipped when the JSON is read.

Statement object ​

json
{
  "propertyType": "number",
  "value": 2019
}
FieldTypeRequiredDescription
propertyTypestringYesThe property's type when the value was written — the writer's schema (ADR 011).
valuevariesYesThe value, shaped by propertyType. See Value formats.

Value formats ​

propertyType holds the property type name, which fixes the value shape:

propertyTypevalue
text, url, select, date, dateTimeArray of strings, one per value part.
numberA single number (integer or float).
booleanA single boolean.
relationArray of relation objects.
monolingualTextArray of texts with their language.

A multi-part text value:

json
{ "propertyType": "text", "value": [ "First value", "Second value" ] }

Every registered PropertyType uses one of these five value shapes. A propertyType whose PropertyType is not registered — its extension disabled — keeps the raw value that was stored (unregistered-type).

Relations ​

Each relation value is an array of objects pointing at other Subjects:

json
{
  "propertyType": "relation",
  "value": [
    { "id": "r1demo5rrrrrrr1", "target": "s1demo4sssssss1" }
  ]
}
FieldTypeRequiredDescription
idstringYesID of this relation.
targetstringYesID of the target Subject.

Text with language ​

Each monolingualText value is an array of objects, one per value part:

json
{
  "propertyType": "monolingualText",
  "value": [
    { "text": "Zinema", "language": "eu" },
    { "text": "Cine", "language": "es" }
  ]
}
FieldTypeRequiredDescription
textstringYesThe text. Stored trimmed; a part whose text is empty is dropped.
languagestringYesBCP-47-shaped language tag (^[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*$, e.g. eu, pt-BR). Stored lowercase.

The same language may appear more than once. A language that does not match the pattern is rejected with 400.

IDs ​

Subject and Relation IDs are 15-character nanoid-style strings, lexicographically sortable by creation time. Subject IDs start with s (s1demo5sssssss1), Relation IDs with r (r1demo5rrrrrrr1). See ADR 014.

A Subject from another Source (ADR 023) is identified by sourceKey:localId, split at the first colon — wikibase:Q42, otherwiki:s1demo5sssssss1. The source key is a letter followed by up to 63 letters, digits, underscores or hyphens. The local ID is whatever that Source calls the Subject, up to 256 characters of A-Z a-z 0-9 and . _ ~ - : @ ! $ ( ) * + , ; = — further colons included. A Subject of this wiki is always written bare, never thisWikiId:s1demo5sssssss1, and the two forms name the same Subject wherever both are accepted.

The keys of a page's subjects map are these bare local IDs: a Subject from another Source is fetched from that Source and is never stored in a page's content. An entry keyed otherwise is skipped when the page is read.

The 15-character grammar, and the creation-time sorting that comes with it, hold for this wiki's Subjects only.

REST API ​

Reading Subjects ​

GET /rest.php/neowiki/v0/subject/{subjectId} returns a top-level requestedId and a subjects map; each Subject gains an id and a displayName, and carries label as null where the stored JSON omits it. Every endpoint that returns a Subject object serves this shape.

displayName is the name to show, never null: the label where there is one, otherwise the page name for a main Subject and the Schema name for any other Subject, or for a main Subject whose page is titled with the id of a Subject stored on it (ADR 31). displayNameIsGenerated is true wherever that Schema name is used.

A Dependent Subject also carries hostSubject: name it after its Schema and its Host Subject, as in "Birth of Pablo Picasso", rather than by displayName. hostSubject holds the Host Subject's id, schema, displayName and displayNameIsGenerated, plus its own hostSubject, named the same way, where the Host Subject is a Dependent Subject too. The rows of the Subject lists carry hostSubject as well.

  • ?expand=page adds pageId, pageTitle, and pageNamespaceId to each Subject. pageTitle is the full page title with namespace prefix (e.g. Help:Installation); pageNamespaceId is the canonical MediaWiki namespace ID (e.g. 0 for the main namespace, 12 for Help).
  • ?expand=relations embeds the Subjects this one's relations target, and in turn those that the relations of its Dependent Subjects target; see REST API for the shape.
  • ?revisionId= returns the Subject as of that MediaWiki revision; an unknown or unreadable revision returns 404.
  • ?latest=1 returns the Subject as of the hosting page's current revision rather than the one the wiki publishes, for editing, when the viewer may see that revision. Combined with revisionId or expand=relations it returns 400.

Referencing Subjects ​

GET /rest.php/neowiki/v0/subject/{subjectId}/referencingSubjects returns {referencingSubjects: [{subject, propertyNames}], truncated}: the Subjects whose relations point at the given one, itself excluded, ordered by name, generated names last, each subject carrying its page fields. truncated means more were left out; false does not promise there are none. ?limit= caps the list, 10 by default.

Creating Subjects ​

POST /rest.php/neowiki/v0/page/{pageId}/mainSubject and .../{pageId}/subjects create a Subject on a page, and POST /rest.php/neowiki/v0/subjects creates one together with a page of its own. The body takes schema and statements, both required, plus an optional label, an optional comment edit summary, and optional dependentSubjects. Omitting label, or passing only whitespace, creates a Subject with no label.

The server mints the Subject ID unless you pass one:

FieldRequiredNotes
idNoSubject ID to assign. Well-formed (400 otherwise) and unused (409 otherwise). Pre-mint a batch with POST /rest.php/neowiki/v0/subject-ids to wire relations before their targets exist.

POST /rest.php/neowiki/v0/subjects also takes an optional pageTitle. A page-first wiki titles the new page by pageTitle, else by the label, else by the Subject's ID when there is no label or the label cannot be a main-namespace title. A subject-first wiki titles it by the Subject's ID, in the Subject namespace by default, and answers 400 to any pageTitle. A pageTitle that cannot be a main-namespace title also answers 400; a title already taken answers 409 carrying pageTitle; an id in use answers 409 without it.

Writing Subjects ​

PUT /rest.php/neowiki/v0/subject/{subjectId} replaces the Subject's label and statements:

json
{
  "label": "Updated Label",
  "statements": {
    "Founded at": {
      "propertyType": "number",
      "value": 2019
    }
  },
  "comment": "Optional edit summary"
}
FieldRequiredNotes
labelNoReplaces the stored label. Omitted or whitespace-only clears any label the Subject has.
statementsYesMap of property name to Statement; omitted names are deleted. Pass {} to clear all.
dependentSubjectsNoSee Dependent Subjects.
commentNoEdit summary.

On every endpoint that takes statements, an entry without propertyType, or whose value is empty for its type, is dropped without error. A value whose shape does not match its propertyType (see Value formats) is rejected with 400. For schema/value validation outcomes see Validation Codes.

A relation may omit id; the server generates one. The Subject's id and schema are immutable and ignored if sent, as are its page fields on these endpoints: the page a Subject is on changes only through the move endpoint.

Writing one Statement ​

PUT /rest.php/neowiki/v0/subject/{subjectId}/statements/{propertyName} sets a single Statement, leaving the Subject's label and its other Statements as they are:

json
{
  "statement": {
    "propertyType": "url",
    "value": ["https://example.com"]
  },
  "comment": "Optional edit summary"
}
FieldRequiredNotes
statementYesA Statement object carrying a value. propertyType may be omitted, and then takes the type the Subject's Schema currently gives the property; a property the Schema does not define is rejected with 400. A value that is empty for its type removes the Statement.
commentNoEdit summary.

DELETE on the same path removes the Statement and takes only comment. Removing a Statement the Subject does not have succeeds and changes nothing.

Violations in the response cover the whole Subject, not only the property written.

{propertyName} is URL-encoded, so Founded at is Founded%20at. A property name containing / is not addressable this way on servers that reject %2F in paths; write those Statements with PUT /subject/{subjectId}.

Dependent Subjects ​

A Subject whose Schema declares its Subjects dependent is a Dependent Subject; any other Subject is standalone. A Dependent Subject has no label, is never a page's main Subject, and exactly one relation points at it: from its Host Subject on the same page, through a property whose target Schemas declare their Subjects dependent. Such a property points only at Subjects on the page of the Subject holding it. A write that breaks any of this is refused with 422 and these codes.

The create and replace endpoints take an optional dependentSubjects list, written in the same revision as the Subject they create or replace:

json
{
  "label": "Pablo Picasso",
  "schema": "Person",
  "statements": {
    "Birth": { "propertyType": "relation", "value": [ { "target": "s1demo6sssssss1" } ] }
  },
  "dependentSubjects": [
    { "id": "s1demo6sssssss1", "schema": "Birth", "statements": { ... } }
  ]
}

Each item has an id, a schema that declares its Subjects dependent (400 otherwise), and statements. An unused id, such as one from POST /rest.php/neowiki/v0/subject-ids, creates a Subject. When replacing a Subject, the id of a Dependent Subject of the same Schema that it points at, directly or through other Dependent Subjects, replaces that Dependent Subject. Any other id in use answers 409. A relation to another Subject of the same request does not report relation-target-not-found. The response's subject, schema and violations cover only the Subject the request creates or replaces; violations of a Dependent Subject appear only in a 422 body.

Every write removes the Dependent Subjects it leaves unreached: those that a standalone Subject on the page pointed at before the write, directly or through other Dependent Subjects, and none does after it. Dropping the relation to a Dependent Subject removes it, and deleting a Subject removes its Dependent Subjects; a write that leaves one of its own dependentSubjects unreached is refused instead. These rules never refuse a deletion, and deleting a Dependent Subject removes the relation to it.

Write responses ​

Creating a Subject, writing one, and writing one of its Statements all answer with the Subject as persisted and the Schema it instantiates, so a client does not have to re-read after a write:

json
{
  "status": "updated",
  "subjectId": "s1demo5sssssss1",
  "violations": [],
  "subject": { "id": "s1demo5sssssss1", "label": "Updated Label", "displayName": "Updated Label",
               "displayNameIsGenerated": false, "schema": "Company", "pageId": 42,
               "pageTitle": "Help:Installation", "pageNamespaceId": 12, "statements": {} },
  "schema": { "description": "A company", "propertyDefinitions": {} }
}

subject is the same shape GET /subject/{subjectId}?expand=page serves, so it reflects any normalisation the write applied. Its page fields are omitted when the server cannot resolve the Subject's page.

schema carries no name of its own — that is the Subject's schema field — and is absent when the Schema does not exist or its page is unreadable. It is the Schema as of the write, which may define properties the client's copy does not.

Complete example ​

A page about Berlin with a main Subject and another Subject for population data:

json
{
  "mainSubject": "s1demo2sssssss1",
  "subjects": {
    "s1demo2sssssss1": {
      "label": "Berlin",
      "schema": "City",
      "statements": {
        "Country": {
          "propertyType": "text",
          "value": ["Germany"]
        }
      }
    },
    "s1demo2sssssss2": {
      "label": "Latest",
      "schema": "Population",
      "statements": {
        "Population": {
          "propertyType": "number",
          "value": 3677472
        },
        "Date": {
          "propertyType": "text",
          "value": ["2020-12-31"]
        },
        "References": {
          "propertyType": "url",
          "value": ["https://example.com/Pop2020"]
        }
      }
    }
  }
}