Skip to main content

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:

ChannelURLPurpose
Dashboardwss://api.spatialflow.io/ws/dashboard/events/Aggregated stats and live activity feed
Geofence Eventswss://api.spatialflow.io/ws/events/geofence/Real-time geofence entry/exit events
Workflowwss://api.spatialflow.io/ws/workflows/{workflow_id}/Live workflow status updates
Workflow Executionwss://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
Who can open each channel

The /ws/webhooks/status/ channel requires is_superuser=True on the authenticated user. The Workflow and Workflow Execution channels require an owner or manager role in the workflow's workspace; a field worker's connection is refused. Both cases close the connection immediately with WebSocket close code 4003. The Dashboard and Geofence Events channels are open 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 TypeDescriptionPayload
dashboard_updateInitial dashboard snapshot (sent on connect){ stats: { active_geofences, active_devices, events_today, entries_today, exits_today, timestamp }, recent_events: [...], timestamp }
new_eventReal-time event push (sent for each new event){ event: { ... }, timestamp }

Geofence Events Channel​

Message TypeDescriptionPayload
geofence_eventDevice 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
  • Errors - Handle connection errors