Connect by JBRH Open Connect

Server-sent events

Server-sent events is one HTTP response that never ends. The client makes an ordinary request, the server holds it open and writes text/event-stream records as things happen, and the browser reassembles them into events. It goes one way only — server to client — and it reconnects by itself. Connect's app uses it for its own event stream.

Status
Reference What this means
Audience
developer, both
Last verified
Product version
6.3.2

What goes down the wire#

GET /api/events HTTP/1.1
Accept: text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

event: thread.updated
id: 8412
data: {"thread_id":"th_example","state":"needs_you"}

: a comment line keeps the connection warm

event: call.ended
id: 8413
data: {"call_id":"cl_example"}

Three things in that transcript do the real work. Each record ends with a blank line, which is how the client knows an event is complete. The id field is remembered, and on reconnection the client sends it back as Last-Event-ID so the server can resume rather than restart. And a comment line — a line beginning with a colon — carries no event but keeps intermediaries from deciding an idle connection is a dead one.

How it differs from WebSocket#

Server-sent eventsWebSocket
DirectionServer to client onlyBoth ways
ProtocolOrdinary HTTP; no upgrade, no second schemeAn upgrade to ws:/wss:, a different protocol after the handshake
ReconnectionBuilt in, with resume from the last event idYours to write, including the resume logic
PayloadText recordsText or binary frames
InfrastructurePasses proxies and CDNs as a normal responseNeeds upgrade support along the whole path
Cost of a mistakeA dropped stream reconnectsA stalled socket can look alive indefinitely

The asymmetry is the feature, not a limitation to work around. An application whose client sends actions as ordinary requests and receives notifications as a stream has one authorisation path — every state change goes through the same middleware, the same allowlist and the same workspace filtering as any other request. A duplex socket invites a second path, and a second path is a second place for a permission check to be forgotten.

Does Connect use server-sent events?#

Used, for the application's own event stream. When a conversation changes, an approval appears in Needs You, or a call finishes, the browser learns about it over this stream rather than by polling. Connect does not use WebSocket for its own application data — that is a deliberate choice and not an accident of history.

WebSocket does appear in the product, in one unrelated place: SIP signalling for the browser softphone runs over it, because that is what the SIP-in-a-browser standard requires. Two different problems, two different transports; neither is a fallback for the other.

Operating one#

  • Send heartbeats. An idle connection is indistinguishable from a broken one to a proxy, and many will close it. A comment line every few seconds costs nothing and prevents a class of phantom disconnection.
  • Number your events. Without an id, a reconnection restarts and the client silently misses whatever happened during the gap — the most common way a live screen ends up quietly stale.
  • Treat an event as a nudge, not as the truth. The safe pattern is *something changed, go and read it*, because that re-reads through the normal authorised path. An event carrying a full record has to carry its own authorisation story.
  • Watch the per-origin connection budget. Over HTTP/1.1, browsers allow only a handful of connections to one origin, and a stream holds one for its whole life. Under HTTP/2 the limit is far higher; do not assume either.

Questions#

Why not just poll every few seconds?

Polling costs a full request round trip per interval per open tab, and it is still late by up to one interval. A held-open response costs one connection and delivers when the thing happens. Polling remains the right answer for a back-end integration that runs occasionally — that is a different question, answered separately for developers.

Can the client send anything back on the stream?

No. That is what an ordinary request is for. If you find yourself wanting a return channel, what you usually want is a normal API call plus the confirmation arriving as the next event.

What happens on a flaky mobile connection?

The browser reconnects on its own and replays the last event identifier it saw. Whether that resumes cleanly depends on the server honouring it — which is the argument for numbering events even when everything looks fine on a desk.