Ontologies

An ontology is your team's live schema — a completed template instantiated for real product data. Creating one deep-copies the template's structure (regenerating every property id) and automatically creates the root passport at the top level, so you can start adding data immediately. There's no separate "activate" step: an ontology is live the moment it's created.

The ontology model

  • Name
    object
    Type
    string
    Description

    Always ontology.

  • Name
    id
    Type
    string
    Description

    Unique identifier, e.g. ontology_00a1b2c3d4e5f607.

  • Name
    name
    Type
    string
    Description

    Display name (3–255 characters). Nullable.

  • Name
    description
    Type
    string
    Description

    Optional description (up to 1000 characters). Nullable.

  • Name
    template
    Type
    string
    Description

    The id of the template this ontology was instantiated from. Nullable.

  • Name
    levels
    Type
    array
    Description

    The schema tree — an array of level objects (see below), always the latest version's. This is your reference for level keys and property ids.

  • Name
    latest_version
    Type
    integer
    Description

    The highest published version number.

  • Name
    has_draft
    Type
    boolean
    Description

    Whether an open draft exists.

  • Name
    metadata
    Type
    object
    Description

    Caller-owned metadata bag. Always present, {} when empty.

  • Name
    created_at
    Type
    string
    Description

    ISO 8601 timestamp of creation.

  • Name
    updated_at
    Type
    string
    Description

    ISO 8601 timestamp of the last update.

The level object

Each entry in levels describes one level of the schema. See the Data model for how the type values behave.

  • Name
    key
    Type
    string
    Description

    Level key — A is the root; children follow breadth-first (B, C, …). This is what you pass as level when creating passports and imports.

  • Name
    name
    Type
    string
    Description

    Human-readable level name. Nullable.

  • Name
    type
    Type
    string
    Description

    singular, series, many, or composite. Nullable.

  • Name
    is_root
    Type
    boolean
    Description

    Whether this is the root level.

  • Name
    static_lineage
    Type
    boolean
    Description

    Whether every ancestor of this level is singular (affects how paths are encoded). Nullable.

  • Name
    parent
    Type
    string
    Description

    The parent level key, or null for the root.

  • Name
    children
    Type
    array
    Description

    Child level keys.

  • Name
    discoverable
    Type
    array
    Description

    The child levels traversed when rendering the public passport (series children are excluded).

  • Name
    properties
    Type
    array
    Description

    Property definitions — the same shape as a template's property definitions: id, name, type, and value constraints. The id is what you use in passport values and import mappings.


GET/v1/ontologies

List ontologies

Lists your team's ontologies as a paginated list. Requires ontologies:read.

Optional attributes

  • Name
    created[gte]
    Type
    string
    Description

    Only ontologies created at or after this time. Unix seconds or an ISO date, inclusive of the whole second named.

  • Name
    created[lte]
    Type
    string
    Description

    Only ontologies created at or before this time. An inverted range is 400.

  • Name
    limit
    Type
    integer
    Description

    Page size, 1–100 (default 10).

  • Name
    cursor
    Type
    string
    Description

    A pagination cursor from a previous response's next_cursor.

The creation window runs as a true key condition, so pages stay exact — this list never comes back short because of the filter. Filters join the cursor's scope; see pagination.

Request

GET
/v1/ontologies
curl -G https://api.synexcloud.com/v1/ontologies \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -d limit=10

Response

