SCAD Maps.Log inRequest a demo

Realtime events

The Socket.IO events that keep every screen live: names, scopes and payload shapes.

The screens of everyone on an instance stay in step through a live connection. When somebody adds a note, moves an area or sends a message, the server pushes the change to every device that has that part of the map or that channel open. This page lists the events, who receives them and what they carry. It is meant for people building a wall display, a monitor or another integration on top of an instance. Requests that create and change data are described in REST API.

Connecting#

  • The connection is a Socket.IO connection to the instance's own address. The transport is WebSocket, with long polling as fallback.
  • Pass the access token in the handshake: io(origin, { auth: { token } }). A missing, invalid or revoked token is refused with an Unauthorized error.
  • Two namespaces exist. The default one carries content, presence and chat. /loc carries live positions. Both take the same token.
  • An access token lasts 15 minutes and so does the right to stay connected. About a minute before it runs out the server sends auth:expiring; answer with auth:renew and a fresh token to stay connected. If you do not, the server closes the connection after a short grace period, and a normal reconnect signs in again. Guest sessions cannot be extended past their fixed window.
  • If a session is revoked, for example after a password reset or a refresh token replay, the server disconnects its connections immediately.
  • Reconnect with a jittered back-off, and after every reconnect load the current state through the REST API before trusting the stream again.

Where events are sent#

The server does not broadcast everything to everyone. Each connection joins rooms, and each event is sent to the rooms it concerns.

ScopeHow you joinUsed for
Map regionSend rooms:join_by_bounds with { south, west, north, east }. The server answers rooms:joined with the region keys. Joining again replaces the previous set.Notes, areas, tasks
Chat channelSend chat:subscribe with { channelIds: [...] } (at most 200). Only channels the caller may read are joined.Messages, typing, receipts
PersonalAutomaticEvents addressed to one person, such as read state and geofence warnings
Location cellOn /loc, send viewport:update with the same bounds. The answer is viewport:rooms.Live positions
EveryoneAutomaticPresence (except guests), building annotations and model placements

Map regions are cells of 0.1 degrees. A very large or invalid rectangle joins no rooms, so ask for the area the screen really shows. Restrictions apply on top of rooms: a restricted user is only sent items of their own groups, and person-private tasks go only to the people entitled to them. See Roles and permissions matrix. Guests receive only the events for notes marked visible to guests.

Notes, areas and tasks#

These events follow one pattern: the noun, then what happened. created and updated carry the full record as the REST API returns it. Removal events carry the identifier and the acting user's name.

EventPayloadSent when
note:createdthe noteA note is created, restored, or moves into a region you watch.
note:updatedthe noteText, position, attachments or checklist ticks change.
note:deleted{ id, username }Deleted, or moved out of your region.
note:archived, note:unarchivedidentifier and actor, the full note on unarchiveArchive state changes.
zone:created, zone:updatedthe areaSent to every region the area overlaps.
zone:deleted{ id }An area is removed.
task:created, task:updatedthe task or incidentAny change, including status, assignment and acknowledgement.
task:deletedidentifier and actorDeleted, or moved out of your region.
task:archived, task:unarchivedidentifier, the full task on unarchiveArchive state changes.
task:unplacedthe taskA task lost its location, so it leaves the map but stays in lists.

Presence#

EventDirectionPayload
presence:deltaServer to client, at most once per second{ connected: [{ userId, username, connectedAt }], disconnected: [{ userId, username }] }
get_online_usersClient to serverAsks for the current roster.
users:online_listServer to clientThe roster as [{ userId, username }].
presence:activeClient to server{ active: true } while the app is in the foreground. The server then skips redundant push notifications.

Guests receive neither the roster nor the deltas, and do not appear in them.

Live location#

Positions travel on the /loc namespace and are only sent for people who chose to share.

EventDirectionPayload
loc:enable, loc:disableClient to serverStarts or stops sharing for this connection. Guests cannot share.
loc:updateClient to server{ lat, lng, acc, speed, heading, ts }. At most 10 a second per connection. Extra updates are dropped.
viewport:updateClient to serverBounds of what the screen shows, so the server sends positions for that area only.
loc:batchServer to clientA list of the latest position of everyone who moved in your area. One batch every 500 milliseconds per cell, and a batch is dropped rather than queued when your connection is slow, because the next one replaces it.
loc:leaveServer to client{ userId } when someone stops sharing or disconnects.

Each entry of a batch holds userId, username, the coordinates and the accuracy, speed and heading when the device provided them. Stored positions expire about ten minutes after the last update; see Security and privacy.

Chat#

EventPayloadSent to
chat:message{ channelId, message }Everyone subscribed to the channel.
chat:message:updated{ channelId, message }The same room, after an edit.
chat:message:deleted{ channelId, id }The same room.
chat:channel{ channel }A person who was added to a new channel, such as the other side of a new direct message.
chat:read{ channelId, lastReadAt }The reader's own other devices, to clear their unread counter.
chat:receipt{ channelId, userId, lastReadAt }The channel, to show read ticks.
chat:typing{ channelId, userId, username, isTyping }The channel, except the sender. Sent as an occasional hint: a typing event may be dropped without notice.
chat:channels:resyncnoneThe client should reload its channel list, since membership changed.
chat:errormessageA subscribe request failed.

Clients send chat:subscribe and chat:unsubscribe with { channelIds }, and chat:typing with { channelId, isTyping }. Typing hints are limited per connection. Guests have no chat and are not subscribed.

Areas with geofencing#

When an area has geofencing switched on, the server watches shared positions against it. These events are sent to the person concerned and to administrators.

EventMeaning
zone:enter, zone:exitA person entered or left the area. The person sees a warning on entering a high or critical area.
zone:occupancyThe current head count of the area, with its capacity when it has one.
zone:overflowThe head count exceeded the capacity.

Other events#

EventWho receives itMeaning
building:updated, building:deletedEvery connectionA building annotation changed. Carries the building identifier. See 3D buildings and models.
model:placementsEvery connection3D drawing models were placed or moved.
admin:account_requestAdministrators who can review itA new access request is waiting. See Invitations, QR codes and access requests.
ping and pongClient and serverA latency check: send ping, and the server answers pong.

Practical advice#

  1. Connect, then join the regions and channels you need.
  2. Load the current data through the REST API for the same area.
  3. Apply events on top, keyed by identifier. An event for an item you already have replaces it.
  4. On a reconnect, or after chat:channels:resync, load again. Events sent while you were away are not replayed.
  5. Renew the token when auth:expiring arrives, so a wall display never drops off.

Something missing or unclear? Email info@anivizzion.nl.