WebSocket Real-time Updates
SpatialFlow provides WebSocket connections for streaming real-time device locations, geofence events, and dashboard statistics without polling.
Overview
WebSockets enable your application to receive instant updates as devices move and trigger geofence events. The following channels are available:
| Channel | URL | Purpose |
|---|---|---|
| Dashboard | wss://api.spatialflow.io/ws/dashboard/events/ | Aggregated stats and live activity feed |
| Geofence Events | wss://api.spatialflow.io/ws/events/geofence/ | Real-time geofence entry/exit events |
| Workflow | wss://api.spatialflow.io/ws/workflows/{workflow_id}/ | Live workflow status updates |
| Workflow Execution | wss://api.spatialflow.io/ws/workflows/{workflow_id}/executions/{execution_id}/ | Step-by-step execution progress |
| Webhook Status (admin only) | wss://api.spatialflow.io/ws/webhooks/status/ | Webhook delivery status updates |
The /ws/webhooks/status/ channel requires is_superuser=True on the authenticated user. Non-admin clients are disconnected immediately with WebSocket close code 4003. All other channels listed above are available to any authenticated workspace user.
Authentication
Pass your JWT access token via the Sec-WebSocket-Protocol subprotocol header:
Sec-WebSocket-Protocol: access_token, YOUR_JWT_TOKEN
JWTs in WebSocket query strings are rejected because URLs can be retained in proxy and application access logs.
Connecting
JavaScript Example
const token = 'YOUR_JWT_TOKEN';
const ws = new WebSocket(
`wss://api.spatialflow.io/ws/dashboard/events/`,
['access_token', token]
);
ws.onopen = () => {
console.log('Connected to SpatialFlow WebSocket');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data.type, data);
};
ws.onclose = (event) => {
console.log('Disconnected:', event.code, event.reason);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
Python Example
import asyncio
import websockets
import json
async def connect():
token = "YOUR_JWT_TOKEN"
uri = "wss://api.spatialflow.io/ws/dashboard/events/"
async with websockets.connect(uri, subprotocols=["access_token", token]) as ws:
async for message in ws:
data = json.loads(message)
print(f"Received: {data['type']}")
Message Types
Dashboard Channel
| Message Type | Description | Payload |
|---|---|---|
dashboard_update | Initial dashboard snapshot (sent on connect) | { stats: { active_geofences, active_devices, events_today, entries_today, exits_today, timestamp }, recent_events: [...], timestamp } |
new_event | Real-time event push (sent for each new event) | { event: { ... }, timestamp } |
Geofence Events Channel
| Message Type | Description | Payload |
|---|---|---|
geofence_event | Device entered or exited a geofence | { event: { ... }, timestamp } |
Message Format
Messages are JSON objects with a type field identifying the event kind. Here is an example geofence event:
{
"type": "geofence_event",
"event": {
"id": "6f9c1e2a-1b3d-4c5e-8a7b-9d0e1f2a3b4c",
"event_type": "entry",
"device": {
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567890",
"device_id": "truck-005",
"name": "Delivery Truck 5",
"type": "vehicle"
},
"geofence": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Downtown Delivery Zone",
"description": "Primary downtown drop-off area"
},
"timestamp": "2026-02-05T14:30:00Z",
"location": {
"latitude": 37.7749,
"longitude": -122.4194,
"accuracy": 8.5,
"speed": 12.4
},
"workflows_triggered": ["a1b2c3d4-e5f6-7890-abcd-ef0123456789"],
"created_at": "2026-02-05T14:30:00Z"
},
"timestamp": "2026-02-05T14:30:00Z"
}
The device and geofence fields are nested objects, not flat device_id/geofence_name values. event_type is "entry" or "exit", and workflows_triggered lists the IDs of any workflows the event fired.
Reconnection Strategy
WebSocket connections can drop due to network issues. Implement automatic reconnection with exponential backoff:
function connectWithRetry(url, token, maxRetries = 10) {
let retries = 0;
function connect() {
const ws = new WebSocket(url, ['access_token', token]);
ws.onopen = () => {
console.log('Connected');
retries = 0; // Reset on successful connection
};
ws.onclose = (event) => {
if (retries < maxRetries) {
const delay = Math.min(1000 * Math.pow(2, retries), 30000);
console.log(`Reconnecting in ${delay}ms...`);
retries++;
setTimeout(connect, delay);
}
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
handleMessage(data);
};
return ws;
}
return connect();
}
Next Steps
- Devices - Send device location updates
- Geofences - Create geofence boundaries
- Error Handling - Handle connection errors