Skip to main content

Client Socket Communication

Sockets

We use socket.io for our websocket connections. To get started you will need to initialise a websocket connection to the chat server host that is returned from webSettings under the namespace /c.

io(`wss://${chatServerHost}/c`, {
transports: ['websocket'],
reconnectionDelay: 10000,
reconnectionDelayMax: 15000,
});

You will need to handle the sockets connect and disconnect events, whether connecting first time or reconnecting following a socket disconnect, follow the standard procedure as outlined below with user connections.


User Connection

The session to the chat server is started by sending a user_add message with the current visitors ID. If this is a new visitor then a value of null can be sent to initialise the connection. The visitors userId will be returned by the chat server, along with an optional parameter mm that is only sent when maintenance mode is enabled on the chat server. If this is the case, then no new chats can be created, so it is advised to show a helper message to the visitor instead.

Client sends
["user_add", {
"userId": null
}]
Client receives
["user_connected", {
"userId": "EUUEnhja54z95u1tzYf5OcOREDxbPc5W",
"mm": true
}]

Error responses

If the provided data for the user_connect message is invalid.

Client receives
["user_rejected", {
"error": {
"code": "event_invalid_format",
"message": "Invalid format of event."
}
}]

Manually Tracking Visitors

In the context of Single Page Applications (SPAs) that use Ajax for routing (this setting is defined in webSettings under the hasAjaxRouting key), the system does not automatically track a visitor's navigation. For these scenarios, we can send a manual track message.

Here is an example of how we achieve this using MutationObserver.

function attachMutationObserverToBody(): void {
let href = location.href;

const observer = new MutationObserver(
debounce(() => {
if (href !== location.href) {
href = location.href;

trackVisitor(href);
}
}, 2000));

observer.observe(document.body, {childList: true, subtree: true});
}
Client sends
["track_visitor", {
"room": "123_identifier",
// The current webpage the visitor is on.
"pageUrl": "http://localhost/"
}]
Client receives
["track_visitor_confirmed", {
"room": "123_identifier",
"pageUrl": "http://localhost/"
}]

Error responses

If the user is invalid (i.e. not connected as per above).

Client receives
["track_visitor_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]

If the provided data for the track_visitor message is invalid.

Client receives
["track_visitor_rejected", {
"error": {
"code": "event_invalid_format",
"message": "Invalid format of event."
}
}]

If the room we are trying to check is not valid for the visitor, i.e. the visitor hasn't connected to the room.

Client receives
["track_visitor_rejected", {
"error": {
"code": "room_invalid",
"message": "Room name is invalid."
},
"origData": {
"room": "123_identifier",
"pageUrl": "http://localhost/"
}
}]

Room Connection

Once the visitor has connected to the chat server, this is when we will now want to connect to them to the correct room. Same scenario with initially connecting the visitor, if this is a new chat then set the chatId as null.

Client sends for new chat
["room_connect", {
"chatId": null,
// The identifier for the installation.
"room": "identifier",
// Provided via the webSettings API request.
"installationId": 123,
// The current webpage the visitor is on.
"pageUrl": "http://localhost/",
// (Optional) The visitor referrer if set, e.g. `new URL(document.referrer).hostname`.
"referrer": "www.google.com"
}]
Client receives for room connect
["room_connected", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
// The ID for the chat, used in subsequent requests to.
"chatId": "vZGlk8BbCe4J06WnJNquo5S1Bm2doknT",
// Whether there are any operators currently online, then show the offline form if applicable or do not show chat box.
"operatorOnline": true,
// Whether we are awaiting an answer from an operator, this will always be false for new chats.
"needAnswer": false,
// Whether the chat is currently active, the visitor has not interacted yet, so false is returned.
"chatActive": false,
// (Optional) If the chat is already assigned to an operator, e.g. for returning visitors / existing chats.
"operatorAssigned": {
// The optional avatar image for the operator.
"avatar": null,
// The ID of the operator.
"id": 153,
// The name of the operator.
"name": "Operator"
}
}]

With subsequent connections for existing visitors / chats, then you will also need to provide additional parameters for the chat server to know how to deal with the visitor's connection.

Client sends for existing chats
{
// The current existing chat for the visitor.
"chatId": "vZGlk8BbCe4J06WnJNquo5S1Bm2doknT",
// Whether the reconnecting visitor is awaiting an answer from an operator.
"needAnswer": false,
// Whether the reconnecting visitor's chat is active.
"chatActive": false
}

