WebSocket Integration Guide
Build real-time dashboards and monitoring tools with the Haltless WebSocket API. This guide provides complete implementation examples for connecting, authenticating, handling messages, and reconnecting gracefully.
Connection overview
Client Haltless
│ │
│──── WebSocket connect ────────>│
│ │
│──── Send JWT token ──────────>│ (first message)
│ │
│<──── heartbeat ───────────────│ (periodic)
│<──── sensor_update ───────────│
│<──── alert ───────────────────│
│<──── machine_status ──────────│
The WebSocket streams all real-time events for your tenant: sensor readings, alerts, and machine status changes.
Quick start
JavaScript / TypeScript
const WS_URL = "wss://api.haltless.io/api/v1/ws/dashboard";
function connect(accessToken: string) {
const ws = new WebSocket(WS_URL);
ws.onopen = () => {
// Authenticate by sending the JWT as the first message
ws.send(accessToken);
console.log("Connected and authenticated");
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case "heartbeat":
// Connection is alive
break;
case "sensor_update":
console.log(
`${data.machine_id}: ${data.metric_name} = ${data.value} ${data.unit}`
);
break;
case "alert":
console.log(
`[${data.severity.toUpperCase()}] ${data.message}`
);
break;
case "machine_status":
console.log(
`Machine ${data.machine_id}: ${data.old_status} → ${data.new_status}`
);
break;
}
};
ws.onclose = (event) => {
console.log(`Disconnected: code=${event.code}`);
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
};
return ws;
}
Python
import asyncio
import json
import websockets
WS_URL = "wss://api.haltless.io/api/v1/ws/dashboard"
async def connect(access_token: str):
async with websockets.connect(WS_URL) as ws:
# Authenticate
await ws.send(access_token)
print("Connected and authenticated")
async for raw_message in ws:
data = json.loads(raw_message)
if data["type"] == "sensor_update":
print(f"{data['machine_id']}: {data['metric_name']} = {data['value']}")
elif data["type"] == "alert":
print(f"[{data['severity']}] {data['message']}")
elif data["type"] == "machine_status":
print(f"Machine {data['machine_id']}: {data['old_status']} → {data['new_status']}")
asyncio.run(connect("your-jwt-token"))
Authentication
The JWT token must be sent as the first text message after the WebSocket connection opens. This keeps the token out of URL query strings and server access logs.
Do not pass the token as a query parameter:
# ❌ Don't do this , token appears in server logs
wss://api.haltless.io/api/v1/ws/dashboard?token=eyJhbG...
# ✅ Do this , send token as first message after connect
ws.send(accessToken);
Authentication timeout
If the token is not sent within the timeout window, the server closes the connection with code 4002. Send the token immediately in your onopen handler.
Close codes
| Code | Meaning | Action |
|---|---|---|
4001 | Invalid or expired token | Refresh the JWT and reconnect |
4002 | Authentication timeout | Send token faster in onopen |
4003 | Too many pending connections | Back off and retry |
Message types
Heartbeat
Sent periodically by the server to keep the connection alive.
{
"type": "heartbeat",
"server_time": "2026-04-04T10:30:00Z"
}
Track heartbeats to detect stale connections. If you miss several consecutive heartbeats, proactively reconnect.
Sensor update
A new sensor reading was ingested (from an edge agent or direct ingestion).
{
"type": "sensor_update",
"machine_id": "uuid",
"metric_name": "temperature",
"value": 73.2,
"unit": "celsius",
"timestamp": "2026-04-04T10:30:05Z"
}
Alert triggered
An alert rule's threshold was crossed.
{
"type": "alert",
"alert_id": "uuid",
"machine_id": "uuid",
"severity": "critical",
"message": "Temperature exceeded threshold",
"metric_value": 87.3,
"threshold_value": 85.0,
"created_at": "2026-04-04T10:30:05Z"
}
Machine status change
A machine's health status changed (e.g., from healthy to warning).
{
"type": "machine_status",
"machine_id": "uuid",
"old_status": "healthy",
"new_status": "warning",
"timestamp": "2026-04-04T10:30:05Z"
}
Reconnection with exponential backoff
Network interruptions are inevitable. Implement automatic reconnection with backoff:
TypeScript
class HaltlessWebSocket {
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectDelay = 30_000; // 30 seconds
private accessToken: string;
constructor(accessToken: string) {
this.accessToken = accessToken;
this.connect();
}
private connect() {
this.ws = new WebSocket("wss://api.haltless.io/api/v1/ws/dashboard");
this.ws.onopen = () => {
this.ws!.send(this.accessToken);
this.reconnectAttempts = 0; // Reset on successful connection
console.log("WebSocket connected");
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
this.ws.onclose = (event) => {
console.log(`WebSocket closed: ${event.code}`);
if (event.code === 4001) {
// Token expired , refresh before reconnecting
this.refreshTokenAndReconnect();
return;
}
this.scheduleReconnect();
};
this.ws.onerror = () => {
// onerror is always followed by onclose, so reconnection
// is handled there
};
}
private scheduleReconnect() {
const delay = Math.min(
1000 * Math.pow(2, this.reconnectAttempts),
this.maxReconnectDelay
);
console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts + 1})`);
setTimeout(() => {
this.reconnectAttempts++;
this.connect();
}, delay);
}
private async refreshTokenAndReconnect() {
// Call your auth refresh endpoint
const response = await fetch("https://api.haltless.io/api/v1/auth/refresh", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token: "YOUR_REFRESH_TOKEN" }),
});
const { access_token } = await response.json();
this.accessToken = access_token;
this.connect();
}
private handleMessage(data: any) {
switch (data.type) {
case "heartbeat":
break;
case "sensor_update":
// Update your dashboard charts
break;
case "alert":
// Show alert notification
break;
case "machine_status":
// Update machine status indicators
break;
}
}
disconnect() {
this.ws?.close();
}
}
// Usage
const ws = new HaltlessWebSocket("your-jwt-token");
Python
import asyncio
import json
import websockets
WS_URL = "wss://api.haltless.io/api/v1/ws/dashboard"
async def connect_with_reconnect(access_token: str):
reconnect_attempts = 0
max_delay = 30
while True:
try:
async with websockets.connect(WS_URL) as ws:
await ws.send(access_token)
reconnect_attempts = 0
print("Connected")
async for message in ws:
data = json.loads(message)
handle_message(data)
except (websockets.ConnectionClosed, ConnectionError) as e:
delay = min(2 ** reconnect_attempts, max_delay)
print(f"Disconnected: {e}. Reconnecting in {delay}s...")
await asyncio.sleep(delay)
reconnect_attempts += 1
def handle_message(data: dict):
msg_type = data["type"]
if msg_type == "sensor_update":
print(f"{data['metric_name']}: {data['value']} {data['unit']}")
elif msg_type == "alert":
print(f"[{data['severity']}] {data['message']}")
elif msg_type == "machine_status":
print(f"Status: {data['old_status']} → {data['new_status']}")
asyncio.run(connect_with_reconnect("your-jwt-token"))
React integration
Use the WebSocket in a React dashboard with a custom hook:
import { useEffect, useRef, useCallback, useState } from "react";
interface SensorUpdate {
type: "sensor_update";
machine_id: string;
metric_name: string;
value: number;
unit: string;
timestamp: string;
}
interface AlertEvent {
type: "alert";
alert_id: string;
machine_id: string;
severity: string;
message: string;
}
type WsMessage = SensorUpdate | AlertEvent | { type: "heartbeat" | "machine_status"; [key: string]: any };
export function useHaltlessWebSocket(accessToken: string | null) {
const wsRef = useRef<WebSocket | null>(null);
const [lastMessage, setLastMessage] = useState<WsMessage | null>(null);
const [isConnected, setIsConnected] = useState(false);
const connect = useCallback(() => {
if (!accessToken) return;
const ws = new WebSocket("wss://api.haltless.io/api/v1/ws/dashboard");
ws.onopen = () => {
ws.send(accessToken);
setIsConnected(true);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type !== "heartbeat") {
setLastMessage(data);
}
};
ws.onclose = () => {
setIsConnected(false);
// Reconnect after 2 seconds
setTimeout(connect, 2000);
};
wsRef.current = ws;
}, [accessToken]);
useEffect(() => {
connect();
return () => wsRef.current?.close();
}, [connect]);
return { lastMessage, isConnected };
}
// Usage in a component
function LiveDashboard({ accessToken }: { accessToken: string }) {
const { lastMessage, isConnected } = useHaltlessWebSocket(accessToken);
useEffect(() => {
if (!lastMessage) return;
if (lastMessage.type === "sensor_update") {
// Update chart data
} else if (lastMessage.type === "alert") {
// Show toast notification
}
}, [lastMessage]);
return (
<div>
<span>{isConnected ? "🟢 Live" : "🔴 Disconnected"}</span>
{/* Your dashboard components */}
</div>
);
}
Performance tips
- Filter client-side , The WebSocket sends all tenant events. Filter by
machine_idin your message handler to show only relevant data. - Throttle UI updates , Sensor updates can arrive many times per second. Batch updates using
requestAnimationFrameor a throttle function to avoid excessive re-renders. - Use a single connection , Open one WebSocket per browser tab, not one per component. Share the connection via React context or a global store.
- Monitor heartbeats , Track the last heartbeat timestamp. If it's older than 3× the heartbeat interval, reconnect proactively.
Next steps
- WebSocket API Reference , close codes, message schemas, connection limits
- Sensor Data API , query historical data via REST
- Alert Rules , configure what triggers alert events