WebSocket events
Real-time Socket.IO events: channels, event payloads and every notification type.
Websocket (Socket.io) events emitted by the websocket server (hosted separately from the REST API).
Connecting / channels. Every channel is a Socket.io namespace named /{resourceType}/{resourceId}[/{visibility}], where resourceType is one of workspace, project, user, chat, asset, submission, task or public, resourceId is a UUID (or legacy 24-char hex id) and the optional visibility is creator, reviewer or public. Public release channels are /public/{token} (10-character public token, no visibility suffix). Any other namespace is refused. Authenticate by passing query: { token } on the handshake: a user JWT, a personal access token or a bot API key (bot keys must connect through a configured bot gateway hostname). /public/{token} namespaces need no handshake token (the token in the namespace is validated instead); authenticated users may also join them. Failures surface as connect_error with message forbidden (invalid credentials or no permission) or badRequest (malformed channel / permission lookup error).
Permissions. A user may only join their own /user/{userId}. /project/{id}/creator, /reviewer and /public require canGetProjectCreatorNotifications, canGetProjectReviewerNotifications and canGetProjectPublicNotifications respectively; every other channel requires canGet{ResourceType}Notifications (direct or inherited).
Server -> client events: notification (every notification event documented below, emitted on each channel in the notification's channels list; the payload type selects the schema), tokenEvent, typing:start / typing:stop and the collab:* events (only on /chat/{chatId} namespaces). Client -> server events: ws:probe, typing:start / typing:stop, collab:*. Sockets whose JWT expired or was blacklisted, or whose bot key was revoked, receive tokenEvent and are disconnected shortly after.
Each event type below is documented using 'get' to represent the emitted event payload under the '200' response schema. Keys marked with (*) will be deprecated in a future release and should not be used for new development and should be replaced with values and objects in the changes key in existing implementations.
Servers
| Server | URL | Notes |
|---|---|---|
production | wss://ws.nurama.com/socket.io | WebSocket server for user credentials (JWT or personal access token). Socket.IO transport: connect a Socket.IO client to the channel address, not a raw WebSocket. |
bots | wss://bot-ws.nurama.com/socket.io | WebSocket server for bot API keys. |
Channels
Channels are Socket.IO namespaces. Connect a Socket.IO client to the namespace address; do not open a raw WebSocket.
Resource namespace
Address: /\{resourceType\}/\{resourceId\}/\{visibility\}
Notifications for one workspace, project, user, chat, asset, submission or task.
Socket.IO namespace named after the resource. The /{visibility} segment is optional: connect to /{resourceType}/{resourceId} for the default channel. Requires a handshake token. A user may only join their own /user/{userId}; /project/{id}/creator, /reviewer and /public require canGetProjectCreatorNotifications, canGetProjectReviewerNotifications and canGetProjectPublicNotifications; every other channel requires canGet{ResourceType}Notifications (direct or inherited).
| Parameter | Description |
|---|---|
resourceType | Type of the resource whose events to receive. |
resourceId | UUID (or legacy 24-character hex id) of the resource. |
visibility | Optional visibility sub-channel; omit the segment for the default channel. |
Public release namespace
Address: /public/\{token\}
Notifications for one public release, authenticated by the token in the namespace name.
No handshake token is needed (authenticated users may still send one). No visibility suffix. Typing indicators for chats that belong to the release are relayed here as well.
| Parameter | Description |
|---|---|
token | 10-character alphanumeric public release token. |
Collab session namespace
Address: /chat/\{resourceId\}
Real-time collaboration relay (cursors, drawing, presenter control) between the sockets of one chat.
The relay runs on namespaces starting with /chat/; the same connection also receives the notification events of the resource namespace. Relayed payloads are enriched server-side with the authenticated user identity, so client supplied identity keys are overwritten.
| Parameter | Description |
|---|---|
resourceId | UUID of the chat. |
Events
| Event | Direction | Channel | Summary |
|---|---|---|---|
| Receive notification | server → client | resource | Envelope event on which every notification listed in this section is delivered. The type property tells you which event schema applies. |
| Receive notification | server → client | public | Envelope event on which every notification listed in this section is delivered. The type property tells you which event schema applies. |
| Receive tokenEvent | server → client | resource | Emitted by the server right before it force-disconnects a socket whose credential is no longer valid. |
| Emit ws:probe | client → server | resource | Client -> server liveness probe. Emit with an acknowledgement callback; the server acks immediately with no payload. |
| Emit typing:start | client → server | resource | Ephemeral typing indicator. Client -> server: emit { chatId }. Server -> clients: relayed with the authenticated userId to every other socket on the chat channels. |
| Receive typing:start | server → client | resource | Ephemeral typing indicator. Client -> server: emit { chatId }. Server -> clients: relayed with the authenticated userId to every other socket on the chat channels. |
| Emit typing:stop | client → server | resource | Ephemeral typing indicator stop. Client -> server: emit { chatId }. Server -> clients: relayed with the authenticated userId to every other socket on the chat channels. |
| Receive typing:stop | server → client | resource | Ephemeral typing indicator stop. Client -> server: emit { chatId }. Server -> clients: relayed with the authenticated userId to every other socket on the chat channels. |
| Emit collab:join | client → server | collab | A user joined the collab session on this chat namespace (broadcast to the other members). |
| Receive collab:join | server → client | collab | A user joined the collab session on this chat namespace (broadcast to the other members). |
| Emit collab:leave | client → server | collab | A user left the collab session (explicit collab:leave from the client or socket disconnect). |
| Receive collab:leave | server → client | collab | A user left the collab session (explicit collab:leave from the client or socket disconnect). |
| Emit collab:cursor-move | client → server | collab | Remote cursor position update (percentage coordinates). |
| Receive collab:cursor-move | server → client | collab | Remote cursor position update (percentage coordinates). |
| Emit collab:cursor-hide | client → server | collab | Remote cursor left the asset area. |
| Receive collab:cursor-hide | server → client | collab | Remote cursor left the asset area. |
| Emit collab:path-start | client → server | collab | Pen drawing started. |
| Receive collab:path-start | server → client | collab | Pen drawing started. |
| Emit collab:path-update | client → server | collab | Pen drawing continued with additional points. |
| Receive collab:path-update | server → client | collab | Pen drawing continued with additional points. |
| Emit collab:path-end | client → server | collab | Pen drawing completed. |
| Receive collab:path-end | server → client | collab | Pen drawing completed. |
| Emit collab:path-clear | client → server | collab | Drawn path(s) cleared. With pathId only that path is cleared, otherwise all paths of the user. |
| Receive collab:path-clear | server → client | collab | Drawn path(s) cleared. With pathId only that path is cleared, otherwise all paths of the user. |
| Emit collab:click | client → server | collab | Click ripple at percentage coordinates. |
| Receive collab:click | server → client | collab | Click ripple at percentage coordinates. |
| Emit collab:color-change | client → server | collab | User changed their annotation colour. The server stores the new colour on the socket, so subsequent relayed events carry it. |
| Receive collab:color-change | server → client | collab | User changed their annotation colour. The server stores the new colour on the socket, so subsequent relayed events carry it. |
| Emit collab:region-create | client → server | collab | Audio/video region created on the waveform/timeline. |
| Receive collab:region-create | server → client | collab | Audio/video region created on the waveform/timeline. |
| Emit collab:region-remove | client → server | collab | Region removed. |
| Receive collab:region-remove | server → client | collab | Region removed. |
| Emit collab:zoom-sync | client → server | collab | Zoom level broadcast (followers of the presenter apply it). |
| Receive collab:zoom-sync | server → client | collab | Zoom level broadcast (followers of the presenter apply it). |
| Emit collab:playback-sync | client → server | collab | Play/pause/seek state broadcast by the presenter to the other members. |
| Receive collab:playback-sync | server → client | collab | Play/pause/seek state broadcast by the presenter to the other members. |
| Receive collab:playback-sync:rejected | server → client | collab | Server -> sender only: the sender is not the presenter, so its collab:playback-sync was dropped. Carries the real presenter and playback position so the client can snap back to follower mode. |
| Receive collab:presenter-assign | server → client | collab | Server -> all sockets in the namespace: a new presenter was assigned. |
| Receive collab:presenter-state | server → client | collab | Server -> joining socket: current presenter and playback state when a presenter already exists. |
| Emit collab:presenter-request | client → server | collab | Request for presenter control. From an admin it is granted immediately (collab:presenter-assign, reason admin-takeover); from a non-admin it is relayed to the current presenter socket only. |
| Receive collab:presenter-request | server → client | collab | Request for presenter control. From an admin it is granted immediately (collab:presenter-assign, reason admin-takeover); from a non-admin it is relayed to the current presenter socket only. |
| Emit collab:presenter-takeover | client → server | collab | Client -> server only: an admin or the current presenter hands presenter control to a target user. Results in a collab:presenter-assign (reason admin-takeover) to everyone; nothing is relayed under this name. |
| Emit collab:state-request | client → server | collab | Server -> the earliest joined other member: asks it to send its current annotation state to a late joiner (collab:state-response). |
| Receive collab:state-request | server → client | collab | Server -> the earliest joined other member: asks it to send its current annotation state to a late joiner (collab:state-response). |
| Emit collab:state-response | client → server | collab | Server -> the requesting socket only: sanitised snapshot of paths, regions and zoom provided by an existing member. |
| Receive collab:state-response | server → client | collab | Server -> the requesting socket only: sanitised snapshot of paths, regions and zoom provided by an existing member. |
| Receive collab:members | server → client | collab | Server -> joining socket: the members already present in the namespace. |
| Receive collab:self-color | server → client | collab | Server -> joining socket: the annotation colour assigned to it for this session. |
Notification types
126 notification types are delivered inside the notification envelope. See Notification types.