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 child Subjects (ADR 007), all in one subjects map with mainSubject pointing at the main one.
{
"mainSubject": "<subject-id>",
"subjects": {
"<subject-id>": { ... }
}
}| Field | Type | Required | Description |
|---|---|---|---|
mainSubject | string | No | ID of the page's main Subject. Omitted or null when the page has none. |
subjects | object | No | Map of Subject ID to Subject object. Omitted or empty when the page has no Subjects. |
Subject object
{
"label": "Professional Wiki GmbH",
"schema": "Company",
"statements": {
"<property-name>": { ... }
}
}| Field | Type | Required | Description |
|---|---|---|---|
label | string | Yes | Human-readable label for the Subject. |
schema | string | Yes | Name of the Schema the Subject follows (a page in the Schema namespace). |
statements | object | No | Map 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
{
"propertyType": "number",
"value": 2019
}| Field | Type | Required | Description |
|---|---|---|---|
propertyType | string | Yes | The property's type when the value was written — the writer's schema (ADR 011). |
value | varies | Yes | The value, shaped by propertyType. See Value formats. |
Value formats
propertyType holds the property type name, which fixes the value shape:
propertyType | value |
|---|---|
text, url, select, date, dateTime | Array of strings, one per value part. |
number | A single number (integer or float). |
boolean | A single boolean. |
relation | Array of relation objects. |
A multi-part text value:
{ "propertyType": "text", "value": [ "First value", "Second value" ] }Every registered PropertyType uses one of these four 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:
{
"propertyType": "relation",
"value": [
{ "id": "r1demo5rrrrrrr1", "target": "s1demo4sssssss1" }
]
}| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | ID of this relation. |
target | string | Yes | ID of the target Subject. |
properties | object | No | Key-value relation properties. Present only when non-empty. |
With relation properties:
{
"id": "r1demo5rrrrrrr1",
"target": "s1demo4sssssss1",
"properties": {
"role": "CEO",
"since": 2019
}
}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.
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 field.
?expand=pageaddspageId,pageTitle, andpageNamespaceIdto each Subject.pageTitleis the full page title with namespace prefix (e.g.Help:Installation);pageNamespaceIdis the canonical MediaWiki namespace ID (e.g.0for the main namespace,12for Help).?expand=relationsembeds the Subjects this one's relations target; see REST API for the shape.?revisionId=returns the Subject as of that MediaWiki revision; an unknown or unreadable revision returns404.
Creating Subjects
POST /rest.php/neowiki/v0/page/{pageId}/mainSubject and .../childSubjects create a Subject on a page. The body takes label, schema, and statements (all required), plus an optional comment edit summary.
The server mints the Subject ID unless you pass one:
| Field | Required | Notes |
|---|---|---|
id | No | Subject 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. |
Writing Subjects
PUT /rest.php/neowiki/v0/subject/{subjectId} replaces the Subject's label and statements:
{
"label": "Updated Label",
"statements": {
"Founded at": {
"propertyType": "number",
"value": 2019
}
},
"comment": "Optional edit summary"
}| Field | Required | Notes |
|---|---|---|
label | Yes | Non-empty after trim. |
statements | Yes | Map of property name to Statement; omitted names are deleted. Pass {} to clear all. |
comment | No | Edit 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, schema, and page fields are immutable and ignored if sent.
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:
{
"statement": {
"propertyType": "url",
"value": ["https://example.com"]
},
"comment": "Optional edit summary"
}| Field | Required | Notes |
|---|---|---|
statement | Yes | A 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. |
comment | No | Edit 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}.
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:
{
"status": "updated",
"subjectId": "s1demo5sssssss1",
"violations": [],
"subject": { "id": "s1demo5sssssss1", "label": "Updated Label", "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 a child Subject for population data:
{
"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"]
}
}
}
}
}