Defining an ontology schema
Adopt templates and manage concept types and relation types — the schema that powers graph filters.
This tutorial uses GET /api/v1/ontology/templates, POST /api/v1/ontology/templates/{id}/adopt, and the /api/v1/ontologies/{id}/… type endpoints. The full schema lives in the API reference.
After this tutorial you'll have a workspace ontology with concept types and relation types — the schema that powers graph filtering. You define (or adopt) this once, then create as many concept instances and document links as you need.
There are two ways to get a schema:
- Adopt a starter template — clone a ready-made ontology into a workspace, then customize
- Build from scratch — create concept types and relation types on an adopted ontology (or extend a vertical kit)
Option A: Adopt a starter template
Browse templates, then adopt one into a workspace. Context212 ships vertical kits inspired by common document taxonomies:
| Key | Label |
|---|---|
legal | Legal (contracts, litigation, compliance, …) |
healthcare | Healthcare |
finance | Finance |
tech | Technology |
manufacturing | Manufacturing |
Each kit includes concept types (with parent_key for attribute inheritance), shared organization / person entities, and domain relation types such as classified_as, party_to / signed_by, and vertical-specific edges (e.g. governs in legal).
const headers = {
Authorization: `Bearer ${process.env.C212_API_KEY}`,
};
const templates = await fetch(
"https://api.context212.com/api/v1/ontology/templates",
{ headers },
).then((r) => r.json());
for (const ontology of templates) {
console.log(`${ontology.key.padEnd(16)} ${ontology.label} (${ontology.id})`);
}Adopt into a workspace (replace IDs with your own):
const headers = {
Authorization: `Bearer ${process.env.C212_API_KEY}`,
};
const response = await fetch(
`https://api.context212.com/api/v1/ontology/templates/${templateId}/adopt`,
{
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ workspace_id: 12 }),
},
);
const ontology = await response.json();
console.log(ontology);
// { id, key, label, version: 1, is_template: false, adopted_from, workspace_id: 12, status: "active", ... }Adoption clones concept types and relation types into a new workspace-owned ontology. After that, everything is fully editable — rename, add, or remove types. Adoption is a starting point, not a lock-in.
Example from the legal kit: concept type keys are plain snake_case (nda, contract) with parent_key for inheritance — attributes are defined only on the type that owns them (an NDA inherits jurisdiction from legal via the parent chain at validation time). Use classified_as from a file to a concept of that type (or any descendant of legal).
Option B: Add concept types
List types on your adopted ontology, then create more:
const headers = {
Authorization: `Bearer ${process.env.C212_API_KEY}`,
};
const types = await fetch(
`https://api.context212.com/api/v1/ontologies/${ontologyId}/concept-types`,
{ headers },
).then((r) => r.json());
console.log(types.map((t) => t.key));
const created = await fetch(
`https://api.context212.com/api/v1/ontologies/${ontologyId}/concept-types`,
{
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
key: "incident",
label: "Incident",
attributes: [
{
key: "severity",
label: "Severity",
data_type: "select",
select_options: ["low", "medium", "high", "critical"],
required: true,
},
{
key: "occurred_on",
label: "Occurred on",
data_type: "date",
required: false,
},
],
}),
},
).then((r) => r.json());
console.log(created.id, created.key);Update or delete a type by ID:
// PATCH /api/v1/ontology/concept-types/{concept_type_id}
// DELETE /api/v1/ontology/concept-types/{concept_type_id} → 409 if live instances existparent_id is optional and only for attribute inheritance — concept types are not a display tree.
Add relation types
Document-sourced edges use source_concept_type_id: null. Concept-to-concept edges set both type IDs.
const headers = {
Authorization: `Bearer ${process.env.C212_API_KEY}`,
};
const relationType = await fetch(
`https://api.context212.com/api/v1/ontologies/${ontologyId}/relation-types`,
{
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
key: "involves",
label: "Involves",
inverse_label: "Involved in",
source_concept_type_id: null, // document source
target_concept_type_id: personTypeId,
cardinality: "many_to_many",
}),
},
).then((r) => r.json());
console.log(relationType.id, relationType.key);Cardinality:
| Value | Behaviour when creating another edge from the same source |
|---|---|
many_to_many | Allowed (default) |
one_to_many / one_to_one | 409 if that source already has this relation type |
Version the schema
When you need a clean schema bump without mutating the version documents were classified against:
const next = await fetch(
`https://api.context212.com/api/v1/ontologies/${ontologyId}/new-version`,
{
method: "POST",
headers: { Authorization: `Bearer ${process.env.C212_API_KEY}` },
},
).then((r) => r.json());
// Previous ontology → status "archived"
// New ontology → status "active", version = previous + 1, types clonedExisting document_ontology_version rows keep pointing at the ontology ID that was active when the document received its first relation.
Next
With a schema in place, create concepts and link documents.