Overview
A listener is a filter on one session’s event stream that pushes each match to the connection that added it. It lasts as long as that connection and is gone when the connection closes. Declared listeners, which act inside the session instead of pushing, are on Listeners.Add a listener
listener/add starts a listener on a session of a revision. The caller needs READ on the revision’s blob.
The server answers with the id of the new listener:
workflow.listeners_per_connection). Frames name their listener by
listener_id, which is how one connection tells its listeners apart.
Remove a listener
listener/remove ends a listener of this connection.
{} in result. A listener_id this connection does not hold, whether it never existed, was
already removed or belongs to another connection, is answered with a 400 invalid_params error and the connection
stays open.
Receive events
Withoutbatch, each matching event arrives in its own frame. The frame is a notification, so it has no id.
event is the event record that get_session_event returns.
Batch
Withbatch: {window_seconds?, max_events?, distinct_by?} events are collected and arrive together:
window_seconds(default 0) opens at the first pending event’s own time.max_events(default 100 with abatch) counts entries afterdistinct_by.distinct_byis up to 8 dotted paths. Events that agree on every path are one entry, which keeps the newest event.countis the number of entries inevents;first_idandlast_idare the ids of the first and last of them.- A frame is cut before it passes 120 KiB. An event that alone passes 120 KiB is never pushed; read it with
get_session_event.
batch is answered invalid_params and no listener is added.
Resume
Pushes are at least once and can be lost, and a connection lasts at most two hours, so a client that must not miss an event pairs the listener with the listing:- Connect, send
status/initialize, andlistener/addfirst, so nothing that happens next is missed. - Read what came before with
list_session_eventsin ascending order, withafter: <last id held>when there is one. - Read again from the boundary id of a timestamp a minute older than the last event seen, because a row can commit after a row with a larger id. This is the settle time recipe.
- Drop every id already held, from pushes and listing alike. An id is the event’s identity.
- After a reconnect, repeat from step 1 with
afterset to the last id held. Listeners do not survive a connection, so add them again.
Keep the connection
status/initialize must be the first request on a connection; any other method but status/ping before it is a 405
and the server closes the connection. status/ping answers {} in every phase and writes nothing. API Gateway closes
a connection idle for 10 minutes, so ping at least that often. It also closes every connection after 2 hours, whatever
its traffic. See Lifecycle.
Errors
An error answers the request that caused it, in the error shape.
A message that is not valid JSON-RPC, or that names an internal method, closes the connection without a reply. A 403
does not say whether the revision or session exists.