{
  "object": "list",
  "data": [
    {
      "object": "ontology",
      "id": "ontology_00a1b2c3d4e5f607",
      "name": "EV Battery Line A",
      "description": "2026 production",
      "template": "template_00a1b2c3d4e5f607",
      "levels": [
        { "key": "A", "name": "SKU", "type": "singular", "is_root": true }
      ],
      "created_at": "2026-07-10T12:00:00Z",
      "updated_at": "2026-07-10T12:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "url": "/v1/ontologies"
}

POST/v1/ontologies

Create an ontology

Instantiates a completed template — yours or a public one — into a live ontology. The root passport is created automatically and returned as root_passport; use its id as the parent for your first child passports. Draft templates are rejected with 409 template_is_draft. Requires ontologies:write. Accepts an optional Idempotency-Key header.

Required attributes

  • Name
    template
    Type
    string
    Description

    The completed template's id.

  • Name
    name
    Type
    string
    Description

    Display name, 3–255 characters.

Optional attributes

  • Name
    description
    Type
    string
    Description

    Description, up to 1000 characters.

Request

POST
/v1/ontologies
curl https://api.synexcloud.com/v1/ontologies \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "template": "template_00a1b2c3d4e5f607", "name": "EV Battery Line A", "description": "2026 production" }'

Response

{
  "object": "ontology",
  "id": "ontology_00a1b2c3d4e5f607",
  "name": "EV Battery Line A",
  "description": "2026 production",
  "template": "template_00a1b2c3d4e5f607",
  "levels": [
    { "key": "A", "name": "SKU", "type": "singular", "is_root": true,
      "children": ["B"], "properties": [] },
    { "key": "B", "name": "Pack", "type": "series", "parent": "A",
      "properties": [
        { "id": "property_9f2k7d", "name": "Capacity", "type": "measurement" }
      ] }
  ],
  "root_passport": {
    "object": "passport",
    "id": "pass_00a1b2c3d4e5f607",
    "ontology": "ontology_00a1b2c3d4e5f607",
    "parent": null,
    "level": "A",
    "path": "A",
    "publish_status": null,
    "values": {}
  },
  "created_at": "2026-07-10T12:00:00Z",
  "updated_at": "2026-07-10T12:00:00Z"
}

GET/v1/ontologies/:id

Retrieve an ontology

Returns the ontology including its full levels — level keys, types, and property definitions. This is where you read the property ids you'll write to in passport values and reference in import mappings. Requires ontologies:read.

levels here is always the latest version's. To resolve an older passport's schema, read its pinned version with GET /v1/ontologies/{ontology}/versions/{version}.

Request

GET
/v1/ontologies/ontology_00a1b2c3d4e5f607
curl https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607 \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "ontology",
  "id": "ontology_00a1b2c3d4e5f607",
  "name": "EV Battery Line A",
  "description": "2026 production",
  "template": "template_00a1b2c3d4e5f607",
  "levels": [
    {
      "key": "A", "name": "SKU", "type": "singular", "is_root": true,
      "static_lineage": true, "parent": null, "children": ["B"],
      "discoverable": ["B"], "properties": []
    },
    {
      "key": "B", "name": "Pack", "type": "series", "is_root": false,
      "static_lineage": true, "parent": "A", "children": ["C"],
      "discoverable": ["A"],
      "properties": [
        { "id": "property_9f2k7d", "name": "Capacity", "type": "measurement" }
      ]
    }
  ],
  "latest_version": 2,
  "has_draft": false,
  "metadata": {},
  "created_at": "2026-07-10T12:00:00Z",
  "updated_at": "2026-07-10T12:00:00Z"
}

PATCH/v1/ontologies/:id

Update an ontology

Updates the metadata bag and nothing else. Requires ontologies:write.

That is the whole endpoint. An ontology's name, description and structure come from its template and change only through the draft/version lifecycle, where every edit is checked against the passports already issued under the old schema. This is not a second, unversioned way in.

metadata must be present in the body — an empty body is 400 parameter_invalid naming metadata — so send {} for a deliberate no-op.

Required attributes

  • Name
    metadata
    Type
    object
    Description

    Merged per key; a null value deletes that key.

Request

PATCH
/v1/ontologies/ontology_00a1b2c3d4e5f607
curl -X PATCH https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607 \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metadata": {"erp_line": "L-14"}}'

Response

{
  "object": "ontology",
  "id": "ontology_00a1b2c3d4e5f607",
  "name": "EV Battery Line A",
  "description": "2026 production",
  "template": "template_00a1b2c3d4e5f607",
  "levels": [
    { "key": "A", "name": "SKU", "type": "singular", "is_root": true }
  ],
  "latest_version": 2,
  "has_draft": false,
  "metadata": { "erp_line": "L-14" },
  "created_at": "2026-07-10T12:00:00Z",
  "updated_at": "2026-07-28T14:02:19Z"
}

Versioning

A schema evolves without breaking the passports already issued under it. Versions are append-only: publishing adds version N+1 and never alters or removes an existing one.

Every passport is pinned to the version it was created under, exposed as ontology_version. That pin decides two things:

  • Reads render the passport against its own version's levels — its discoverable_levels, discovery chain and property definitions are the ones that existed when it was issued.
  • PATCH /v1/passports/{passport} validates values against its own version's property definitions. A property added in a later version is rejected on an older-pinned passport.

Changes must be additive

The draft is validated against the published version before it can be saved or published. A violation is 400 structure_invalid, with one message per violation in error.errors.structure:

  • published levels cannot be deleted, re-parented, re-typed, or re-lettered;
  • a published parent/child edge cannot be dropped;
  • a property carried over from a published version must keep its id;
  • the graph stays a single tree rooted at A.

Adding levels and adding, editing or removing properties are all fine.

Level keys are assigned by the server

This is the part that most often surprises people. You do not choose level keys, and you do not choose ids for new properties.

  • Send any placeholder key you like for a new level. The server assigns the next unused letter — C, D, … AA — and returns it in the response's levels. Read the real key back from there; the placeholder is gone.
  • A key that already exists in the published schema or the current draft is kept as-is. Existing levels are never re-lettered — every passport path in the system depends on that.
  • One trap: a placeholder that looks like a level key (^[A-Z]+$) but is not one of the known keys is treated as a placeholder and silently re-lettered. Use obviously non-key placeholders ("new-module", "tmp-1") so you never have to think about it.
  • Property ids work the same way: omit id for a new property and one is minted; carry an existing id through verbatim to keep the values already stored against it.

Concurrency

Three guards, three different 409s:

CodeMeaningWhat to do
draft_existsPOST /draft when one is already openGET it, or DELETE it first
draft_conflictPATCH /draft with a stale base_updated_atRe-read the draft, reapply your changes
version_conflictpublish with a stale expected_latest_versionRe-read latest_version and retry

base_updated_at is an opaque token, not a timestamp you should read. Echo it back exactly as received; do not parse, reformat, or compare it. Every draft response carries a fresh one.

PATCH /draft is deliberately not covered by idempotency keysbase_updated_at is already its concurrency control. The two POSTs are: note that a replayed POST /draft returns the stored response, whose base_updated_at may since have gone stale, so if a following PATCH returns draft_conflict, re-read the draft with GET.

Ontology and version mutations do not emit audit events. Publishing does emit ontology.version.published on the change feed.


GET/v1/ontologies/:id/versions

List versions

Every published version of the ontology. Requires ontologies:read.

Versions are immutable once published, so this list only ever grows.

Request

GET
/v1/ontologies/ontology_00a1b2c3d4e5f607/versions
curl https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607/versions \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "list",
  "data": [
    {
      "object": "ontology.version",
      "ontology": "ontology_00a1b2c3d4e5f607",
      "version": 1,
      "status": "published",
      "published_at": "2026-07-10T12:00:00Z",
      "levels": [
        { "key": "A", "name": "SKU", "type": "singular", "is_root": true }
      ]
    },
    {
      "object": "ontology.version",
      "ontology": "ontology_00a1b2c3d4e5f607",
      "version": 2,
      "status": "published",
      "published_at": "2026-07-24T08:31:47Z",
      "levels": [
        { "key": "A", "name": "SKU", "type": "singular", "is_root": true },
        { "key": "C", "name": "Module", "type": "many", "parent": "A" }
      ]
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "url": "/v1/ontologies/ontology_00a1b2c3d4e5f607/versions"
}

GET/v1/ontologies/:id/versions/:version

Retrieve a version

One published version, with the schema exactly as passports pinned to it read it. Requires ontologies:read.

This is how you resolve an older passport's schema: take its ontology_version and read that version's levels.

Request

GET
/v1/ontologies/ontology_00a1b2c3d4e5f607/versions/1
curl https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607/versions/1 \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "ontology.version",
  "ontology": "ontology_00a1b2c3d4e5f607",
  "version": 1,
  "status": "published",
  "published_at": "2026-07-10T12:00:00Z",
  "levels": [
    {
      "key": "A", "name": "SKU", "type": "singular", "is_root": true,
      "static_lineage": true, "parent": null, "children": ["B"],
      "discoverable": ["B"], "properties": []
    },
    {
      "key": "B", "name": "Pack", "type": "series", "is_root": false,
      "static_lineage": true, "parent": "A", "children": [],
      "discoverable": ["A"],
      "properties": [
        { "id": "property_9f2k7d", "name": "Capacity", "type": "measurement" }
      ]
    }
  ]
}

The draft lifecycle

An ontology has at most one open draft at a time, always based on the latest published version.

  1. Open a draft — a copy of the latest published version.
  2. Edit it, sending the complete level map each time.
  3. Publish it as the next version, or delete it to throw the work away.

Deleting a draft never affects published versions.


GET/v1/ontologies/:id/draft

Retrieve the draft

Reads the open draft. Requires ontologies:read. 404 draft_missing when none is open.

Every draft response carries a fresh base_updated_at — echo it back verbatim on your next PATCH.

Request

GET
/v1/ontologies/ontology_00a1b2c3d4e5f607/draft
curl https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607/draft \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "ontology.draft",
  "ontology": "ontology_00a1b2c3d4e5f607",
  "status": "draft",
  "created_from_version": 2,
  "base_updated_at": "1753699847.482913",
  "latest_version": 2,
  "levels": [
    { "key": "A", "name": "SKU", "type": "singular", "is_root": true,
      "children": ["B", "C"], "properties": [] },
    { "key": "C", "name": "Module", "type": "many", "parent": "A",
      "properties": [
        { "id": "property_4c8b1e", "name": "Slot", "type": "short_text" }
      ] }
  ]
}

