Applying concepts and relations
Create concept instances and link documents into the ontology graph.
This tutorial uses POST /api/v1/ontology/concepts, POST /api/v1/ontology/relations, and GET /api/v1/files/{file_id}/ontology. The full schema lives in the API reference.
Your ontology schema exists. Now you'll create concept instances and relations that connect documents (and concepts) into a queryable graph.
Applying ontology data is a two-step act:
- Create concepts: standalone instances (
Organization,Person, …) with validated attributes - Create relations: edges from a file (or concept) to a target concept
Step 1: Upload a document
Before linking, you need a file. If you already have one, skip this step and use your existing file_id.
Replace workspace_id with your own. List workspaces via GET /api/v1/workspaces.
import { readFileSync } from "node:fs";
const headers = {
Authorization: `Bearer ${process.env.C212_API_KEY}`,
};
const workspaceId = 12;
const form = new FormData();
form.append("file", new Blob([readFileSync("acme_nda_2025.pdf")]), "acme_nda_2025.pdf");
form.append("workspace_id", String(workspaceId));
form.append("title", "NDA with Acme Corp");
const file = await fetch("https://api.context212.com/api/v1/files", {
method: "POST",
headers,
body: form,
}).then((r) => r.json());
console.log(file.id); // e.g. 42Step 2: Create a concept
Concepts live in a workspace and must use a concept type from an ontology adopted in that same workspace.
const headers = {
Authorization: `Bearer ${process.env.C212_API_KEY}`,
};
const concept = await fetch("https://api.context212.com/api/v1/ontology/concepts", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
workspace_id: 12,
concept_type_id: organizationTypeId,
label: "Acme Corp",
attributes: { region: "EU" },
}),
}).then((r) => r.json());
console.log(concept);
// { id, workspace_id, concept_type_id, label, attributes: { region: "EU" }, ... }Attributes are validated against the concept type: required fields, data_type, and select_options. Invalid payloads return 422.
List and filter concepts
const params = new URLSearchParams({
workspace_id: "12",
concept_type_id: organizationTypeId,
query: "Acme",
});
params.append("attribute", "region:EU|US");
const listed = await fetch(
`https://api.context212.com/api/v1/ontology/concepts?${params}`,
{ headers: { Authorization: `Bearer ${process.env.C212_API_KEY}` } },
).then((r) => r.json());
console.log(listed.count, listed.results);Attribute filter syntax: repeated attribute params are AND; | inside one value is OR; comparisons (>, >=, <, <=), prefixes (Casa*), and contains (*report*) are supported.
Step 3: Link the document
Create a relation from the file to the concept. Use a relation type whose source_concept_type_id is null (document source).
const headers = {
Authorization: `Bearer ${process.env.C212_API_KEY}`,
};
const relation = await fetch("https://api.context212.com/api/v1/ontology/relations", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
relation_type_id: referencesTypeId,
source_document_id: 42,
target_concept_id: concept.id,
}),
}).then((r) => r.json());
console.log(relation.id);Rules enforced on create:
- Exactly one of
source_document_id/source_concept_id - Source and target types must match the relation type
- File workspace must match the ontology workspace
- Cardinality
one_to_one/one_to_manyrejects a second edge from the same source (409)
The first relation from a document under a given ontology inserts an immutable document_ontology_version row (audit trail for which schema version classified the file).
Step 4: Inspect the graph on a file
const graph = await fetch(
"https://api.context212.com/api/v1/files/42/ontology?depth=2",
{ headers: { Authorization: `Bearer ${process.env.C212_API_KEY}` } },
).then((r) => r.json());
console.log(JSON.stringify(graph, null, 2));Example response:
{
"document_id": 42,
"relations": [
{
"relation_type": { "key": "references", "label": "References" },
"target_concept": {
"id": "…",
"label": "Acme Corp",
"concept_type": { "key": "organization", "label": "Organization" },
"attributes": { "region": "EU" }
},
"depth": 1
}
]
}depth is capped at 3. For edges starting from a concept, use GET /api/v1/ontology/concepts/{id}/relations?depth=1.
Update and delete
// PATCH /api/v1/ontology/concepts/{id} — label and/or attributes
// DELETE /api/v1/ontology/concepts/{id} — 409 if relations still point at it
// DELETE /api/v1/ontology/relations/{id}Next
With documents linked into the graph, filter Search and Ask with relations.