Skip to content

Working with Data

Getting data in

Direct writes

POST /v1/records (single or batch, with inline relationships) and POST /v1/relationships for edges. Structured JSON ingestion (format: "json") applies synchronously and infers schema as it goes: no upfront modeling, ever. Property types are observed per label and queryable historically (GET /v1/schema?at=<time>&label=<label>).

Document ingestion

POST /v1/ingest with a format hint. Workers parse and project:

Format Handles Projection
docling PDF, DOCX, PPTX Document, sections, paragraphs, tables as nodes with CONTAINS edges, page/bbox metadata
tabular CSV, XLSX Rows as nodes, typed columns, cross-reference columns become edges
web URLs, SPAs Markdown projection, headings as hierarchy, links as REFERENCES edges
email .eml, .msg Threading edges (SENT_BY, TO, IN_REPLY_TO), attachments fan out as child ingests
code rs, py, js/ts Files, functions, classes as nodes; imports and calls as edges
json Structured records Direct node projection, nested objects flatten or become child nodes

Duplicate content is detected by hash: re-ingesting the same bytes is a no-op, changed bytes create a new source version.

Connectors (the usual way)

Connectors sync continuously, read-only, preserving authorship, ownership, ACLs, and deletions:

  • SharePoint / OneDrive (files by extension)
  • Exchange / Outlook (mailbox allowlists, attachments included)
  • Slack (thread-aware conversation documents per allowlisted channel)
  • Salesforce (allowlisted objects as records)
  • Entra ID (people, groups, managers: the identity backbone for routing and SSO)

Configure per connection under [connectors.<source>.<connection>] with credentials by environment-variable name, an explicit scope allowlist (nothing syncs by default), and an interval. telha connector ls|status|run <name> shows cursor age, processed/skipped/failed counts, and last error. Source deletions tombstone in Telha; history before the deletion stays queryable.

Querying

The query language is JSON. The important parts:

{
  "find": "CONTRACT_VERSION",              // label
  "where": {                                // operator objects, always
    "riskScore": { "$gte": 3 },             // $eq $ne $gt $gte $lt $lte $in $exists
    "$or": [ { "region": { "$eq": "EU" } }, { "region": { "$eq": "UK" } } ]
  },
  "atValidTime": "2026-03-01T00:00:00Z",   // world-time pin
  "atTxTime":    "2026-04-01T00:00:00Z",   // knowledge-time pin
  "expand": {                               // graph traversal from results
    "types": ["AMENDS"], "direction": "out", "depth": 2
  },
  "vector": {                               // semantic stage (hybrid ranking)
    "text": "supply chain delay risk", "model": "default", "k": 20, "minScore": 0.3
  },
  "limit": 100,                             // + opaque cursor for pagination
  "trace": true                             // persist an execution trace
}

Notes that save time:

  • where values are always operator objects: {"$eq": 5}, never a bare 5.
  • Expanded results arrive in a related section and never repeat nodes already in the primary results.
  • Datetimes serialize as microsecond integers in responses; bytes as base64.
  • Vector plus cursor do not compose (vector results are ranked, not scan-ordered).
  • Scores: hybrid queries return a similarity-and-decay fused score per result.

Snapshots and compare

POST /v1/snapshot exports a consistent subgraph pinned at exact coordinates: the state of everything matching a query, as of a moment, as known at a moment.

POST /v1/compare diffs two coordinate pairs: added, removed, and modified nodes and edges, with property-level diffs, paginated for large deltas. This is the "what changed in our risk assessment between Q1 and Q2" call, and it is also available as the compareSnapshots MCP tool and in chat.