Error responses

If the user is invalid (i.e. not connected as per above).

Client receives
["room_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]

If the provided data for the room_connect message is invalid.

Client receives
["room_rejected", {
"error": {
"code": "event_invalid_format",
"message": "Invalid format of event."
}
}]

Team Status

If the installation has a qualification process, then the qualification answers could have a specific team associated with that endpoint. An answer should only be clickable if an operator for that team is online.

Client sends
["team_online_status", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
// The team ID to check for online operators.
"teamId": 99
}]
Client receives
["team_online_status", {
// The team ID we are returning a response for.
"teamId": 99,
// Whether operators are online for this team.
"online": true
}]

Error responses

If the user is invalid.

Client receives
["team_online_status_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]

If the data provided to team_online_status is in the incorrect format or missing parameters.

Client receives
["team_online_status_rejected", {
"error": {
"code": "event_invalid_format",
"message": "Invalid format of event."
}
}]

If the room we are trying to check is not valid for the visitor, i.e. the visitor hasn't connected to the room.

Client receives
["team_online_status_rejected", {
"error": {
"code": "room_invalid",
"message": "Room name is invalid."
},
"origData": {
"room": "123_identifier",
"teamId": 99
}
}]

Chat Box Visibility

When the visitor opens and closes the chat box, the chat server should be notified. This field is important to keep synced as this is visible by the operator (and can also be forcefully opened if closed) and is used for reporting statistics.

Client sends for opening
["chat_box_open", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
}]
Client receives for opening
["chat_box_opened", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
}]
Client sends for closing
["chat_box_close", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
}]
Client receives for closing
["chat_box_closed", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
}]

Error responses

These errors apply to both opening and closing. For closing, the message chat_box_close_rejected will be returned instead of chat_box_open_rejected.

If the user is invalid.

