Overview
The Events API provides a Server-Sent Events (SSE) stream for real-time updates from Mission Control. This persistent connection delivers database mutations, system events, and state changes as they happen.
SSE is a one-way communication channel from server to client. Clients connect via EventSource and receive JSON-encoded events. Connection includes automatic heartbeat to prevent proxy timeouts.
Establish SSE Connection
GET /api/events
Open a persistent SSE connection to receive real-time events.
Content-Type: text/event-stream
Cache-Control: no-cache, no-transform
Connection: keep-alive
X-Accel-Buffering: no
All events are JSON-encoded and follow this structure:
Event payload (varies by type)
Unix timestamp (milliseconds)
EventSource
Fetch API
Python (SSE-Client)
const eventSource = new EventSource ( '/api/events' , {
withCredentials: true
});
eventSource . onmessage = ( event ) => {
const data = JSON . parse ( event . data );
console . log ( 'Event received:' , data . type , data . data );
};
eventSource . onerror = ( error ) => {
console . error ( 'SSE connection error:' , error );
// EventSource will automatically reconnect
};
// Close connection when done
// eventSource.close();
Event Types
Connection Events
connected
Sent immediately upon establishing SSE connection.
{
"type" : "connected" ,
"data" : null ,
"timestamp" : 1709823456789
}
heartbeat
Sent every 30 seconds to keep connection alive through proxies.
Entity Events
agent:created
New agent provisioned.
{
"type" : "agent:created" ,
"data" : {
"id" : 8 ,
"name" : "DataAnalyst" ,
"role" : "analyst" ,
"status" : "offline" ,
"created_at" : 1709823456
},
"timestamp" : 1709823456789
}
agent:updated
Agent configuration or status changed.
{
"type" : "agent:updated" ,
"data" : {
"id" : 8 ,
"name" : "DataAnalyst" ,
"status" : "online" ,
"changes" : [ "status" ]
},
"timestamp" : 1709823567890
}
agent:deleted
Agent removed from system.
{
"type" : "agent:deleted" ,
"data" : {
"id" : 8 ,
"name" : "DataAnalyst"
},
"timestamp" : 1709823678901
}
task:created
New task created.
{
"type" : "task:created" ,
"data" : {
"id" : 42 ,
"title" : "Analyze Q4 metrics" ,
"status" : "inbox" ,
"priority" : "high" ,
"created_by" : "admin"
},
"timestamp" : 1709823789012
}
task:updated
Task modified (status, assignment, etc.).
{
"type" : "task:updated" ,
"data" : {
"id" : 42 ,
"title" : "Analyze Q4 metrics" ,
"status" : "in_progress" ,
"assigned_to" : "DataAnalyst" ,
"changes" : [ "status" , "assigned_to" ]
},
"timestamp" : 1709823890123
}
task:deleted
Task removed.
{
"type" : "task:deleted" ,
"data" : {
"id" : 42 ,
"title" : "Analyze Q4 metrics"
},
"timestamp" : 1709823901234
}
notification:created
New notification for an agent.
{
"type" : "notification:created" ,
"data" : {
"id" : 234 ,
"recipient" : "DataAnalyst" ,
"type" : "task_assigned" ,
"title" : "New Task Assigned" ,
"priority" : "high"
},
"timestamp" : 1709824012345
}
activity:created
New activity logged.
{
"type" : "activity:created" ,
"data" : {
"id" : 1523 ,
"type" : "task.status_changed" ,
"actor" : "DataAnalyst" ,
"entity_type" : "task" ,
"entity_id" : 42
},
"timestamp" : 1709824123456
}
System Events
alert:triggered
Alert rule condition met.
{
"type" : "alert:triggered" ,
"data" : {
"rule_id" : 5 ,
"rule_name" : "Agent Offline Alert" ,
"entity_type" : "agent" ,
"entity_id" : 8 ,
"message" : "Agent DataAnalyst has been offline for 10 minutes"
},
"timestamp" : 1709824234567
}
webhook:delivered
Webhook successfully delivered.
{
"type" : "webhook:delivered" ,
"data" : {
"webhook_id" : 3 ,
"event" : "task.created" ,
"status_code" : 200 ,
"response_time_ms" : 145
},
"timestamp" : 1709824345678
}
system:status_changed
System-wide status change.
{
"type" : "system:status_changed" ,
"data" : {
"component" : "gateway" ,
"status" : "degraded" ,
"message" : "High latency detected"
},
"timestamp" : 1709824456789
}
Connection Management
Automatic Reconnection
EventSource automatically reconnects on connection loss:
const eventSource = new EventSource ( '/api/events' );
let reconnectAttempts = 0 ;
eventSource . onerror = ( error ) => {
reconnectAttempts ++ ;
console . log ( `Connection lost. Reconnect attempt ${ reconnectAttempts } ` );
if ( reconnectAttempts > 5 ) {
console . error ( 'Too many reconnect attempts, giving up' );
eventSource . close ();
}
};
eventSource . onopen = () => {
reconnectAttempts = 0 ;
console . log ( 'SSE connection established' );
};
Graceful Shutdown
// Close connection cleanly
eventSource . close ();
// Or on page unload
window . addEventListener ( 'beforeunload' , () => {
eventSource . close ();
});
Event Filtering
Client-Side Filtering
const eventSource = new EventSource ( '/api/events' );
eventSource . onmessage = ( event ) => {
const { type , data } = JSON . parse ( event . data );
// Only handle task events
if ( type . startsWith ( 'task:' )) {
handleTaskEvent ( type , data );
}
// Ignore heartbeats
if ( type === 'heartbeat' ) return ;
// Handle high-priority notifications
if ( type === 'notification:created' && data . priority === 'high' ) {
showNotification ( data );
}
};
Use Cases
Live Dashboard Updates
const eventSource = new EventSource ( '/api/events' );
eventSource . onmessage = ( event ) => {
const { type , data } = JSON . parse ( event . data );
switch ( type ) {
case 'agent:updated' :
updateAgentStatus ( data . id , data . status );
break ;
case 'task:updated' :
refreshTaskBoard ();
break ;
case 'alert:triggered' :
showAlertBanner ( data . message );
break ;
}
};
Activity Feed
const activityFeed = [];
eventSource . onmessage = ( event ) => {
const { type , data , timestamp } = JSON . parse ( event . data );
if ( type === 'activity:created' ) {
activityFeed . unshift ({
... data ,
timestamp
});
// Keep only last 100
if ( activityFeed . length > 100 ) {
activityFeed . pop ();
}
renderActivityFeed ();
}
};
Real-Time Notifications
eventSource . onmessage = ( event ) => {
const { type , data } = JSON . parse ( event . data );
if ( type === 'notification:created' && data . recipient === currentAgent ) {
// Show browser notification
if ( Notification . permission === 'granted' ) {
new Notification ( data . title , {
body: data . message ,
icon: '/icon.png'
});
}
// Update UI
incrementUnreadCount ();
}
};
Connection Pooling : SSE connections are long-lived. Limit to 1 per client.
Heartbeat : 30s heartbeat prevents proxy timeouts.
Buffering : Response buffering is disabled (X-Accel-Buffering: no).
Browser Limits : Most browsers limit SSE connections to 6 per domain.
Error Handling
const eventSource = new EventSource ( '/api/events' );
eventSource . onerror = ( error ) => {
if ( eventSource . readyState === EventSource . CLOSED ) {
console . error ( 'Connection closed by server' );
} else if ( eventSource . readyState === EventSource . CONNECTING ) {
console . log ( 'Reconnecting...' );
}
};
eventSource . onmessage = ( event ) => {
try {
const data = JSON . parse ( event . data );
handleEvent ( data );
} catch ( error ) {
console . error ( 'Failed to parse event:' , error );
}
};
Authentication
SSE connections require valid authentication:
// Session cookie is automatically sent
const eventSource = new EventSource ( '/api/events' , {
withCredentials: true
});
// For cross-origin requests
const eventSource = new EventSource ( 'https://other-domain.com/api/events' , {
withCredentials: true
});
Error Responses
Status Code Description 401 Unauthorized - Invalid or missing session 403 Forbidden - Insufficient permissions (viewer role required)
If authentication fails, the connection will close immediately with an error event.