Skip to documentation

Atlas

Manage entities & relations

Extraction builds most of the graph for you, but the API lets you curate it directly — assert an entity a document never quite stated, correct a type, draw a relation you know is true. The model is deliberately open-ended: entity and relation types are free text that auto-register the first time you use them, and manual facts sit on the same graph as extracted ones. This guide shows the write endpoints and the coexistence rules that make hand-curation safe.

Prerequisites

  • A key with kb:write for mutations and kb:read to read back. See Getting started.
  • Familiarity with the graph model helps — skim Entity graph.

Create an entity

Give a name and a type. The type is free text — pass "vendor", "risk", or anything your domain needs, and if it is new to the workspace it registers into the ontology automatically. A manually created entity carries origin: "manual".

POST/api/v1/entitiesBearer · kb:write

Create a manual entity. An arbitrary type auto-registers into the workspace ontology on first use. Origin is recorded as manual.

Request body

namerequiredstringDisplay name. Normalized for matching (case/whitespace-insensitive).
typerequiredstringFree-text type, e.g. organization, person, vendor, risk. Auto-registers if new.
confidencenumber0–1. Defaults to 1 for manual entities — you asserted it.
aliasesstring[]Alternative names that should resolve to this entity.
Example requestbash
curl -s -X POST https://your-host/api/v1/entities \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Logistics",
    "type": "vendor",
    "aliases": ["Acme Freight", "Acme Log."]
  }'
Responsejson
{
  "id": "01KY11BBBBBBBBBBBBBBBBBBBB",
  "name": "Acme Logistics",
  "type": "vendor",
  "confidence": 1,
  "origin": "manual",
  "authority": "asserted",
  "lifecycleStatus": "published",
  "version": 1,
  "aliases": ["Acme Freight", "Acme Log."],
  "createdAt": "2026-07-21T11:20:00.000Z"
}

Arbitrary types are a feature, not a footgun

There is no fixed enum of entity types. The first time you use type: "risk" it becomes part of the ontology, colored in the graph and filterable in the API. Curate the vocabulary in Ontology authoring when you want to consolidate loose types.

Why manual and extracted coexist

The key to hand-curation being safe is name-based reuse. Entities resolve by normalized name, so a manual entity and a later extraction of the same name are the same row — not a duplicate. Create Acme Logisticstoday; when next week's ingested contract mentions Acme Logistics, extraction links its new facts to the entity you already made. Your manual assertion and the machine's extracted mentions accumulate on one node.

  • Curatewhen you know something the documents don't say outright, when you want an entity to exist before its first mention, or to fix a type extraction got wrong.
  • Extract for volume — let ingestion discover the long tail of entities and relations you would never enumerate by hand.
  • Both, always: the graph is the union. origin tells you which facts a human asserted vs. a model proposed, so you can weight or audit them differently.

Authority, lifecycle & versions

Beyond origin, every entity and relation carries formal governance state. A manually created entity or relation starts at authority asserted (a human vouched for it); extraction writes at machine_extracted; steward review promotes to curated; owner sign-off reaches certified — the only tier an assistant states without hedging. Lifecycle is live by default: new entities land published, and lifecycleStatus tells you when a concept has been deprecated or superseded. Every mutation on this page — create, update, delete — bumps version and appends an immutable revision (field-level diff, actor, provenance), readable at GET /api/v1/entities/{id}/revisions. The full loop — change requests, certification, review SLAs, the workspace manifest — is walked in Curate & govern concepts.

Edit & delete an entity

PATCH/api/v1/entities/{id}Bearer · kb:write

Update an entity's name, type, confidence, or aliases. Renaming re-normalizes it for matching.

Request body

namestringNew display name.
typestringNew type — auto-registers if new to the workspace.
confidencenumberNew 0–1 confidence.
aliasesstring[]Replace the alias set.
Example requestbash
curl -s -X PATCH https://your-host/api/v1/entities/01KY11BBBBBBBBBBBBBBBBBBBB \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"logistics-partner"}'
Responsejson
{ "id": "01KY11BBBBBBBBBBBBBBBBBBBB", "name": "Acme Logistics", "type": "logistics-partner", "origin": "manual" }
DELETE/api/v1/entities/{id}Bearer · kb:write