Client receives
["chat_box_open_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]

If the data provided to chat_box_open or chat_box_close is in the incorrect format or missing parameters.

Client receives
["chat_box_open_rejected", {
"error": {
"code": "event_invalid_format",
"message": "Invalid format of event."
}
}]

If the room we are trying to check is not valid for the visitor, i.e. the visitor hasn't connected to the room.

Client receives
["chat_box_open_rejected", {
"error": {
"code": "room_invalid",
"message": "Room name is invalid."
}
}]

Force Opening Chat Box

When it comes to chat box visibility, operators can force the chat box to remain open for visitors, even if it's closed initially.
In such cases, the server will issue the same chat_box_opened response, which triggers the chat box to open.


Visitor Typing

As part of the chat system, we send the current typing status and the visitor's preview message. For our own client, we use a debounced function which sends an initial typing_start while the visitor is typing, if the visitor hasn't typed for 1500ms then the client sends a typing_stop message, and if the visitor types again then this will emit another typing_start message etc.

Client sends when starting typing
["typing_start", {
"room": "123_identifier",
"clientId": "EUUEnhja54z95u1tzYf5OcOREDxbPc5W",
"message": "Hello can I",
"chatId": "vZGlk8BbCe4J06WnJNquo5S1Bm2doknT"
}]
Client sends when finished typing
["typing_stop", {
"room": "123_identifier",
"clientId": "EUUEnhja54z95u1tzYf5OcOREDxbPc5W",
// This can also be blank, e.g. if the visitor has submitted the message.
"message": "Hello can I enquire about the",
"chatId": "vZGlk8BbCe4J06WnJNquo5S1Bm2doknT"
}]

Operator Typing

As well as visitor typing events, we also notify the visitor of when the operator is typing.

Client receives when operator starting typing
["typing_start", {
"room": "123_identifier"
}]
Client receives when operator finished typing
["typing_stop", {
"room": "123_identifier"
}]

Sending a Message

There are a few different types and scenarios to account for when sending messages.
For the initial chat message (i.e. the message that starts the chat), there are various additional fields to send which dictate which operators the chat will go to, along with additional visitor tracking information.

When sending messages for an existing chat, the fields marked as (Optional) in the comments below can be omitted.
This results in an object with keys room, userId, message and type.

info

The types of messages that can be sent can be found here.

Client sends for new chat
["message_add", {
"room": "123_identifier",
// The ID of the visitor (clientId in the previous messages above).
"userId": "EUUEnhja54z95u1tzYf5OcOREDxbPc5W",
// The message the visitor has sent.
"message": "Hello can I enquire about the McLaren 720s",
// The type of message, use 'human' for visitor's text, alternative values are 'system' and 'audit'.
"type": "human",
// (Optional) The initial welcome message (see webSettings, `data.chatBox.welcomeMessage`).
"welcomeMessage": "Welcome. How may I help you?",
// (Optional) Whether we should be querying a specific team, this resolves from the last qualification process the visitor clicked.
"teamId": 123,
// (Optional) The route the visitor has clicked through the qualification process (if applicable).
"qualificationStepAnswers": [{ "id": 123, "name": "Sales" }],
// (Optional) window.VC_SETTINGS || window.vc_settings.
"pageSettings": { "id": "123_identifier", "data": {} },
// (Optional) If the visitor is set to provide details before entering the chat (see webSettings, `data.chatBox.detailsFormActive`).
"visitorDetails": {
"name": "visitor",
"telephone": "447123456789",
"email": "[email protected]"
}
}]
Client receives
["message_added", {
"room": "123_identifier",
"message": "Hello can I enquire about the McLaren 720s",
"messageType": "text"
}]

Chats Being Assigned to Operators

Until an operator has been assigned to a chat, then the state of the chat as per initial room_connect should be set to awaiting answer and show a loading screen or similar to the visitor. Assigning of operators via the chat server can either be automated if applicable for the room and there is an available operator, or the operator will need to claim the chat.

Client receives
["chat_assigned", {
"room": "123_identifier",
// The ID of the operator.
"userId": 153,
// The name of the operator.
"userName": "Operator",
// The optional avatar image for the operator.
"avatar": null,
// Visitors latest socket ID, however, we don't currently use this field ourselves.
"lastMessageSocketId": "pW6ttvdqr4l0nKShAAAP"
}]

If the installation supports offline messages and a value has been returned from webSettings for data.leaveMessage.showAfterNoAnswer, then in case a chat doesn't get assigned during this timeframe, the offline message form should be displayed.
As well as showing the offline message form, we need to advise the chat server that we have not been assigned an operator in time.

Client sends
["no_answer_timeout", {
"room": "123_identifier"
}]

Error responses

If the user is invalid.

Client receives
["no_answer_timeout_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]

If the data provided to no_answer_timeout is in the incorrect format or missing parameters.

Client receives
["no_answer_timeout_rejected", {
"error": {
"code": "event_invalid_format",
"message": "Invalid format of event."
}
}]

If the room we are trying to check is not valid for the visitor, i.e. the visitor hasn't connected to the room.

Client receives
["no_answer_timeout_rejected", {
"error": {
"code": "room_invalid",
"message": "Room name is invalid."
},
"origData": {
"room": "123_identifier"
}
}]

Incoming Messages

Messages that are sent to the visitor from either an operator or the chat server (system / audit messages) will come through the same event message. Chat messages can also be obtained via the messages api endpoint, this is useful for returning visitors / navigating the website to populate the current chats messages.

Client receives
["message_new", {
// The type of message; text, card or url.
"messageType": "text",
// The message contents.
"message": "Can I help you with anything else?",
"room": "123_identifier",
// The ID of the operator that sent this message.
"userId": 123
}]
Possible Message Types

For variations in chat message types, refer to our message format types guide.


Operators Leaving and Rejoining

If no operators are available in the visitor's room, an event message is sent to the client, prompting appropriate management of the local visitor's chat. Similarly, when an operator reenters a room, the client receives a notification, which should also be addressed accordingly.

Client receives when operator leaves
["room_left", {
"room": "123_identifier"
}]
Client receives when operator joins
["room_joined", {
"room": "123_identifier"
}]

Unassigned Chats

If an operator departs and a visitor's chat is still active, there's a possibility that the chat could become unassigned. This can also occur when chats are closed. Additionally, the unassigned message may indicate if a chat is held.

Client receives
["chat_unassigned", {
"room": "123_identifier",
"holdChatId": false
}]

Closed Chats

When an operator closes a visitor's chat, a message gets sent out telling whether that chat turned into a lead or not. We also share a timeout in seconds when closing chats - this denotes when the chat is officially considered closed. Keep in mind, if the visitor types something during this period, the chat stays active and the timeout should be removed. But if the timeout period passes without interruption, the active chat should be reset.

["chat_closing", {
"room": "123_identifier",
"chatId": "vZGlk8BbCe4J06WnJNquo5S1Bm2doknT",
// Number of seconds before chat is considered officially closed.
"closingTimeout": 20,
// Whether there are still operators online.
"operatorOnline": true,
// (Optional) Whether the chat was resolved as a lead.
"leadResolution": "Sales Lead"
}]

Held Chats

In addition to closing, chats can also be placed on hold. This functionality is particularly beneficial for SMS and WhatsApp style chats that often last for days, unlike typical website live chats. However, even live chats on websites can be held and will require appropriate management.

When a chat is in the held state, it follows the same procedure as closed chats described above. The key difference is that the chat ID persists and should not be cleared, this is dictated when the chat becomes unassigned. This ensures we can continue the conversation when the chat is retrieved from the held state.

Chats Lifespan

For our own website client, please note that we only keep held chats and local visitor storage around for a 24-hour period. After that, they are treated as new visitors.


Offline Messages

Installations can choose whether to allow visitors to leave offline messages when there are no operators available.

Also, the offline message feature comes into play when a chat doesn't get assigned to an operator within a set time. For more details on this, check out assigning operators.

Client sends
["offline_message_add", {
"room": "123_identifier",
// Visitor's name.
"name": "Tester",
// Visitor's e-mail (required if the phone field is empty).
"email": "[email protected]",
// Visitor's telephone (required if the email field is empty).
"phone": "",
// Visitor's message.
"message": "Hello can I enquire about the McLaren 720s"
}]
Client receives
["offline_message_added", {
"room": "123_identifier"
}]

Error responses

If the user is invalid.

Client receives
["offline_message_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]

If the provided data for the offline_message_add message is invalid.

Client receives
["offline_message_rejected", {
"error": {
"code": "event_invalid_format",
"message": "Invalid format of event."
}
}]

If the room we are trying to check is not valid for the visitor, i.e. the visitor hasn't connected to the room.

Client receives
["offline_message_rejected", {
"error": {
"code": "room_invalid",
"message": "Room name is invalid."
}
}]

Operator Pushing URLs

Within our chat system, operators possess the ability to direct visitors to a specific URL. In our own implementation, we achieve this by updating the current window location of the visitor to match the pushed URL.

Client receives
["chat_pushed", {
"room": "123_identifier",
"url": "http://localhost/new-url"
}]
note

URLs are not restricted to a specific address, and therefore, visitors can be directed to any URL. Operators are mindful of this flexibility and exercise caution when using this feature.


Chat ID Change

If a visitor's chat ID ever changes - say, if someone tries to start a chat that's already closed, then the server will send out an event with the new chat ID to use for the visitor's active chat.

Client recevies
["chat_change", {
"chatId": "vZGlk8BbCe4J06WnJNquo5S1Bm2doknT",
"newChatId": "uWRHgwb3uPDbSsHOEFmM9qhw0DyCf3x6",
"room": "123_identifier"
}]

Auto Closed Chats

In our system, any chat left open and inactive for a continuous duration of one hour is deemed incomplete. To maintain order, the system automatically terminates these chats. Although such incidents are rare in website-based chats, it's still an important feature to consider.

Client recevies
["chat_auto_closed", {
"room": "123_identifier"
}]

Maintenance Mode

In the event of server restarts or feature deployments to the chat server, we initially place the server in maintenance mode. This measure ensures that the server won't accept new chats during the changeover, allowing for a restart without any active chat disruptions.
While the server is in maintenance mode, we recommend displaying a notification to visitors advising them of the situation. Downtimes are typically brief, and our team always schedules any intended deployments outside peak hours.

Suggested message

Maintenance updates in progress. Temporary disruptions possible. Thank you for your patience.

Client receives when maintenance mode is enabled
["mm_enabled", {
// Delay field isn't currently used, but is returned, value is in seconds.
"delay": 30000
}]
Client receives when maintenance mode is disabled
["mm_disabled"]

Caveats

You will need to persist data across the visitor session to support browsing / returning visitors, e.g. localStorage.
In regard to persisting data, ensure the visitor has cookies enabled if applicable, such as in the case of localStorage.