> Source: https://wexa.ai/docs/concepts/platform-schema

# Platform schema

The **platform schema** is the fixed, platform-wide vocabulary of node and relationship types that
every project shares. It is defined once, in the platform's own source, when the platform is built.
It cannot be created, edited or deleted at runtime — by anyone, through any surface, in any project
mode.

It is always named in full. It is never called an ontology, because that word is reserved for the
vocabulary a project owns and can change: the
[project ontology](/docs/concepts/project-ontology). Two different things share one word in
conversation, and this page exists because getting them the wrong way round produces a failure that
reads exactly like a bug.

## The short version

You are always editing the project ontology. There is no call that edits the platform schema, so
you will never be told "you may not edit the platform schema" — no such request exists to refuse.

What actually goes wrong is narrower and easier to miss: you choose a **name** that the platform
already owns. What happens next depends on which call you made, and in one of the three cases it
comes back as a success.

## What the platform schema contains

Two families of type, both platform-managed and neither of them yours to redefine.

**The canonical vocabulary.** The node and relationship types the platform models the world with —
people, systems, processes, context, time, provenance and the relationships between them. They are
organized into layers: the lower layers are *base constructs*, protected against redefinition, and
the upper layer is deliberately left empty for the specializations that arrive with your own data.
That empty layer is the room your project ontology is meant to occupy.

**The data-estate catalog types.** The types the [data catalog](/docs/concepts/data-catalog) uses
to describe assets that live outside Wexa: `DataService`, `Database`, `Table`, `Column`,
`Dashboard`, `Pipeline`, `MLModel`, `GlossaryTerm`, `Classification` and `DataOwner`. These carry the
platform's own semantics: lineage and quality are computed against them, so a project that redefined
`Table` to mean something of its own would break the catalog for that project in a way nothing would
report.

The names in that second family are the ones most likely to collide with a name you would pick
yourself, and they are the usual cause of everything below.

## The three failures, and which call produces which

### Declaring a platform catalog type through `create-ontology`

Declaring a node type whose label is one of the data-estate catalog types, under any category other
than the reserved `data` category, is refused during validation, before anything is written. This
check runs in both project modes. The message names the position and the label:

```text
node[0] label "Table" is reserved for the platform data-estate catalog
```

This one is honest about itself. It is an error, it names the type, and it tells you the reason.

### Writing a platform-managed label through `save-context`

A **different** set of labels is platform-managed for *data* writes — `Organization`, `Project`,
`User`, `Connector`, `Skill` and others the platform maintains for itself. The two sets have no
members in common, which is the part worth remembering: the label `save-context` refuses is not the
label `create-ontology` refuses, so testing one and generalizing from it will mislead you. Saving a
node with one of these labels, or a relationship with one of them at either end, is refused with a
message that also says what to do instead:

```text
node[0]: "User" is a platform-managed type and cannot be written through save-context —
use create-ontology to change the ontology, or choose a different label for your own data
```

Note what that message is *not* saying. It is not saying the platform schema can be changed with
`create-ontology`. It is saying that if what you wanted was a type of your own, declare it in your
project ontology under a name that is yours.

### The one that looks like success

The two refusals above are the names the platform reserves at the edge, where validation can see
them. They are not the whole list. Some names are owned further in, by the service that actually
stores the vocabulary, and a declaration that uses one of those clears validation and is simply not
stored.

In Simple mode that case is caught, but only after the fact: `create-ontology` does not stage
anything, so it ensures the types and then reads the project ontology back to report what is
genuinely there rather than echoing what you sent. If a label you declared did not survive, the call
returns **HTTP 200** with:

```json
{
  "status": "partially_committed",
  "missing_labels": ["YourLabel"],
  "note": "…"
}
```

The note it carries says it plainly: these labels are not in the project ontology after the commit,
they were declared but not stored, usually because the name is one the platform already owns — so
rename them and retry. A name the platform owns is the common cause but not the only one; a label
that breaks the PascalCase convention can produce the same result, so check the spelling of the
label as well as the word before concluding the name is taken.

There is a fourth status, `committed_unverified`. That one is not about naming at all: it means the
write was accepted but reading the vocabulary back afterwards failed, so the result cannot confirm
the labels are there. Treat it as "probably fine, unconfirmed" and check again rather than as a
refusal.

## Why it is fixed

Three reasons, and they are the reasons a runtime write path was not built rather than reasons it
was built and then locked.

**It is what "shared" means.** The platform schema is the vocabulary every project has in common. A
runtime edit in one project either leaks into the others — which breaks isolation — or does not,
which means it was never the platform schema in the first place. There is no third option, so the
edit does not exist.

**Platform behaviour is written against it.** Lineage traversal, quality scorecards, classification
and the relationship allow-list all read these types by name. They are compiled against the
vocabulary, not configured with it, and a type whose meaning could change underneath them would
make every one of those features conditionally correct.

**A project has somewhere better to put the change.** Nothing is lost by the restriction, because
the thing a reader usually wants — a type of their own, with their own properties, related to
things the platform already models — is exactly what the project ontology is for. The platform
schema is the floor, not the ceiling.

## What to do instead

If a name you wanted is taken, rename yours. Prefix it with your domain or your organization —
`AcmeTable`, `WarehouseTable`, `ContractTable` — and declare it in your
[project ontology](/docs/concepts/project-ontology). Relate it to the platform's types rather than
replacing them: a relationship from your own type to a `Table` gives you your meaning and keeps the
catalog's.

If what you actually wanted was to describe a real warehouse table, you did not want a type at all
— you wanted the [data catalog](/docs/concepts/data-catalog), which already models it and keeps its
lineage and ownership current.

And if you are not sure which of the project's four stores you are dealing with, that question has
its own page: [where your data goes](/docs/concepts/where-your-data-goes).
