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.
["user_add", {
"userId": null
}]
["user_connected", {
"userId": "EUUEnhja54z95u1tzYf5OcOREDxbPc5W",
"mm": true
}]
Error responses
If the provided data for the user_connect
message is invalid.
["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});
}
["track_visitor", {
"room": "123_identifier",
// The current webpage the visitor is on.
"pageUrl": "http://localhost/"
}]
["track_visitor_confirmed", {
"room": "123_identifier",
"pageUrl": "http://localhost/"
}]
Error responses
If the user is invalid (i.e. not connected as per above).
["track_visitor_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]
If the provided data for the track_visitor
message is invalid.
["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.
["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
.
["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"
}]
["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.
{
// 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).
["room_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]
If the provided data for the room_connect
message is invalid.
["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.
["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
}]
["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.
["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.
["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.
["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.
["chat_box_open", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
}]
["chat_box_opened", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
}]
["chat_box_close", {
// This is a combination of the installationId and the room.
"room": "123_identifier",
}]
["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.
["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.
["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.
["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.
["typing_start", {
"room": "123_identifier",
"clientId": "EUUEnhja54z95u1tzYf5OcOREDxbPc5W",
"message": "Hello can I",
"chatId": "vZGlk8BbCe4J06WnJNquo5S1Bm2doknT"
}]
["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.
["typing_start", {
"room": "123_identifier"
}]
["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
.
The types of messages that can be sent can be found here.
["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]"
}
}]
["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.
["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.
["no_answer_timeout", {
"room": "123_identifier"
}]
Error responses
If the user is invalid.
["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.
["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.
["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.
["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
}]
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.
["room_left", {
"room": "123_identifier"
}]
["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
.
["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.
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.
["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"
}]
["offline_message_added", {
"room": "123_identifier"
}]
Error responses
If the user is invalid.
["offline_message_rejected", {
"error": {
"code": "user_unidentified",
"message": "User is not identified."
}
}]
If the provided data for the offline_message_add
message is invalid.
["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.
["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.
["chat_pushed", {
"room": "123_identifier",
"url": "http://localhost/new-url"
}]
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.
["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.
["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.
Maintenance updates in progress. Temporary disruptions possible. Thank you for your patience.
["mm_enabled", {
// Delay field isn't currently used, but is returned, value is in seconds.
"delay": 30000
}]
["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.