CortexDB Docs
API Reference

GET /v1/events

List raw events from the WAL — the append-only source of truth.

GET
/v1/events
AuthorizationBearer <token>

PASETO v4 public token (or deployment gate key). Auth-disabled dev instances accept any caller.

In: header

Query Parameters

scope*string
limit?integer
cursor?string
labels?string

Response Body

curl -X GET "https://example.com/v1/events?scope=string"
Empty
GET
/v1/events/{id}
AuthorizationBearer <token>

PASETO v4 public token (or deployment gate key). Auth-disabled dev instances accept any caller.

In: header

Path Parameters

id*string

Query Parameters

scope?string

event_scope_filter_v1 (0.10.2): return the event only when its own scope is exactly this scope path; an ancestor or descendant scope does not match, and a mismatch answers the same 404 as a missing id. An empty or malformed value answers 422 INVALID_SCOPE_GRAMMAR. Without it the read is authorized against the event's own scope only, which the static operator key (CORTEX_API_KEY) and auth-disabled mode pass for every scope and every billing tenant. A server that does not list event_scope_filter_v1 ignores the parameter, so send it only when the name is listed.

Response Body

application/json

application/json

curl -X GET "https://example.com/v1/events/string"
Empty

Event item shape

Real item shape — id, subject object, nested context

Each event is { id, scope, caller, observed_actor, subject: { id, type }, scope_key, modality, content, context: { observed_at, recorded_at, intent, labels }, wal_offset }. Note: id (not event_id); subject is an object { id, type } (not a string); observed_at / recorded_at / labels / intent are nested under context (not top-level). This is the same shape recall and GET /v1/events/{id} return. Events written with a source descriptor carry it back verbatim as source; the key is absent otherwise.

Since v0.10.1 an event can also carry language, scripts and language_source (a non-English or explicitly tagged event; an event that is English by default has none), context.timezone and context.refers_to (when the capture sent them), and source_blob_ids (the uploaded blob a blob_ref event was made from). See Experience.

scope_key is the billing partition the event was captured under — the capturing credential's billing tenant, default for the static operator key (CORTEX_API_KEY) — not the event's scope, which is scope.

Reading one event in one scope

GET /v1/events/{id}?scope=<scope> (v0.10.2, capability event_scope_filter_v1) returns the event only when its own scope is exactly <scope>:

  • An event stored in a sibling, an ancestor or a descendant of <scope> gets the same 404 NOT_FOUND body as an unknown id ("event not found"), so the filter never tells you where an event lives.
  • An empty, blank or malformed value (?scope=, ?scope=%20, ?scope=nocolon) → 422 INVALID_SCOPE_GRAMMAR. Sending scope twice → a plain-text 400 ("duplicate field scope"). Neither falls back to a read without the filter.
  • Without the parameter nothing changes. Use it with operator-wide credentials: the static operator key (CORTEX_API_KEY) and an auth-disabled server read every scope of every tenant by id.
  • v0.10.1 and earlier ignore the parameter and return the event. Send it only when capabilities[] lists event_scope_filter_v1, or compare the returned scope with the one you expect.
  • It applies to this route only; GET /v1/facts/{id} has no scope filter.

Behavior notes

Content is always included

GET /v1/events always returns each event's full content. To leave content out, use recall with exclude_content: true.

  • Envelope: { items: [...], next_cursor, has_more }; next_cursor is omitted on the last page. Items are newest first by the server's context.recorded_at, whatever their observed_at. Default view is local; the enum is local / granular / raw / structured / holistic / descend (anything else → 422).
  • Filters that work: labels (comma-separated, any-match), observed_actor, modality, and since (RFC 3339). since compares context.recorded_at (when the server stored the event), not observed_at: an event observed in 2020 but written today passes since=2024-01-01.
  • There is no until filter: to bound the end of a time range, filter on context.recorded_at on your side.
  • GET /v1/events/{id} returns a single event, always with its full text (even when a recall served it as an excerpt); a missing, unknown, forgotten or cancelled id → 404. Since v0.10.2 every recalled layers.events item carries the same caller, observed_actor, subject, scope_key, scope and wal_offset as this route (see Recall).
  • limit is clamped rather than rejected: limit=0 returns one item, and limit=5000 returns 200. A missing scope query param → 400 (query-string validation returns 400; body validation returns 422).
  • Embedding-provider outages: an event accepted while the embedding endpoint is down stays captured until the provider returns, and is then indexed automatically with no operator action. During an outage the indexer holds rather than drops, so ?wait=indexed waits until the provider is back or its wait budget runs out (408 WAIT_TIMEOUT after about 30 s on v0.10.2; see Experience). (Upgrade from v0.9.10 or earlier, where an outage could leave events stuck.)

On this page