This document describes WebSocket/SignalR behavior. It is served as static HTML at /docs/signalr.html and linked from the Scalar API reference header.
Build the hub URL with the same host you use for HTTPS (or HTTP in dev). The JWT access token (user or device) is passed on the query string so the WebSocket handshake can authenticate:
wss://YOUR_HOST/hubs/fleet?access_token=YOUR_JWT wss://YOUR_HOST/hubs/pairing?access_token=OPTIONAL
PairingHub is [AllowAnonymous]; access_token is optional there. FleetHub requires a valid JWT (user or device) unless noted below.
After connect, the server adds connections to groups automatically:
| Group | Members |
|---|---|
account:{accountId} | All FleetHub connections whose JWT has that AccountId (web dashboards and devices). |
device:{deviceId} | FleetHub connections whose JWT includes DeviceId matching that device. |
/hubs/fleetMethod names below are C# hub names. JSON clients often use camelCase (reportLocation); negotiate with your SignalR client defaults.
| Method | Auth | Parameters | Returns / behavior |
|---|---|---|---|
RequestTokenRefresh |
AllowAnonymous | string refreshToken |
AuthResponse? — same shape as HTTP refresh; lets web UI refresh without reconnecting if connection still alive. |
ReportLocation |
Device JWT | double lat, double lon, double? batteryPercent, string? appVersion, string? appLanguage |
Updates in-memory location store; persists last lat/lon to DB; may notify account: group via DeviceUpdated. |
ReportDeviceInfo |
Device JWT | double? batteryPercent, string? appVersion, string? appLanguage |
Phone battery + app metadata before GPS fix; notifies dashboard. |
MessageReceived |
Device JWT | string messageId |
Stops server resend loop for that message; notifies web DeviceMessageReceived + DeviceMessageAcknowledged. |
MessageSeen |
Device JWT | string messageId |
Marks seen; broadcasts DeviceMessageSeen to account group. |
MessageResponse |
Device JWT | string messageId, string responseText |
Persists reply to DeviceMessages; broadcasts DeviceMessageResponse and DeviceMessageSeen. |
Subscribe with your client’s On handler using these exact event names:
| Event | Typical target | Arguments (order) |
|---|---|---|
DeviceUpdated |
account:* |
deviceId (uint), isConnected, lastLat, lastLon, lastLocationAt, batteryLevelPercent, appVersion, appLanguage, appInfoAt, syncState |
ReceiveMessage |
device:{id} |
messageId (string), body (string), displaySeconds (int) |
DeviceMessageReceived |
account:* |
deviceId, messageId |
DeviceMessageAcknowledged |
account:* |
deviceId, messageId — legacy alias for receipt |
DeviceMessageSeen |
account:* |
deviceId, messageId |
DeviceMessageResponse |
account:* |
deviceId, messageId, responseText, respondedAt (DateTime) |
ZoneSync |
Single connection or devices | List of zone DTOs (full list) — sent to a device on connect |
ZoneAdded |
Account + devices | Zone DTO |
ZoneDeleted |
Account + devices | zoneId (int) |
StartFastLocation |
device:{id} |
intervalSeconds (int), durationSeconds (int) |
StopFastLocation |
device:{id} |
(no args) |
RequestHistorySync |
device:{id} |
hours (int) — device should POST /api/devices/history-sync with cached samples |
RescueSessionStarted |
account:{id} + each device:{id} in the pair |
Object: sessionId, rescueeDeviceId, rescuerDeviceId, startedUtc; device-targeted sends also include yourRole (rescuee|rescuer), peerDeviceId, peerDisplayName |
RescuePeerLocation |
device:{peerId} only |
Object: peerDeviceId, lat, lon, atUtc |
RescueSessionEnded |
account:{id} + both devices |
Object: sessionId, endReason (completed | cancelled_by_dispatcher) |
/hubs/pairingAnonymous. Used while the app shows a pairing code (or before POST /api/devices/by-code completes).
| Method | Parameters | Returns |
|---|---|---|
RegisterConnectionForPairing |
string deviceGuid (32 hex, hyphens optional) |
bool — links this SignalR connection to the pending row created by POST /api/device/init |
RequestPairingCode |
(optional cancellation) | string — new 6-character code; creates PendingDevice tied to this connection |
| Event | When | Payload |
|---|---|---|
DeviceBound |
After web binds device via POST /api/devices/by-code (or debug trigger) |
Object: deviceId, accountId, signalREndpoint, name, identifier, accessToken, speedUnit, country, accountTimezone, periodicAirplaneModeOff |
POST /api/device/init — pairing code or registered device tokenPOST /api/devices/by-code — web binds code → server sends DeviceBoundPOST /api/devices/message (HTTP) — queues overlay; delivery via ReceiveMessagePOST /api/rescue/sessions, GET /api/rescue/sessions/active (JSON body when active, otherwise 204 No Content), POST .../ack-end, POST .../cancel — rescue lifecycleGenerated for DSP Fleet API. Keep in sync with Hubs/FleetHub.cs, Hubs/PairingHub.cs, ZonePushService, and DeviceMessageService.