POST/v1/ontologies/:id/draft

Open a draft

Copies the latest published version into a new draft. Requires ontologies:write. Accepts an optional Idempotency-Key header.

409 draft_exists if one is already open — GET it, or DELETE it first.

A replayed POST returns the stored response, whose base_updated_at may since have gone stale. If a following PATCH returns draft_conflict, re-read the draft with GET.

Request

POST
/v1/ontologies/ontology_00a1b2c3d4e5f607/draft
curl -X POST https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607/draft \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "ontology.draft",
  "ontology": "ontology_00a1b2c3d4e5f607",
  "status": "draft",
  "created_from_version": 2,
  "base_updated_at": "1753699847.482913",
  "latest_version": 2,
  "levels": [
    { "key": "A", "name": "SKU", "type": "singular", "is_root": true,
      "children": ["B"], "properties": [] },
    { "key": "B", "name": "Pack", "type": "series", "parent": "A",
      "properties": [
        { "id": "property_9f2k7d", "name": "Capacity", "type": "measurement" }
      ] }
  ]
}

PATCH/v1/ontologies/:id/draft

Edit the draft

Replaces the draft's structure. Requires ontologies:write.

Changes are validated against the published version on every save, so a violation surfaces here rather than at publish time.

Required attributes

  • Name
    base_updated_at
    Type
    string
    Description

    The opaque token from the previous draft response, echoed verbatim. A mismatch is 409 draft_conflict.

  • Name
    structure
    Type
    object
    Description

    The complete level map, keyed by level key. New levels may use any placeholder key — the server assigns the real one.

