CortexDB Docs
Core Concepts

Bi-temporal Model

How CortexDB tracks two independent time axes — when something was true in the world, and when the system learned it.

CortexDB records two time axes on every derived record, so you can ask both "what was true on 2026-04-15?" and "what did we know on 2026-04-15?"

  • Valid time — valid_from / valid_to: when the fact was true in the world.
  • Recorded time — recorded_from / recorded_to: when CortexDB learned it.

null on a *_to field means "open / still current." Events carry only observed_at and recorded_at; the four-field bi-temporal shape appears on derived layers (Facts, Beliefs, …).

Querying a point in time

Pass a temporal block to /v1/recall, /v1/answer, or the layer-read endpoints:

{
  "scope": "org:acme/dept:eng/user:alice",
  "query": "seat count",
  "temporal": {
    "as_of": "2026-04-15T00:00:00Z",
    "natural": "last 30 days",
    "reference_date": "2026-05-01T00:00:00Z"
  }
}

as_of pins both axes at once (known by then, and true then). To pin one axis alone, use as_of_valid (records true at that instant) or as_of_recorded (records known at that instant); both are accepted on v0.10.1 (the meaning is from the pinned temporal_block schema).

valid_during / recorded_during are 2-element arrays

Range filters take a two-element array [start, end], not an object. This is enforced — an object returns 422 INVALID_BODY: "invalid type: map, expected an array of length 2".

// correct
"valid_during": ["2026-04-01T00:00:00Z", "2026-04-30T00:00:00Z"]

// WRONG → 422
"valid_during": { "from": "2026-04-01", "to": "2026-04-30" }

Dates may be RFC-3339 datetimes or YYYY-MM-DD (end date inclusive). Applies to recorded_during too.

On recall, valid_during filters events by capture time

In a /v1/recall or /v1/answer temporal block, valid_during keeps the events whose observed_at falls in the window. It is not the facts' valid time. To favour memories by the dates they talk about, capture context.refers_to and recall with temporal.refers_during; see Languages & Multilingual Memory.

Supersession

When a newer fact arrives for the same (subject, predicate), the prior fact's valid_to (or recorded_to) closes rather than being overwritten — the full history is retained. Walk it with GET /v1/facts/timeline.

Accepted temporal fields

temporal.as_of, temporal.natural and temporal.reference_date are all accepted, and range filters take the two-element array form shown above.

On this page