Korely

Context & facts

Get facts

Read the typed facts Korely extracted from your memories, deterministically, with no model call. Filter by entity or predicate family, and travel back in time with as_of.

GET /v1/facts

SDK: korely.get_facts(...). This is a pure SQL filter-and-sort over the facts extracted from your memories, no embeddings, no LLM, fully deterministic. Every fact is a typed (subject, predicate, object) triple with bi-temporal validity, so you can ask "what was true on this date" with as_of.

Authentication

HTTP header, required: Authorization: Bearer kor_live_.... The key must carry the memories:read scope.

Query parameters

All parameters are optional. With none set, you get the most recent valid facts across all your end users.

ParameterTypeDefaultDescription
user_idstringNoneFilter to one end user. When omitted, facts are returned across all end users (no end-user scoping).
agent_idstringNoneOptional agent-namespace filter.
subjectstringNoneMatch the subject side of the triple only. Exact and case-insensitive on the subject's current name: a former name (an alias) finds the same entity. An alias belongs to the end user and agent whose memory stated it, so across end users each fact is matched under what its own end user means by the name: if Maria's Acme is now Globex and Luca's Acme is still Acme, subject=Acme without user_id returns both.
entitystringNoneMatch the subject or the object side, every fact mentioning this entity. Same matching as subject.
predicatestringNoneFilter by one predicate (e.g. works_at). Normalized like a written predicate (works at becomes works_at) and resolved through your project's learned vocabulary, then matched on the canonical predicate.
predicate_familystringNoneFilter by predicate family: one of preferences, instructions, people, places, work, ownership, health, financial, events, identity, other. Case-insensitive; an unknown family returns an empty list, not an error. Expanded at the SQL level to every predicate in the family.
include_invalidatedbooleanfalseWhen true, also include facts that are no longer true. They carry invalid_at and invalidated_by so you can trace the supersede chain. Ignored when as_of is set.
as_ofstringNoneISO date or datetime, point-in-time validity. Returns the facts that were valid at that instant. A naive value is coerced to UTC; a bare date (2026-06-01) means midnight.
limitinteger50Page size. Range 1-200.
offsetinteger0Pagination offset. Minimum 0.

Example request

Terminal window
curl -G https://api.korely.ai/v1/facts \
-H "Authorization: Bearer kor_live_..." \
--data-urlencode "user_id=customer-giulia-4812" \
--data-urlencode "predicate_family=work" \
--data-urlencode "limit=50"

Response

200 OK. A page of typed facts plus the total matching count (before paging).

{
"facts": [
{
"id": "fct_a1",
"subject": "Giulia",
"subject_type": "person",
"predicate": "works_at",
"predicate_raw": "works at",
"object": "Acme Corp",
"object_is_literal": false,
"predicate_family": "work",
"confidence": 0.92,
"user_id": "customer-giulia-4812",
"agent_id": "support-bot",
"valid_from": "2026-03-01T00:00:00+00:00",
"invalid_at": null,
"invalidated_by": null,
"source_memory_id": "mem_8f2c1a",
"created_at": "2026-03-01T09:14:22+00:00",
"subject_canonical": "Giulia",
"object_canonical": "Acme Corp",
"tense": "current",
"last_confirmed_at": "2026-05-12T16:40:03+00:00",
"observation_count": 2,
"source_memory_ids": ["mem_8f2c1a", "mem_41d0e7"]
}
],
"total": 1
}
FieldTypeDescription
factsarray<object>The page of serialized facts. Each item carries the fields below.
facts[].idstringThe fact id, e.g. fct_a1.
facts[].subjectstringThe subject of the triple.
facts[].subject_typestringFrom extraction: person, organization, product, place, concept, event or unknown. On a fact written with POST /v1/facts, what the caller sent.
facts[].predicatestringThe normalized predicate, e.g. works_at.
facts[].predicate_rawstringThe predicate as it appeared in the source text, e.g. works at.
facts[].objectstringThe object of the triple.
facts[].object_is_literalbooleantrue when the object is a literal value rather than an entity.
facts[].predicate_familystringThe family the predicate belongs to, e.g. work.
facts[].confidencenumber · nullExtraction confidence, 0-1, rounded to three decimals.
facts[].user_id / facts[].agent_idstring · nullThe scope the fact belongs to (null if unscoped).
facts[].valid_fromstring · nullISO 8601, when the fact became true (bi-temporal valid time).
facts[].invalid_atstring · nullISO 8601, when the fact stopped, or will stop, being true: a contradiction, a correction, a forget or a past write. null while it is open; a future value is a known end date, and the fact is true until then.
facts[].invalidated_bystring · nullThe id of the fact that superseded this one, or null.
facts[].source_memory_idstring · nullThe memory this fact was first extracted from; null for a fact written directly with POST /v1/facts.
facts[].created_atstring · nullISO 8601, when the store learned the fact.
facts[].subject_canonicalstringThe subject's current name after aliases (subject keeps the words as written).
facts[].object_canonicalstringThe object's current name after aliases.
facts[].tensestringcurrent, past or planned, as the text or the writer said it.
facts[].last_confirmed_atstring · nullISO 8601, when a memory last restated the fact.
facts[].observation_countintegerHow many memories assert the fact. Above 1 it was reconfirmed, not duplicated.
facts[].source_memory_idsarray<string>Every mem_ id that asserts the fact.
totalintegerTotal facts matching the filters, before limit / offset paging.

Errors

StatusCodeCause
401invalid_keyMissing or invalid Authorization header. Message: Invalid or missing API key: ..., then what is wrong; response carries WWW-Authenticate: Bearer.
403forbiddenThe API key lacks the memories:read scope. Message: API key missing required scope(s): memories:read.
422invalid_requestas_of is not a parseable ISO date or datetime. Message: as_of must be an ISO date (2026-06-01) or datetime. A standard validation 422 is also returned if limit or offset falls outside its range.
429rate_limit_exceededPer-tier minute / hour / day request limit exceeded. Message: Rate limit exceeded (... per ...). Retry shortly.; response carries Retry-After and X-RateLimit-* headers.
429quota_exceededMonthly query quota (plus 10% grace) exhausted. The body adds limit (the plan's figure), used and resets_at (00:00 UTC on the 1st) to code and message ("Monthly query limit reached: ... queries on the ... plan, plus a 10% grace. It starts again on ..."). No Retry-After: read resets_at.

Notes

  • Deterministic. No model or LLM call, a pure SQL filter and sort. The same query returns the same facts every time.
  • Travel through time. as_of accepts an ISO date (2026-06-01 → midnight) or a full datetime; naive values are coerced to UTC. It returns the facts that were valid at that instant.
  • Supersede, not delete. Facts that are no longer true are excluded by default: a fact is current while valid_from is not in the future and invalid_at is null or still ahead. Pass include_invalidated=true to surface the others, they carry invalid_at and invalidated_by so you can walk the chain.
  • Order. Without as_of, the most recently confirmed first (last_confirmed_at, else valid_from, descending); with as_of, by valid_from descending.
  • Entity vs subject. entity matches the subject or the object side; subject matches only the subject. predicate_family is expanded at the SQL level to every predicate in that family.
  • Scope. When user_id is omitted there is no end-user scoping, facts are returned across all your end users.
  • Paging. limit defaults to 50 (range 1-200), offset defaults to 0. total reflects the full match count before paging.

Related