Skip to main content

Setting Up the Knowledge Graph

This tutorial walks through configuring a knowledge graph in the Enterprise Context Engine: creating a graph database, uploading the ontology that defines its entity types and relationships, and routing documents to that ontology with condition expressions. It ends with verifying the configuration and deleting graph databases and ontologies safely.

Prerequisites​

  • A valid access token (see Authentication)
  • The base URL of your environment
  • The permissions listed in Access Requirements
  • An ontology definition file in YAML (.yaml or .yml)

Set two variables before running the curl examples. The HTTP examples use the equivalent {{baseUrl}} and {{accessToken}} placeholders.

export BASE_URL="https://{environment_id}.content-lake.ai.experience.hyland.com"
export TOKEN="<access token from the identity provider>"

Tutorial Overview​

In this tutorial, you'll learn to:

  1. Choose a graph database version
  2. Create a graph database
  3. Upload an ontology
  4. Route documents to the ontology
  5. Verify the configuration
  6. List and download ontologies
  7. Delete ontologies and graph databases safely

Access Requirements​

  • All endpoints require an authenticated caller with the permission listed per endpoint below. A caller without the required permission receives 401 Unauthorized.
  • The Content Lake ECE Administrator role carries all of these permissions; Content Lake ECE User carries only content-lake-api.graph-configs.read (see Roles and Scopes).
OperationRequired permission
GET /ontologies, GET /ontologies/{ontologyId}, GET /graphdbs/{graphDBId}/configcontent-lake-api.graph-configs.read
POST /ontologiescontent-lake-api.graph-configs.create
DELETE /ontologies/{ontologyId}content-lake-api.graph-configs.delete
PUT /graphdbs/{graphDBId}/ontologyroutescontent-lake-api.graph-configs.edit
POST /graphdbscontent-lake-api.graph-configs.create
DELETE /graphdbs/{graphDBId}content-lake-api.graph-configs.delete
GET /graphdbs, GET /graphdbs/{graphDBId}content-lake-api.graph-configs.read

Errors use the standard Cloud Content Repository error body, {"entity-type":"exception","status":<code>,"message":"..."}. The exceptions are noted below: some 400 and 404 responses have an empty body. Requests whose JSON can't be mapped onto the expected shape (an unknown version, both graphDBName and domainName, and so on) get the generic message "Invalid input".


Step 1: Choose a Graph Database Version​

A graph database's version selects its query engine and access-control model. It is set when the graph database is created.

VersionEngineAccess control
v2 (recommended)GraphQL schemaACLs are enforced. Results are scoped to the caller's permissions.
v1 (deprecated)DQL schemaNo ACL support. Restricted to administrators.
v1 is deprecated

v1 graph databases are deprecated and will not be supported in future. Create every new graph database as v2. Always send "version": "v2" explicitly: version currently still defaults to v1, and the default will change to v2.


Step 2: Create a Graph Database​

POST {{baseUrl}}/api/graph/graphdbs
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
Content-Type: application/json

{
"graphDBName": "my-knowledge-graph",
"version": "v2"
}

Parameters​

FieldTypeRequiredDescription
graphDBNamestringYesName for the new graph database. Must not be blank and must be unique.
versionstringNo"v2" (exact, lowercase). Always set it: it currently defaults to the deprecated "v1". See GraphDB Versions.
defaultGraphbooleanNoWhen true, makes this the tenant's default graph database (see The default graph database). Defaults to false. Only one graph database can be the default.

Permissions​

content-lake-api.graph-configs.create

Response​

201 Created with the created graph database:

{
"graphDBId": "3f2b6c1e-8d4a-4b7e-9a51-2c6e0f9d7a10",
"graphDBName": "my-knowledge-graph",
"version": "v2",
"defaultGraph": false
}

graphDBId is a generated UUID. No Location header is returned; take the ID from the body. defaultGraph is true only for the tenant's default graph database.

