Ontology API reference
client.ontology is available on both backends since 0.7.0: NamsOntology (hosted, REST) and BoltOntology (your own Neo4j, stored as (:Ontology)-[:HAS_VERSION]→(:OntologyVersion)). Both satisfy the neo4j_agent_memory.ontology.OntologyAPI protocol, so the signatures below are backend-independent; the Bolt column records where the behavior differs. The TypeScript SDK targets NAMS only; it has no Bolt transport.
Methods
| Method | Semantics | Bolt |
|---|---|---|
Python: |
System templates + workspace-owned ontologies; the active one is flagged ( |
The eight built-in domain templates come first, as |
Python: |
One ontology with its full revision history ( |
Versions sorted by ascending revision. A |
Python: |
Parsed active document plus binding metadata. Raises |
Per database. Never falls back to the built-in POLE+O ontology; the client’s connect-time resolution applies that fallback itself. |
Python: |
Editable workspace copy of a system template; returns the rev-1 version. |
The copy takes the template’s name, or |
Python: |
New workspace ontology from a schema document; returns the rev-1 version. Identity comes from |
|
Python: |
New immutable revision (n+1); returns it. Does not change which version is active. |
|
Python: |
Bind the specified version to the workspace. Validation enforcement is a service behavior, separate from successful activation. |
Swaps the active flag in a single write, so exactly one version per database is active even under concurrent activation. Takes effect for clients that connect after it. |
Python: |
Delete a workspace-owned ontology. |
Also deletes its revisions and |
Python: |
Convert an external graph/ontology document into a non-persisted draft ( |
Converts locally: |
Python: |
Structural diff between two revisions of an ontology ( |
Same shape, computed locally by |
Python: |
Enqueue an async label-rename migration from one version to another; returns a |
Runs synchronously (see |
Python: |
Poll a migration job’s status and progress ( |
Reads back the persisted |
migrate on Bolt
migrate executes one transaction per (from_label, to_label) pair, inline. For each pair it removes the from label, adds the to label, sets e.subtype from the target version’s entity type, and rewrites e.type when the target label maps onto a different pole_type. The node’s old type label (when the type changes) and old built-in subtype label are removed, judged from each node’s own stored type and subtype, and the target’s type and built-in subtype labels are added, so the node ends up with the labels a fresh write would give it. Labels are matched and written in the form the write paths give them: PascalCase that keeps the label’s own capitals (SupportCase stays SupportCase, tv_show becomes TvShow). type_mappings accepts (from, to) tuples or {"from": …, "to": …} dicts.
dry_run=True counts matching nodes (total) and mutates nothing (processed = 0). A failed step is recorded on the job (errored, error_message, status = "failed") rather than raised. Either way the job is persisted as an (:OntologyMigration) node.
NotFoundError if either version id is unknown. ValueError for a malformed mapping, an invalid Neo4j label, a target label the target version does not declare, or a source label the source version does not declare when the target declares no subtype. On a large graph, prefer a maintenance window, or run the equivalent relabel in batches with apoc.periodic.iterate. For a complete rename-and-migrate run, see Rename an ontology type and migrate a Bolt graph.
Return types
list() returns OntologySummary[]. get() returns Ontology ({record,
versions[]}). get_active() returns ActiveOntology ({document,
validation_mode?, revision?, ontology_id?, version_id?, schema_hash?};
TypeScript uses the camelCase names, and its 0.5.0 release has no
schemaHash). clone / create / update / activate return an
OntologyVersion.
import_() returns an OntologyImportResult ({document?, warnings[]
(ImportWarning), detected_format?, suggested_name?}). diff() returns an
OntologyDiff ({from_revision, to_revision, entity_types, relationships,
mode_change?}). migrate() and get_migration() return a MigrationJob
({id, status (pending|running|completed|failed|paused), total, processed,
errored, …}).
Active binding in the released SDKs
The GET /ontologies/active response carries a version object (id,
ontology_id, revision, validation_mode, …) that identifies the bound
revision, which can be older than the ontology’s latest revision.
Python neo4j-agent-memory==0.7.0 reads that object. The ActiveOntology
returned by get_active() takes version_id, ontology_id, revision,
validation_mode and schema_hash from it, so they name the bound revision.
When a response carries no version object, those fields are None: the
binding is unknown, and the SDK never substitutes the latest revision.
Version metadata that is present but malformed, or whose schema_json
differs from the active document, raises ValueError. On Bolt, get_active()
reads the active (:OntologyVersion) node directly.
Python 0.6.0 and TypeScript @neo4j-labs/[email protected] ignore the
version object: get_active() / getActive() compose the version metadata
from a second list() / get() lookup, which picks the ontology’s current
(latest) revision rather than the bound one. When an older revision is active,
the returned version_id / versionId, revision and validation_mode /
validationMode describe the latest revision instead. With those releases, do
not use them to capture a rollback target or certify restoration.
The hosted Python lessons read GET /ontologies/active themselves:
-
Python: the self-contained ontology lesson uses
read_active_binding(http)fromhosted_tutorial_helpers.py. It uses the same resolved NAMS settings as the SDK, validates the authoritativeversionand schema, and stops before mutation if the binding is incomplete or missing. See the activation recipe for its imports and client lifecycle. The helper is example code displayed on those pages, not an SDK method. -
TypeScript 0.5.0: send the same request with the workspace API key and read
versionfrom the JSON body. The response’sversionuses the snake_case wire names.const response = await fetch("https://memory.neo4jlabs.com/v1/ontologies/active", { headers: { Authorization: `Bearer ${process.env.MEMORY_API_KEY}` }, }); if (!response.ok) throw new Error(`GET /ontologies/active: ${response.status}`); const { version } = (await response.json()) as { ontology: unknown; version?: { id: string; ontology_id: string; revision: number; validation_mode: string }; }; // version.id, version.revision and version.validation_mode name the bound revision.
The TypeScript repository source also reads the authoritative version (and
adds schemaHash to ActiveOntology); that change is not in the 0.5.0
release.
Import formats
import_() accepts these format ids (or auto / omit to detect from content):
format |
Source | Bolt |
|---|---|---|
|
Arrows.app JSON |
Yes |
|
Neo4j Data Importer model |
|
|
Cypher DDL ( |
|
|
GraphQL SDL type definitions |
|
|
LinkML schema |
|
|
RDF / OWL (parsed server-side; the extraction service must be reachable) |
|
|
The library’s own ontology JSON/YAML (NAMS aliases |
Yes |
|
Explicit parsers for a native or arrows body |
Yes (Bolt only) |
|
Detect from content (the default when |
Yes |
On Bolt, auto, json and yaml pick the arrows converter when the parsed body has top-level nodes and relationships keys, and the native path otherwise; the native path also accepts a legacy EntitySchemaConfig body and converts it. url= raises NotSupportedError on Bolt, because the service’s fetch is SSRF-guarded and size-capped and a local converter cannot promise the same; download the document and pass it as content=.
Ontology document schema
| Field | Shape | Notes |
|---|---|---|
|
|
Identity + display metadata. |
|
|
|
|
|
|
|
|
|
|
|
Applied per relation, so an explicit |
Parsing is deliberately lenient (unknown fields are ignored and every extension field has a default), so any document that parsed before 0.7.0 still parses.
Extension fields
These fields, added in 0.7.0, drive the local extraction and resolution pipeline on Bolt, where the stored document keeps all of them. NamsOntology.create / update send an extension field only when it differs from its default, so a document without extensions keeps its pre-0.7.0 request shape. Whether the service keeps fields it does not model is not verified for this release, and on NAMS extraction and resolution run server-side against the service’s own engine.
| Field | On | Effect |
|---|---|---|
|
entity type / relationship / property |
Serialized into the GLiNER2.5 encoder input as a zero-shot annotation guideline, and rendered into the LLM extractor’s prompt. Write negative cases into it ("Not a job title."). |
|
entity type |
Canonical name → known surface forms. The resolver’s gazetteer: a blocking key and a |
|
entity type |
Per-label confidence floor for the decoder. |
|
entity type |
Per-type overrides of |
|
relationship |
Per-relation confidence floor for JointIE (the model cards suggest about 0.6; 0.15–0.35 measured better on crowded relations). |
|
relationship |
Compile to JointIE’s |
|
relationship |
Forbids cycles during decoding. |
|
relationship |
Permits a self-loop. Only valid when |
|
relationship |
Names the mirror relationship. |
validate_structure() is the single validator. It returns a list of human-readable problems (duplicate labels, a pole_type outside POLE+O, an undeclared relationship endpoint, a duplicate relationship triple, a bad allow_self, a non-mirroring inverse), and BoltOntology.create / update and compile_joint_schema call it and raise ValueError when it reports any. content_key() returns a deterministic content-addressed key used to cache compiled schemas by content rather than by name.
The OntologyVersion carries id, ontology_id, revision,
validation_mode (permissive | strict), the parsed document,
schema_hash, created_at, and message.
REST endpoints
These apply to NAMS only; BoltOntology issues Cypher instead. The SDK targets these workspace-scoped endpoints (under the versioned base, e.g.
/v1). Request bodies use snake_case. See
the OpenAPI spec for exact
request/response shapes.
| Method | Path | Maps to |
|---|---|---|
GET |
|
|
GET |
|
|
GET |
|
|
POST |
|
|
POST |
|
|
PUT |
|
|
POST |
|
|
DELETE |
|
|
POST |
|
|
GET |
|
|
POST |
|
|
GET |
|
|
|
|
Errors
| Condition | Exception |
|---|---|
Strict-mode validation rejection |
|
No active ontology bound |
|
Active version metadata malformed, or its schema differs from the active document (NAMS) |
|
Structurally invalid document passed to Bolt |
|
|
|
|
|
Unknown ontology, version, template or migration id on Bolt |
|
Stored document unreadable, or the migration record could not be written (Bolt) |
|
System templates
~28 system templates ship on the service (each is_system = true, revision 1).
nams-default is bound until a workspace activates its own:
agent-memory, conservation, cybersecurity, data-journalism,
digital-twin, education, financial-services, gaming, genai-llm-ops,
gis-cartography, golf-sports, government, healthcare, hospitality,
legal, manufacturing, nams-default, oil-gas, options-intelligence,
personal-knowledge, product-management, real-estate, retail-ecommerce,
scientific-research, software-engineering, trip-planning,
vacation-industry, wildlife-management.
On Bolt the templates are the eight built-in domain templates (poleo, podcast, news, scientific, business, entertainment, medical, legal), listed with an id of template:<name>. poleo is the curated POLE+O ontology (5 labels, 16 relationship types, annotation guidelines and decoding constraints) and is the library default.
Runnable examples
-
examples/ontology-lifecycle/— Python, NAMS:import_,activate,diff,migrateandget_migrationpolling, with aquery.cypherread-back. -
typescript/examples/ontology-lifecycle/— the same lifecycle throughOntologyClient. -
examples/ontology-extraction/— Python, Bolt, no API keys: author an ontology,createandactivateit, ingest with alias variation, read the typed edges back, thenupdateanddiff.
See also
-
Ontologies — the concept: templates, revisions, validation modes, and the Bolt and NAMS mechanics.
-
Use ontologies — the NAMS activation recipe.
-
Drive extraction from an ontology — the Bolt workflow.
-
NAMS limits and behavior — the import/preview rate limit.