Help · section 38 of 44

Events (SSE, Polling, Webhooks)

Mirrors the in-app Help. The live copy in your console and `dlake guide help` are always current.

Row-change events captured per table by a tr_<t>_dl_Event trigger into a platform-managed outbox, consumable three ways:

  • PollingGET /api/ddl/events/changes?cursor=N&tables=&ops= (cursor-based).
  • SSE — mint a stream token (POST /events/stream/token), connect to /events/stream?token=...; Last-Event-ID resume, optional coalesce=<ms> batching; default is live-from-now, since=<id> replays history.
  • Webhooks — see Webhooks. Permissions: events.read to consume (all roles), events.manage (Admin) to enable capture per table or manage webhooks. Row-Level Security applies: on RLS-protected tables, insert/update events are delivered only if the consumer’s identity can read the row (SSE/polling: the caller; webhooks: the subscription owner). Delete events always pass (row key only). Suppressed events still advance cursors. Scope-restricted keys: a scoped key’s polling + SSE feed is narrowed to the entities it may read, and a field-restricted key also has each event’s payload column-filtered to its read field policy — excluded columns are stripped from data, rowKey, and changedColumns before delivery (a not-granted primary-key column is stripped from rowKey too; grant it to correlate on it).

SSE frame format — handle BOTH shapes; do NOT key on the event name:

  • Live (default after connect): one Server-Sent Event per change — event: insert | update | delete, and data: is a single event object: { "id", "occurredUtc", "schema", "table", "op", "rowKey": {<key columns>}, "data": {<full row, incl. control + dl_ columns>}, "changedColumns": [...], "actor": {...} }.
  • Replay (connect with &since=<cursor>): coalesced batches — event: batch, and data: = { "from": N, "to": M, "count": K, "events": [ <event object>, ... ] }.
  • :heartbeat comment lines are sent periodically to keep the connection open.

Track the highest event id you see (or to from a batch) and send it as &since= on reconnect for lossless replay. If the server detects a delivery gap it terminates the stream rather than silently skipping events; reconnect with your last &since= and the stream catches up from there before resuming live. Delete events carry rowKey only (no data).