Events & Real-time
Almost everything that happens in Cortex emits a domain event: a session starts, a message is transcribed, a participant joins, a sanction is created, an invitation is accepted. These events are the backbone of real-time integrations — and they’re all drawn from a single catalog.
One catalog, three delivery surfaces
You choose how to receive events based on what you’re building:
Durable, retried, signed delivery to your backend. Best for server-to-server.
A live stream for dashboards and UI. The SDK’s watch() is built on this.
Run your own code on each event with named on<Event> handlers.
The event envelope
Every event shares the same envelope:
The payload is action-typed. On an updated event, changes carries the old values
of the fields that changed — so you can tell what actually changed without diffing against
your own copy.
See the Domain Events reference for the full list of event types and their payloads.
Server-Sent Events (SSE)
For live UI, open an SSE stream and receive events as they happen. The simplest way is the
SDK’s watch(), which loads the current state over REST and then keeps it in sync over
SSE — no manual EventSource, filtering or merge logic:
watch() works for collections and single objects, and resilient reconnection is handled
for you — every reconnect re-syncs state so you’re never left stale. See the
Real-time updates guide.
The raw stream
If you’re not using the SDK, open the stream yourself:
types(optional) is a comma-separated list of event patterns; wildcards likemessage.*work.- Authenticate with a header. API keys are never accepted as a query parameter, so a plain
browser
EventSource(which can’t set headers) can’t carry one — use afetch-based SSE client instead, as the SDK does. Keep API keys on the server in any case. - Each SSE message carries the event type as
event:, the eventidasid:, and the full JSON-encoded domain event asdata:. Every ~15 seconds the server also sends anevent: heartbeatmessage — if nothing at all arrives for much longer, reconnect. - With an API key you only receive events for the resource families its
scopes cover — e.g.
message.*needsmessages,sanction.*needssanctionsorsanctions.read. A key without any event-bearing scope can’t open a stream. - Long-lived streams opened with an API key are fully supported. Opening (or re-opening) a stream counts against the key’s rate limit, so back off between reconnects.
Server-Sent Events are for now: events that happen while you’re disconnected are not
replayed on reconnect. Re-read the current state after reconnecting (watch() does this for
you), or poll with a cursor. If
you need durable, retried delivery so nothing is missed while your service is offline, use
webhooks or functions
instead.
Subscribing from functions
In a serverless function, subscribe to an event by
exporting a handler named after it (message.created → onMessageCreated). The
subscription is derived from your exports — there’s no list to maintain. The handler
receives the domain event exactly as shown above.
Subscribing from webhooks
Pass the event names (or wildcards) when you create a subscription:
Webhook deliveries are only created for events that belong to a project.