SSE Events (Server-Sent Events)¶
PulseStage uses Server-Sent Events (SSE) for real-time push updates. See /docs/architecture/real-time.md for full architecture.
Connection¶
- Endpoint:
GET /events?tenant={tenantSlug} - Auth: Requires session authentication
- Tenant: Resolved from query parameter (SSE can't send custom headers)
- Format:
text/event-stream - Heartbeat: Every 30 seconds to prevent timeout
Event Types¶
Q&A Events (Currently Implemented)¶
question:created¶
Emitted when a participant submits a question.
{
"type": "question:created",
"tenantId": "uuid",
"data": {
"id": "uuid",
"body": "Question text",
"teamId": "uuid",
"status": "OPEN",
"upvoteCount": 0,
"submittedAt": "2025-01-01T00:00:00Z"
},
"timestamp": 1234567890
}
question:upvoted¶
Emitted when a user upvotes a question.
{
"type": "question:upvoted",
"tenantId": "uuid",
"data": {
"questionId": "uuid",
"upvoteCount": 5
},
"timestamp": 1234567890
}
question:answered¶
Emitted when a moderator/admin answers a question.
{
"type": "question:answered",
"tenantId": "uuid",
"data": {
"id": "uuid",
"status": "ANSWERED",
"responseText": "Here's the answer..."
},
"timestamp": 1234567890
}
question:tagged, question:untagged¶
Emitted when tags are added/removed from a question.
question:pinned¶
Emitted when a question is pinned for presenter mode.
question:frozen¶
Emitted when the queue is frozen (no new questions accepted).
heartbeat¶
Keepalive ping sent every 30 seconds.
{
"type": "heartbeat",
"tenantId": "uuid",
"data": { "timestamp": 1234567890 },
"timestamp": 1234567890
}
Pulse Events (Not Currently Implemented)¶
Future consideration: Should pulse responses trigger SSE events for real-time dashboard updates?
Proposed schema for reference (from pulse.response.recorded.v1.json):
{
"event": "pulse.response.recorded.v1",
"tenantId": "uuid",
"questionId": "uuid",
"teamId": "uuid",
"score": 8,
"cohortName": "Weekday",
"submittedAt": "2025-01-01T00:00:00Z"
}
Implementation Reference¶
- EventBus:
/api/src/lib/eventBus.ts - Endpoint:
/api/src/app.ts(line ~1947) - Client Hook:
/web/src/hooks/useSSE.ts
Versioning¶
When adding/removing fields, use semver versioning in event type names. Do not break existing consumers.