Server-Sent Events stream
Opens a text/event-stream connection. NOT a JSON route: the 200 body is a stream, not a document. Frames: an initial `event: open` with data {"message": "Connection established"}; one frame per notification; a `: keepalive` comment every notifications.sse.heartbeat_seconds (default 25, and a non-positive configured value falls back to 25); and, on graceful shutdown, a final `event: close` with data {"reason": "server_shutdown"} that the manager flushes to every connected client BEFORE cancelling them. Treat that frame as "do not reconnect immediately" — it exists so a client can back off instead of retrying into a draining server. On connect the server replays system notifications the client missed, in two passes around registration so the handshake window is not skipped. THIS ROUTE HAS TWO 503 SHAPES AND THE SHUTDOWN MAP IS NEITHER. health.LifecycleMiddleware explicitly SKIPS /v1/notifications/sse, so that map never appears here. See the 503 below for the two that do. ACCESS-CONTROL-ALLOW-ORIGIN IS ECHOED FROM THE REQUEST'S Origin HEADER VERBATIM on the stream itself. The handler's OPTIONS branch, however, is dead code and no preflight ever reaches it: auth.CORSMiddleware is mounted on the root router ahead of everything and answers a preflight carrying an Origin itself — a bodiless 200 when the origin is allowed, 403 when it is not — while a preflight WITHOUT an Origin header falls through to chi, which has only GET and POST registered for this pattern and answers 405.
Authorization
BearerAuth In: header
Response Body
text/event-stream
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/v1/notifications/sse""event: open\ndata: {\"message\": \"Connection established\"}\n\n: keepalive\n\nevent: close\ndata: {\"reason\": \"server_shutdown\"}\n\n"{
"status": 400,
"message": "Invalid user id",
"code": "notifications_m.invalid_user_id",
"class": "validation"
}{
"status": 401,
"message": "Unauthorized",
"code": "common.unauthorized",
"class": "business"
}{
"status": 403,
"message": "No access to the record",
"code": "common.rbac_no_rec_access",
"class": "business"
}{
"status": 429,
"message": "Rate limit for 203.0.113.10 to 8f1d2c34-5b6e-4a70-9c81-2d3e4f5a6b7c exceeded.",
"code": "rate_limits_m.exceeded",
"class": "temporary",
"retryable": true
}{
"status": 500,
"message": "Internal server error",
"code": "common.server_error",
"class": "business"
}{
"status": 503,
"message": "Internal server error",
"code": "auth_m.internal_server_error",
"class": "temporary",
"retryable": true
}AUTHENTICATED BY X-API-Key, not by a bearer token — auth.APIKeyMiddleware replaces the usual chain, so none of the RBAC or licence refusals apply and no user context exists. Bypasses the queue and writes to connected clients directly. Recipients are every currently connected SSE client UNIONED with user_ids; supplying user_ids does not exclude connected users. When there are no connected clients AND no user_ids, the route answers 200 {"message": "No connected users to notify"} having done nothing. The body is not validated at all — no struct validator, no field checks.
AUTHENTICATED BY X-API-Key, like POST /v1/notifications/sse. Same body, same recipient union, same no-validation. The difference is the path: this one goes through the queue and the full processing pipeline rather than writing to SSE clients directly.