Soft-delete an entity and detach its relations. Extraction can re-surface an entity of the same name later from a fresh mention.

Example requestbash
curl -s -X DELETE https://your-host/api/v1/entities/01KY11BBBBBBBBBBBBBBBBBBBB \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{ "deleted": true }

Merging duplicates

If two entities should have been one, don't delete — merge, which is non-destructive and reversible, preserving both sets of mentions under a canonical node. The merge and unmerge workflow is covered in Entity review & merge.

Create a relation

A relation is a typed, directed edge between two existing entities. Like entity types, the relation type is free text that auto-registers. Supply the source and destination entity ids you got back from entity creation or a list call.

POST/api/v1/relationsBearer · kb:write

Create a directed, typed relation between two entities. The relation type auto-registers if new. Serve-time canonical edges rebuild automatically.

Request body

srcEntityIdrequiredstringSource entity id.
dstEntityIdrequiredstringDestination entity id.
typerequiredstringFree-text relation type, e.g. supplies, depends_on, owns.
confidencenumber0–1. Defaults to 1 for manual relations.
Example requestbash
curl -s -X POST https://your-host/api/v1/relations \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "srcEntityId": "01KY11BBBBBBBBBBBBBBBBBBBB",
    "dstEntityId": "01KY0ZW3B8AVDR67NXSE96P81N",
    "type": "supplies"
  }'
Responsejson
{
  "id": "01KY12CCCCCCCCCCCCCCCCCCCC",
  "srcEntityId": "01KY11BBBBBBBBBBBBBBBBBBBB",
  "dstEntityId": "01KY0ZW3B8AVDR67NXSE96P81N",
  "type": "supplies",
  "confidence": 1,
  "origin": "manual",
  "authority": "asserted"
}

Domain/range constraints

If the workspace ontology declares allowed source/destination types for a relation type (its domain and range — see Ontology authoring), creation is validated against them. A violating relation is rejected with a 400 naming the allowed types, e.g. Relation "governed_by" requires a source entity of type process | system | metric (got "vendor"). Relation types without constraints stay free-form. Extraction respects the same constraints, silently skipping violating candidates.
DELETE/api/v1/relations/{id}Bearer · kb:write

Soft-delete a relation and rebuild the affected serve-time edges.

Example requestbash
curl -s -X DELETE https://your-host/api/v1/relations/01KY12CCCCCCCCCCCCCCCCCCCC \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{ "deleted": true }

Read the neighborhood back

Confirm your edits by expanding the graph frontier around an entity — canonical relations in both directions, up to depth hops:

GET/api/v1/entities/{id}/neighborhoodBearer · kb:read

The knowledge-graph frontier around one entity: its card plus typed edges out to depth hops.

Query parameters

depthintegerHops to expand, 1–3. Default 1.
perHopCapintegerMax edges per hop, 1–100. Default 25.
totalCapintegerMax total nodes, 1–500. Default 200.
Example requestbash
curl -s "https://your-host/api/v1/entities/01KY0ZW3B8AVDR67NXSE96P81N/neighborhood?depth=1" \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{
  "entity": { "id": "01KY0ZW3B8AVDR67NXSE96P81N", "name": "Order Platform", "type": "system" },
  "nodes": [
    { "id": "01KY11BBBBBBBBBBBBBBBBBBBB", "name": "Acme Logistics", "type": "logistics-partner" }
  ],
  "edges": [
    { "srcEntityId": "01KY11BBBBBBBBBBBBBBBBBBBB", "dstEntityId": "01KY0ZW3B8AVDR67NXSE96P81N", "type": "supplies" }
  ],
  "truncated": false
}

The leverage

Curation and extraction are not competing sources of truth — they are one graph a human and a model build together. You can seed a domain ontology by hand, let ingestion pour facts into it, correct what the model gets wrong, and never fight duplicates, because names reconcile automatically. That same graph is what grounded answers and live-data lookups reason over.