Request

PATCH
/v1/ontologies/ontology_00a1b2c3d4e5f607/draft
curl -X PATCH https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607/draft \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "base_updated_at": "1753699847.482913",
    "structure": {
      "A": {"name": "SKU", "type": "singular", "is_root": true,
            "properties": [], "children": ["B", "new-module"]},
      "B": {"name": "Pack", "type": "series", "children": [],
            "properties": [
              {"id": "property_9f2k7d", "name": "Capacity", "type": "measurement"}
            ]},
      "new-module": {"name": "Module", "type": "many", "children": [],
                     "properties": [{"name": "Slot", "type": "short_text"}]}
    }
  }'

Response

{
  "object": "ontology.draft",
  "ontology": "ontology_00a1b2c3d4e5f607",
  "status": "draft",
  "created_from_version": 2,
  "base_updated_at": "1753700114.907255",
  "latest_version": 2,
  "levels": [
    { "key": "A", "name": "SKU", "type": "singular", "is_root": true,
      "children": ["B", "C"], "properties": [] },
    { "key": "B", "name": "Pack", "type": "series", "parent": "A",
      "children": [],
      "properties": [
        { "id": "property_9f2k7d", "name": "Capacity", "type": "measurement" }
      ] },
    { "key": "C", "name": "Module", "type": "many", "parent": "A",
      "children": [],
      "properties": [
        { "id": "property_4c8b1e", "name": "Slot", "type": "short_text" }
      ] }
  ]
}