To make retries safe, send an optional Idempotency-Key header with a value unique to the request. The graph database is then created at most once per key, and a repeat call with the same key returns the original result, marked with the Idempotent-Replay response header. POST /api/graph/ontologies accepts the same header.

The default graph database​

A tenant can mark one graph database as its default by creating it with "defaultGraph": true. Wherever a path takes a {graphDBId} (for example GET /api/graph/graphdbs/{graphDBId}, /config, /ontologyroutes and DELETE), you can pass the literal default instead, and it resolves to the current default graph database.

More Examples​

Duplicate name

400 {"entity-type":"exception","status":400,"message":"GraphDBConfig with name 'my-knowledge-graph' already exists"}

Blank name ({"graphDBName": ""})

400 {"entity-type":"exception","status":400,"message":"GraphDBConfig name must not be null or blank"}

Unknown version ({"graphDBName": "x", "version": "1.0"}), or both name keys ({"graphDBName": "x", "domainName": "y"})

400 {"entity-type":"exception","status":400,"message":"Invalid input"}

Errors​

StatusWhen
400graphDBName is missing or blank: "GraphDBConfig name must not be null or blank".
400A graph database with the same name already exists: "GraphDBConfig with name '...' already exists".
400The maximum number of graph databases has been reached (100 by default): "Cannot create graphDB: tenant has reached the maximum of 100 graphDB instance(s). Delete an existing graphDB before creating a new one."
409defaultGraph is true but another graph database is already the default: "A default graphDB already exists; only one default graphDB is allowed at a time".
400version is not a valid version, or both graphDBName and the legacy domainName are sent: "Invalid input".

Step 3: Upload an Ontology​

Upload a new ontology definition file. The ontology must be in YAML format and uploaded as multipart/form-data.

POST {{baseUrl}}/api/graph/ontologies
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
Content-Type: multipart/form-data; boundary=----ece

------ece
Content-Disposition: form-data; name="ontologyName"

benefits-admin-ontology
------ece
Content-Disposition: form-data; name="description"

Administrative services agreements
------ece
Content-Disposition: form-data; name="file"; filename="benefits_admin_ontology.yaml"
Content-Type: application/x-yaml

< ./benefits_admin_ontology.yaml
------ece--

Upload Fields​

FieldTypeRequiredDescription
ontologyNamestringYesName of the ontology. Must not be blank and must be unique.
descriptionstringNoFree-text description of the ontology
filefile (YAML)YesThe ontology definition file. Only files with a .yaml or .yml extension (case-insensitive) are accepted. An empty file is rejected with 400 "Request part 'file' is missing or empty".
The file content is not validated on upload

Only the file extension is checked. A .yaml file that isn't valid YAML, or isn't a valid ontology, is still accepted with 201. Validate the file yourself before uploading it.

Response Example​

201 Created, with a Location header pointing to the new ontology:

HTTP/1.1 201
Location: /api/graph/ontologies/c28a73bd-3212-47d7-a782-f0f7daae3679

{
"ontologyId": "c28a73bd-3212-47d7-a782-f0f7daae3679",
"ontologyName": "benefits-admin-ontology",
"description": "Administrative services agreements",
"createdAt": "2026-10-01T11:35:18.109502204Z"
}

If no description part is sent, description is returned as an empty string ("").

Error Examples​

Missing or empty ontologyName. An empty value (-F "ontologyName=") is treated the same as leaving the part out:

400 {"entity-type":"exception","status":400,"message":"Required part 'ontologyName' is not present."}

No file part

400 {"entity-type":"exception","status":400,"message":"Required part 'file' is not present."}

Wrong file extension (-F "file=@notes.txt")

400 {"entity-type":"exception","status":400,"message":"Unsupported file type: 'notes.txt'. Only .yaml and .yml files are accepted."}

Name already used

409 {"entity-type":"exception","status":409,"message":"Ontology with name 'benefits-admin-ontology' already exists"}

Error Responses​

StatusDescription
400Missing or empty ontologyName part, missing or empty file part, or an unsupported file extension (messages above)
409An ontology with the given name already exists: "Ontology with name '<name>' already exists"
413Uploaded file exceeds the configured size limit

