GET /v1/events
List raw events from the WAL — the append-only source of truth.
Authorization
bearer PASETO v4 public token (or deployment gate key). Auth-disabled dev instances accept any caller.
In: header
Query Parameters
Response Body
curl -X GET "https://example.com/v1/events?scope=string"Authorization
bearer PASETO v4 public token (or deployment gate key). Auth-disabled dev instances accept any caller.
In: header
Path Parameters
Query Parameters
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"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 same404 NOT_FOUNDbody 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. Sendingscopetwice → a plain-text400("duplicate fieldscope"). 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[]listsevent_scope_filter_v1, or compare the returnedscopewith the one you expect. - It applies to this route only;
GET /v1/facts/{id}has noscopefilter.
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_cursoris omitted on the last page. Items are newest first by the server'scontext.recorded_at, whatever theirobserved_at. Defaultviewislocal; the enum islocal / granular / raw / structured / holistic / descend(anything else →422). - Filters that work:
labels(comma-separated, any-match),observed_actor,modality, andsince(RFC 3339).sincecomparescontext.recorded_at(when the server stored the event), notobserved_at: an event observed in 2020 but written today passessince=2024-01-01. - There is no
untilfilter: to bound the end of a time range, filter oncontext.recorded_aton 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 recalledlayers.eventsitem carries the samecaller,observed_actor,subject,scope_key,scopeandwal_offsetas this route (see Recall).limitis clamped rather than rejected:limit=0returns one item, andlimit=5000returns200. A missingscopequery param →400(query-string validation returns400; body validation returns422).- Embedding-provider outages: an event accepted while the embedding endpoint is down stays
captureduntil the provider returns, and is then indexed automatically with no operator action. During an outage the indexer holds rather than drops, so?wait=indexedwaits until the provider is back or its wait budget runs out (408 WAIT_TIMEOUTafter about 30 s on v0.10.2; see Experience). (Upgrade from v0.9.10 or earlier, where an outage could leave events stuck.)