The placeholder new-module came back as level C, with a minted property_4c8b1e. Read both from the response — the values you sent are not authoritative.


DELETE/v1/ontologies/:id/draft

Discard the draft

Throws the draft away. Requires ontologies:write. Published versions are never affected.

Request

DELETE
/v1/ontologies/ontology_00a1b2c3d4e5f607/draft
curl -X DELETE https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607/draft \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "ontology.draft",
  "id": "ontology_00a1b2c3d4e5f607",
  "deleted": true
}

POST/v1/ontologies/:id/draft/publish

Publish the draft

Turns the draft into version N+1 and closes it. Requires ontologies:write. Accepts an optional Idempotency-Key header.

expected_latest_version is the concurrency guard: pass the latest_version you built the draft against, and a mismatch is 409 version_conflict. Re-read latest_version and retry.

Publishing emits ontology.version.published on the change feed; the snapshot carries the version payload including its number.

Existing passports are untouched — they stay pinned to the version they were created under. New passports created from now on pin the new one.

Required attributes

  • Name
    expected_latest_version
    Type
    integer
    Description

    The version number you expect to be current. A mismatch is 409 version_conflict.

Request

POST
/v1/ontologies/ontology_00a1b2c3d4e5f607/draft/publish
curl -X POST https://api.synexcloud.com/v1/ontologies/ontology_00a1b2c3d4e5f607/draft/publish \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"expected_latest_version": 2}'

Response

{
  "object": "ontology.version",
  "ontology": "ontology_00a1b2c3d4e5f607",
  "version": 3,
  "status": "published",
  "published_at": "2026-07-28T14:20:05Z",
  "levels": [
    { "key": "A", "name": "SKU", "type": "singular", "is_root": true,
      "children": ["B", "C"], "properties": [] },
    { "key": "B", "name": "Pack", "type": "series", "parent": "A",
      "properties": [
        { "id": "property_9f2k7d", "name": "Capacity", "type": "measurement" }
      ] },
    { "key": "C", "name": "Module", "type": "many", "parent": "A",
      "properties": [
        { "id": "property_4c8b1e", "name": "Slot", "type": "short_text" }
      ] }
  ]
}

Was this page helpful?