Step 4: Route Documents to the Ontology​

Replace all ontology routing rules for a graph database with the supplied list. Each rule pairs a boolean condition, evaluated against document properties, with the ontology to apply to documents that match it. An empty or omitted ontologyRoutes list clears all routes.

PUT {{baseUrl}}/api/graph/graphdbs/{graphDBId}/ontologyroutes
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
Content-Type: application/json

{
"ontologyRoutes": [
{
"condition": "content.sys_primaryType == \"SysFile\" && content.dc_type == \"Contract\"",
"ontologyId": "3f6c2a1e-8b4d-4c2f-9a7e-1d5b6c7e8f90"
},
{
"condition": "content.dc_type == \"Invoice\"",
"ontologyId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
}
]
}

Route Object​

FieldTypeRequiredDescription
conditionstringYesBoolean routing expression. Operators: == != < > <= >= && || ! + - * / ,, ternary ?:, grouping ( ), and list.contains(x). Use ==, not =, and && / ||, not AND / OR. Strings must be double-quoted. Document properties are referenced with the content. prefix (e.g. content.sys_primaryType, content.dc_type), and property paths use . (dc_type, not dc:type). The security properties sys_acl, sys_racl and sys_secpol are not available to conditions. Note that a condition referencing them is not rejected: sys_acl == "x" is accepted with 200, so check conditions yourself. See also condition limitations.
ontologyIdstringYesID of an ontology that is already registered

Response Example​

200 OK, returning the routes now in effect.

{
"ontologyRoutes": [
{
"condition": "content.sys_primaryType == \"SysFile\" && content.dc_type == \"Contract\"",
"ontologyId": "3f6c2a1e-8b4d-4c2f-9a7e-1d5b6c7e8f90"
},
{
"condition": "content.dc_type == \"Invoice\"",
"ontologyId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
}
]
}
Always prefix properties with content.

A condition that names a property without the prefix, such as sys_id == "...", is accepted by this endpoint with 200, but it fails later. For example, a rebuild for the graph database is rejected with 400 {"entity-type":"exception","status":400,"message":"No global property: sys_id"}. Write content.sys_id == "..." instead.

Condition Limitations​

  • Complex list properties cannot be traversed: a condition cannot reach into the entries of a list of objects (such as cin_blobs).
  • Partial matching is not supported: there is no wildcard, prefix or substring match on a string. Compare whole values with == or !=.

Condition Examples​

GoalCondition
One specific document (useful for testing a route)content.sys_id == "{docId}"
Files of a given typecontent.sys_primaryType == "SysFile" && content.dc_type == "Contract"
PDFs onlycontent.sys_contentType == "application/pdf"
Either of two typescontent.dc_type == "Invoice" || content.dc_type == "Receipt"
Everything except one type!(content.dc_type == "Draft")

More Examples​

Clear all routes

PUT /api/graph/graphdbs/{graphDBId}/ontologyroutes
{ "ontologyRoutes": [] }

200 { "ontologyRoutes": [] }

Unknown ontology

{ "ontologyRoutes": [ { "condition": "content.dc_type == \"Contract\"", "ontologyId": "nope" } ] }

400 {"entity-type":"exception","status":400,"message":"Unknown ontologyId(s): nope"}

SQL-style = instead of ==

{ "ontologyRoutes": [ { "condition": "sys_id = \"...\"", "ontologyId": "c28a73bd-..." } ] }

400 {"entity-type":"exception","status":400,"message":"Invalid condition for ontologyId 'c28a73bd-3212-47d7-a782-f0f7daae3679': At position 1:7 token recognition error at: '= '"}

Blank condition

400 {"entity-type":"exception","status":400,"message":"Validation failed for argument [1] in public org.springframework.http.ResponseEntity<...OntologyRoutingConfigurationResponse> ...setOntologyRoutes(...)..."}

Route Errors​

