Realtime Events (SSE)
Firetell provides a Server-Sent Events (SSE) stream for subscribing to real-time business events. Unlike WebSocket connections, SSE operates over standard HTTP/2 and natively handles automatic reconnections.
Architecture Note
SSE is used for server-to-client event observation (agent presence, teammate status changes, team assignments, contact mutations, queue updates). Call audio and WebRTC signaling use short-lived per-call WebSockets with call_token.
Endpoints​
| Method | Endpoint | Content-Type | Description |
|---|---|---|---|
GET | /stream?token=YOUR_AGENT_JWT | text/event-stream | Subscribes to real-time events |
PUT | /call-center/events/presence | application/json | Set own presence (busy / ready) |
Authentication​
Authentication is passed via the token query parameter: ?token=YOUR_AGENT_JWT.
Why Query Parameter?
The standard W3C browser EventSource API does not support custom HTTP headers (such as Authorization: Bearer). Passing the JWT via ?token= query parameter allows native browser EventSource to authenticate seamlessly.
Subscribing via JavaScript EventSource​
const agentJwt = "YOUR_AGENT_JWT";
const sseUrl = `https://{workspace_id}.firetell.app/stream?token=${encodeURIComponent(agentJwt)}`;
const eventSource = new EventSource(sseUrl);
// Incoming Call Ring Alert Event (Popup UI / Ring Device)
eventSource.addEventListener("call.ring", (e) => {
const data = JSON.parse(e.data);
console.log("Incoming call ring alert:", data.call_id);
console.log("Caller:", data.from.name, data.from.number);
console.log("WebSocket URL:", data.ws_url);
console.log("Call Token:", data.call_token);
// Connect WebSocket per call using ws_url and call_token
const ws = new WebSocket(data.ws_url);
ws.onopen = () => {
ws.send(
JSON.stringify({
event: "session.connect",
data: { token: data.call_token },
}),
);
};
});
// Call Lifecycle Events (Realtime Call List / Live Monitoring)
eventSource.addEventListener("call.created", (e) => {
const payload = JSON.parse(e.data);
console.log("Call created:", payload.data.call_id, payload.data.status);
});
eventSource.addEventListener("call.started", (e) => {
const payload = JSON.parse(e.data);
console.log("Call ringing / started:", payload.data.call_id, payload.data.to);
});
eventSource.addEventListener("call.answered", (e) => {
const payload = JSON.parse(e.data);
console.log("Call answered:", payload.data.call_id, payload.data.answer_time);
});
eventSource.addEventListener("call.ended", (e) => {
const payload = JSON.parse(e.data);
console.log(
"Call ended:",
payload.data.call_id,
payload.data.hangup_cause,
payload.data.duration,
);
});
// Agent State Change Event
eventSource.addEventListener("agent.state", (e) => {
const data = JSON.parse(e.data);
console.log("Teammate state changed:", data.username, data.state);
});
// Contact Created Event
eventSource.addEventListener("contact.created", (e) => {
const eventData = JSON.parse(e.data);
console.log("New contact created:", eventData.data);
});
// Team Assigned Event
eventSource.addEventListener("team.assigned", (e) => {
const data = JSON.parse(e.data);
console.log("Assigned to team:", data.team_id, "Role:", data.role);
});
Set Agent Presence​
Agents can set their own presence state to busy (Do Not Disturb) or ready (return to active state).
ready is not a stored state — it's a command that tells the system to revert the agent to their appropriate active state based on connectivity:
- If the agent has an active SSE connection →
online - If the agent has a registered VoIP push device →
available - Otherwise →
offline
Request​
PUT /call-center/events/presence
Authorization: Bearer YOUR_AGENT_JWT
Content-Type: application/json
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
state | string | ✅ | busy to set Do Not Disturb, ready to return to active state |
{
"state": "busy"
}
Response​
{
"username": "agent.jane",
"state": "busy"
}
Unset busy (return to active):
// Request
{ "state": "ready" }
// Response — state determined by connectivity
{ "username": "agent.jane", "state": "online" }
Event Types​
system.connected​
Emitted immediately upon opening the SSE stream. Contains the list of Server-Sent Events (SSE) Pub/Sub channels subscribed for the agent (ws:{workspace_id}:{username}, presence:{workspace_id}, workspace:{workspace_id}, and team:{workspace_id}:{team_id} for all teams the agent belongs to).
{
"event": "system.connected",
"workspace_id": "ws_123456789",
"username": "agent.jane",
"subscribed_channels": [
"ws:ws_123456789:agent.jane",
"presence:ws_123456789",
"workspace:ws_123456789",
"team:ws_123456789:tm_sales123"
],
"timestamp": "2026-07-21T19:20:00.000Z"
}
system.ping​
Emitted periodically every 15 seconds to maintain stream activity and prevent intermediate proxies, NGINX, or load balancers from closing the connection due to idle timeouts.
{
"event": "system.ping",
"timestamp": "2026-07-21T19:20:15.000Z"
}
call.ring​
Emitted when an incoming or transferred call is ringing for the agent. Triggered instantly over the SSE stream to allow the client application to display the Ringing UI Popup and establish a short-lived WebSocket connection to ws_url per call using the provided call_token.
{
"event": "call.ring",
"call_id": "call_1770000000000",
"call_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"ws_url": "wss://ws_123456789.firetell.app/ws",
"from": {
"number": "+12025550143",
"name": "Alice Smith"
},
"to": {
"number": "+18005550199",
"name": "Support Team"
},
"is_transfer": false,
"timestamp": "2026-07-30T16:07:00.000Z"
}
Call Lifecycle Events​
Call lifecycle events stream real-time updates for call state transitions (creation, ringing, answered, ended). They carry the full Call object inside data and are used to power live dashboards, call monitoring tables, and real-time history updates.
Role-Based Event Scoping
- Console Admin / Leader / Supervisor: Subscribes to the workspace firehose (
calls:{workspace_id}:all) and receives all call events in real time. - Member (Agent): Receives only calls relevant to their scope:
- Calls where the agent is the caller (
from.number) or callee (to.number). - Calls dispatched to teams the agent belongs to.
- Inbound/Outbound calls on phone numbers shared with the agent's teams.
- Calls where the agent is the caller (
call.created​
Emitted when a new call session is initialized in the workspace.
{
"event": "call.created",
"workspace_id": "ws_123456789",
"data": {
"id": "call_5c1180ce64848be4395d12",
"call_id": "call_5c1180ce64848be4395d12",
"workspace_id": "ws_123456789",
"number": "18005550199",
"client_number": "12025550143",
"from": {
"type": "client",
"number": "12025550143",
"name": "Alice Smith",
"avatar": null
},
"to": {
"type": "agent",
"number": "18005550199",
"name": "Support Desk",
"avatar": null
},
"source": "client-api",
"type": "audio",
"direction": "inbound",
"status": "started",
"start_time": "2026-08-14T08:32:13.819Z",
"answer_time": null,
"end_time": null,
"duration": 0,
"billsec": 0,
"cost": 0,
"hangup_cause": null,
"hangup_code": null,
"hangup_source": null,
"recordings": [],
"queue": null,
"outbound": null,
"agent_id": null,
"call_flow_id": null,
"created_at": "2026-08-14T08:32:13.819Z",
"updated_at": "2026-08-14T08:32:13.819Z"
},
"timestamp": "2026-08-14T08:32:13.825Z"
}
call.started​
Emitted when the call begins ringing / progressing to an agent, team, or carrier destination.
{
"event": "call.started",
"workspace_id": "ws_123456789",
"data": {
"id": "call_5c1180ce64848be4395d12",
"call_id": "call_5c1180ce64848be4395d12",
"workspace_id": "ws_123456789",
"number": "18005550199",
"client_number": "12025550143",
"from": {
"type": "client",
"number": "12025550143",
"name": "Alice Smith",
"avatar": null
},
"to": {
"type": "agent",
"number": "agent.jane",
"name": "Jane Doe",
"avatar": null
},
"source": "client-api",
"type": "audio",
"direction": "inbound",
"status": "started",
"start_time": "2026-08-14T08:32:13.819Z",
"answer_time": null,
"end_time": null,
"duration": 0,
"billsec": 0,
"cost": 0,
"hangup_cause": null,
"hangup_code": null,
"hangup_source": null,
"recordings": [],
"queue": null,
"outbound": null,
"agent_id": "ag_123456",
"call_flow_id": "flow_123456",
"created_at": "2026-08-14T08:32:13.819Z",
"updated_at": "2026-08-14T08:32:23.071Z"
},
"timestamp": "2026-08-14T08:32:23.075Z"
}
call.answered​
Emitted when the call is answered by a destination agent or connected via bridge.
{
"event": "call.answered",
"workspace_id": "ws_123456789",
"data": {
"id": "call_5c1180ce64848be4395d12",
"call_id": "call_5c1180ce64848be4395d12",
"workspace_id": "ws_123456789",
"number": "18005550199",
"client_number": "12025550143",
"from": {
"type": "client",
"number": "12025550143",
"name": "Alice Smith",
"avatar": null
},
"to": {
"type": "agent",
"number": "agent.jane",
"name": "Jane Doe",
"avatar": null
},
"source": "client-api",
"type": "audio",
"direction": "inbound",
"status": "active",
"start_time": "2026-08-14T08:32:13.819Z",
"answer_time": "2026-08-14T08:32:28.140Z",
"end_time": null,
"duration": 0,
"billsec": 0,
"cost": 0,
"hangup_cause": null,
"hangup_code": null,
"hangup_source": null,
"recordings": [],
"queue": null,
"outbound": null,
"agent_id": "ag_123456",
"call_flow_id": "flow_123456",
"created_at": "2026-08-14T08:32:13.819Z",
"updated_at": "2026-08-14T08:32:28.140Z"
},
"timestamp": "2026-08-14T08:32:28.145Z"
}
call.ended​
Emitted when the call finishes and all channels are terminated. Contains final call metrics (duration, billsec, hangup_cause, cost).
{
"event": "call.ended",
"workspace_id": "ws_123456789",
"data": {
"id": "call_5c1180ce64848be4395d12",
"call_id": "call_5c1180ce64848be4395d12",
"workspace_id": "ws_123456789",
"number": "18005550199",
"client_number": "12025550143",
"from": {
"type": "client",
"number": "12025550143",
"name": "Alice Smith",
"avatar": null
},
"to": {
"type": "agent",
"number": "agent.jane",
"name": "Jane Doe",
"avatar": null
},
"source": "client-api",
"type": "audio",
"direction": "inbound",
"status": "completed",
"start_time": "2026-08-14T08:32:13.819Z",
"answer_time": "2026-08-14T08:32:28.140Z",
"end_time": "2026-08-14T08:32:35.676Z",
"duration": 22,
"billsec": 7,
"cost": 0.005,
"hangup_cause": "NORMAL_CLEARING",
"hangup_code": 200,
"hangup_source": "caller",
"recordings": [],
"queue": null,
"outbound": null,
"agent_id": "ag_123456",
"call_flow_id": "flow_123456",
"created_at": "2026-08-14T08:32:13.819Z",
"updated_at": "2026-08-14T08:32:35.676Z"
},
"timestamp": "2026-08-14T08:32:35.680Z"
}
agent.state​
Emitted whenever an agent's presence state changes. State transitions happen automatically based on agent connectivity and call activity.
| State | Description |
|---|---|
online | Agent is actively connected via SSE event stream |
available | Agent is reachable via VoIP push notification (has registered device with push token) but not actively streaming SSE |
incall | Agent is currently in an active call (set when agent offers or answers a call, reverted when call ends) |
busy | Agent is busy (set manually or by supervisor) |
offline | Agent has no active connections or registered push devices |
{
"event": "agent.state",
"workspace_id": "ws_123456789",
"username": "agent.jane",
"state": "online",
"timestamp": "2026-07-30T16:07:00.000Z"
}
State transition examples:
- Agent opens the app and connects SSE →
online - Agent closes the app but has VoIP push token registered →
available - Agent answers or makes a call →
incall - Call ends while SSE is connected →
online - Call ends while only push token exists →
available - Agent logs out from all devices →
offline
agent.state.forced​
Emitted when a team leader or supervisor force-changes an agent's state. Currently, supervisors can only force agents to offline state. Automatic states (online, available, incall) are managed by the system based on connectivity.
{
"event": "agent.state.forced",
"workspace_id": "ws_123456789",
"id": "650000000000000000000001",
"target_agent_id": "650000000000000000000001",
"target_username": "agent.john",
"new_state": "offline",
"reason": "Shift ended",
"forced_by": "supervisor.smith",
"timestamp": "2026-07-30T18:00:00.000Z"
}
agent.created / agent.updated / agent.deleted​
Emitted when a workspace agent account is created, updated, or deleted.
{
"event": "agent.created",
"workspace_id": "ws_123456789",
"data": {
"id": "650000000000000000000001",
"username": "agent.john",
"display_name": "John Doe",
"email": "john@company.com",
"role": "agent",
"avatar": "https://cdn.firetell.com/avatars/agent_john.png",
"country_code": "US",
"is_active": true
},
"timestamp": "2026-08-03T15:00:00.000Z"
}
contact.created / contact.updated / contact.deleted​
Emitted when a contact is created, updated, or deleted within the workspace.
{
"event": "contact.created",
"workspace_id": "ws_123456789",
"data": {
"id": "ct_987654321",
"display_name": "John Doe",
"phone_numbers": [{ "phone_number": "+1234567890", "type": "mobile" }],
"owner_type": "everyone"
},
"timestamp": "2026-07-21T16:00:00.000Z"
}
team.assigned / team.unassigned​
Emitted when an agent is assigned to or removed from a team.
{
"event": "team.assigned",
"workspace_id": "ws_123456789",
"team_id": "tm_123456789",
"agent_id": "ag_123456789",
"role": "member",
"timestamp": "2026-07-21T16:00:00.000Z"
}