StatusDescription
400Invalid payload: a blank condition or ontologyId (a long Spring "Validation failed for argument..." message), an unknown ontologyId ("Unknown ontologyId(s): ..."), or a condition that cannot be parsed ("Invalid condition for ontologyId '...': ...")
404Graph database not found (empty body)

Step 5: Verify the Configuration​

Retrieve the configuration for a specific graph database, including its associated ontology routing rules.

GET {{baseUrl}}/api/graph/graphdbs/{graphDBId}/config
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json

Response Example​

{
"graphDBId": "7b1e9c2d-4a3f-4e8b-b6d1-2c9f0a1e3d45",
"graphDBName": "my-graph",
"version": "v2",
"ontologyRoutes": [
{
"condition": "content.sys_primaryType == \"SysFile\" && content.dc_type == \"Contract\"",
"ontologyId": "3f6c2a1e-8b4d-4c2f-9a7e-1d5b6c7e8f90"
}
]
}

A newly created graph database has no routes:

{
"graphDBId": "164e3760-21a3-473b-9443-eddda8c7f0cd",
"graphDBName": "my-contracts-graph",
"version": "v2",
"ontologyRoutes": []
}

Response Fields​

FieldTypeDescription
graphDBIdstringThe graph database ID
graphDBNamestringThe graph database name
versionstringGraph database version (v2; v1 for deprecated graph databases)
ontologyRoutesarrayCurrent ontology routing rules (see route object below)

Config Errors​

StatusDescription
404Graph database not found (empty body)

You can also read the graph database itself:

GET {{baseUrl}}/api/graph/graphdbs/{graphDBId}
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json

Graph Database Response​

{
"graphDBId": "3f2b6c1e-8d4a-4b7e-9a51-2c6e0f9d7a10",
"graphDBName": "production-graph",
"version": "v2",
"defaultGraph": false
}

Graph Database Errors​

StatusWhen
404No graph database with this ID (empty body).

Step 6: List and Download Ontologies​

Retrieve the metadata of all registered ontologies. The response does not include download URLs; use GET /api/graph/ontologies/{ontologyId} to get a pre-signed download URL for a specific ontology.

GET {{baseUrl}}/api/graph/ontologies?limit=10&offset=0
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json

List Parameters​

ParameterInTypeRequiredDescription
limitqueryintegerNoMax results (default 10; a negative value falls back to the default)
offsetqueryintegerNoResults to skip (default 0; a negative value returns 400)

Response Example​

{
"offset": 0,
"limit": 10,
"totalCount": 1,
"count": 1,
"ontologies": [
{
"ontologyId": "3f6c2a1e-8b4d-4c2f-9a7e-1d5b6c7e8f90",
"ontologyName": "invoice-ontology",
"description": "Entities and relationships for invoices",
"createdAt": "2026-01-15T10:30:00.123456Z"
}
]
}

More Examples​

Page through ontologies one at a time

GET /api/graph/ontologies?limit=1&offset=0

{ "offset": 0, "limit": 1, "totalCount": 8, "count": 1, "ontologies": [ { ... } ] }

Negative offset

GET /api/graph/ontologies?offset=-1

400 {"entity-type":"exception","status":400,"message":"'offset' must not be negative"}

Response Fields​

FieldTypeDescription
offsetintegerOffset applied to this page
limitintegerLimit applied to this page
totalCountintegerTotal number of registered ontologies
countintegerNumber of ontologies on this page
ontologies[].ontologyIdstringOntology ID (a UUID generated on upload)
ontologies[].ontologyNamestringOntology name (unique)
ontologies[].descriptionstringDescription supplied on upload (empty string if none was given)
ontologies[].createdAtstringCreation time as an ISO-8601 UTC timestamp

Download an ontology file​

Get a pre-signed URL to download the ontology definition file.

GET {{baseUrl}}/api/graph/ontologies/{ontologyId}
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json

Returns a pre-signed URL for downloading the ontology YAML file, wrapped in an ontology object.

{
"ontology": {
"presignedUrl": "https://{bucket}.s3.{region}.amazonaws.com/kgconfig/{ontologyId}?...&X-Amz-Expires=3600&X-Amz-Signature=..."
}
}

The URL is valid for 3600 seconds (one hour). Fetch it without the bearer token to download the YAML file. The storage service may report a Content-Type other than YAML (for example text/turtle), so don't rely on it.

Error Responses​

StatusDescription
404{"entity-type":"exception","status":404,"message":"Ontology not found: {ontologyId}"}

List graph databases​

GET {{baseUrl}}/api/graph/graphdbs
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json

Graph Database List Parameters​

ParameterInTypeRequiredDescription
limitqueryintegerNoMaximum number of results (default 10). A negative value falls back to the default.
offsetqueryintegerNoNumber of results to skip (default 0). Must not be negative.

Graph Database List Permissions​

content-lake-api.graph-configs.read

Graph Database List Response​

{
"offset": 0,
"limit": 10,
"totalCount": 1,
"count": 1,
"graphDBs": [
{
"graphDBId": "3f2b6c1e-8d4a-4b7e-9a51-2c6e0f9d7a10",
"graphDBName": "production-graph",
"version": "v2",
"defaultGraph": false
}
]
}

totalCount is the number of graph databases across all pages; count is the number on this page.

Graph Database List Examples​

Second page of two

GET /api/graph/graphdbs?limit=2&offset=1

{ "offset": 1, "limit": 2, "totalCount": 12, "count": 2, "graphDBs": [ {...}, {...} ] }

Negative limit falls back to 10

GET /api/graph/graphdbs?limit=-5

{ "offset": 0, "limit": 10, "totalCount": 12, "count": 10, "graphDBs": [ ... ] }

Negative offset

GET /api/graph/graphdbs?offset=-1

400 {"entity-type":"exception","status":400,"message":"'offset' must not be negative"}

Graph Database List Errors​

StatusWhen
400offset is negative: "'offset' must not be negative".
note

These are the canonical endpoints under /api/graph/graphdbs. The older /api/graph/domain URLs are deprecated aliases (since 2026.06, scheduled for removal) and return the legacy {domainId, domainName, Version} shape (note the capital V). The legacy list, GET /api/graph/domain, returns a bare JSON array rather than a page object, and in testing it ignored limit:

GET /api/graph/domain?limit=2

[
{"domainId":"3f2b6c1e-8d4a-4b7e-9a51-2c6e0f9d7a10","domainName":"production-graph","Version":"v2"},
{"domainId":"9b2575db-c130-4433-86c9-0c23cd06e739","domainName":"legacy-graph","Version":"v1"},
...every graph database...
]

Step 7: Delete Safely​

Delete an ontology​

An ontology can only be deleted when no graph database routes to it.

DELETE {{baseUrl}}/api/graph/ontologies/{ontologyId}
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json

Response​

204 No Content

Error Responses​

StatusDescription
404{"entity-type":"exception","status":404,"message":"Ontology not found: {ontologyId}"}
409The ontology is still referenced by the ontologyRoutes of one or more graph databases. Remove those routes first.

Example: deleting an ontology that is still routed​

DELETE /api/graph/ontologies/c28a73bd-3212-47d7-a782-f0f7daae3679

409 {"entity-type":"exception","status":409,"message":"Ontology is still referenced by the ontologyRoutes configuration of graphDB(s): my-contracts-graph"}

To delete it, first PUT a route list that no longer references the ontology (or {"ontologyRoutes": []}) to every graph database named in the message, then retry.

Delete a graph database​

warning

Deleting a graph database drops its underlying graph namespace, which permanently removes all associated knowledge graph data. This operation cannot be undone.

DELETE {{baseUrl}}/api/graph/graphdbs/{graphDBId}
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json

Response​

204 No Content on success.

Errors​

StatusWhen
400graphDBId is blank: "GraphDB ID must not be null or blank".
404No graph database with this ID, including IDs that are not UUIDs (empty body).

Next Steps​