Omnichannel Chat SDK π¬

β We recommend using official release versions in production as listed here. Support will be provided only on official versions.
π’ Try out our new React component library omnichannel-chat-widget with Chat SDK
Headless Chat SDK to build your own chat widget against Dynamics 365 Omnichannel Services.
Please make sure you have a chat widget configured before using this package or you can follow this link
Table of Contents
Live Chat Widget vs. Chat SDK
Omnichannel offers a live chat widget (LCW) by default. You can use the Chat SDK to build your custom chat widget if:
- You want to fully customize the user interface of the chat widget to conform with your branding.
- You want to integrate Omnichannel into your mobile app using React Native.
- You want to integrate additional functionalities that LCW does not offer.
- Some other cool ideas. Please share with us what you've achieved with the Chat SDK! π
Feature Comparisons
Bring Your Own Widget | β | β | |
Web Support | β | β | |
React Native Support | β | β | |
Escalation to Voice & Video | β | β | Only supported on Web |
Co-browse | β | 3rd party add-on | Only supported on Web |
Screen Sharing | β | 3rd party add-on | Only supported on Web |
Authenticated Chat | β | β | |
Pre-chat Survey | β | β | |
Post-chat Survey | β | β | |
Download Transcript | β | β | |
Email Transcript | β | β | |
Data Masking | β | β | |
File Attachments | β | β | |
Custom Context | β | β | |
Proactive Chat | β | BYOI * | |
Persistent Chat | β | β | |
Chat Reconnect | β | β | |
Operating Hours | β | β | |
Get Agent Availability | β | β | |
Queue Position | β | β | No SDK method. Handled as system message |
Average Wait Time | β | β | No SDK method. Handled as system message |
* BYOI: Bring Your Own Implementation
Releases
New releases are published on a regular basis to ensure the product quality.
Installation
npm install @microsoft/omnichannel-chat-sdk --save
Installation on React Native
The following steps will be required to run Omnichannel Chat SDK on React Native:
-
Install node-libs-react-native
npm install node-libs-react-native --save-dev
-
Install react-native-randomBytes
npm install react-native-randombytes --save-dev
-
Install react-native-get-random-values
npm install react-native-get-random-values --save-dev
-
Install react-native-url-polyfill
npm install react-native-url-polyfill --save-dev
-
Install @azure/core-asynciterator-polyfill
npm install @azure/core-asynciterator-polyfill --save-dev
-
Update metro.config.js to use React Native compatible Node Core modules
module.exports = {
resolver: {
extraNodeModules: {
...require('node-libs-react-native'),
net: require.resolve('node-libs-react-native/mock/net'),
tls: require.resolve('node-libs-react-native/mock/tls')
}
}
};
-
Add the following import on top of your entry point file
import 'node-libs-react-native/globals';
import 'react-native-get-random-values';
import 'react-native-url-polyfill/auto';
import '@azure/core-asynciterator-polyfill';
SDK Methods
Initialization
It handles the initialization of ChatSDK internal data.
import OmnichannelChatSDK from '@microsoft/omnichannel-chat-sdk';
const omnichannelConfig = {
orgUrl: "",
orgId: "",
widgetId: ""
};
const chatSDKConfig = {
dataMasking: {
disable: false,
maskingCharacter: '#'
}
};
const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig, chatSDKConfig);
const optionalParams = {
getLiveChatConfigOptionalParams: {
sendCacheHeaders: false
}
};
await chatSDK.initialize(optionalParams);
Start Chat
It starts an Omnichannel conversation.
const customContext = {
'contextKey1': {'value': 'contextValue1', 'isDisplayable': true},
'contextKey2': {'value': 12.34, 'isDisplayable': false},
'contextKey3': {'value': true}
};
const optionalParams = {
preChatResponse: '',
liveChatContext: {},
customContext,
sendDefaultInitContext: true
};
await chatSDK.startChat(optionalParams);
End Chat
It ends the current Omnichannel conversation.
await chatSDK.endChat();
Get Pre-Chat Survey
It gets the Pre-Chat Survey from Live Chat Config. Pre-Chat Survey is in Adaptive Card format.
Option 1
const preChatSurvey = await getPreChatSurvey();
Option 2
const parseToJSON = false;
const preChatSurvey = await getPreChatSurvey(parseToJSON);
Get Live Chat Config
It fetches the Live Chat Config.
const liveChatConfig = await chatSDK.getLiveChatConfig();
Get Current Live Chat Context
It gets the current live chat context information to be used to reconnect to the same conversation.
const liveChatContext = await chatSDK.getCurrentLiveChatContext();
Get Data Masking Rules
It gets the active data masking rules from Live Chat Config.
Returned type is defined as :
type MaskingRule = {
id: string;
regex: string;
}
type MaskingRules = {
rules: MaskingRule[];
}
const dataMaskingRules : MaskingRules = await chatSDK.getDataMaskingRules();
Get Chat Reconnect Context
It gets the current reconnectable chat context information to connect to a previous existing chat session.
Reconnection options
is required. See documentation
const optionalParams = {
reconnectId: '',
};
const chatReconnectContext = await chatSDK.getChatReconnectContext(optionalParams);
Get Conversation Details
It gets the details of the current conversation such as its state & when the agent joined the conversation.
const optionalParams = {
liveChatContext: {},
};
const conversationDetails = await chatSDK.getConversationDetails(optionalParams);
Get chat Token
It gets the chat token used to initiate a chat with Omnichannel messaging client.
const chatToken = await chatSDK.getChatToken();
Get Calling Token
It gets the calling token used to initiate a Voice & Video Call.
const callingToken = await chatSDK.getCallingToken();
Get Messages
It gets all the messages of the current conversation.
const messages = await chatSDK.getMessages();
Send Messages
It sends a message to Omnichannel.
import {DeliveryMode, MessageContentType, MessageType, PersonType} from '@microsoft/omnichannel-chat-sdk';
...
const displayName = "Contoso"
const message = "Sample message from customer";
const messageToSend = {
content: message
};
await chatSDK.sendMessage(messageToSend);
On New Message
It subscribes to new incoming messages of the current conversation such as system messages, client messages, agent messages, adaptive cards and attachments.
const optionalParams = {
rehydrate: true,
}
chatSDK.onNewMessage((message) => {
console.log(`[NewMessage] ${message.content}`);
console.log(message);
}, optionalParams);
On Typing Event
It subscribes to an agent typing event.
chatSDK.onTypingEvent(() => {
console.log("Agent is typing...");
})
On Agent End Session
It subscribes to an agent ending the session of the conversation.
chatSDK.onAgentEndSession(() => {
console.log("Session ended!");
});
Send Typing Event
It sends a customer typing event.
await chatSDK.sendTypingEvent();
Email Live Chat Transcript
It sends an email of the live chat transcript.
const body = {
emailAddress: 'contoso@microsoft.com',
attachmentMessage: 'Attachment Message'
};
await chatSDK.emailLiveChatTranscript(body);
Get Live Chat Transcript
It fetches the current conversation transcript data in JSON.
const optionalParams = {
liveChatContext: {},
};
await chatSDK.getLiveChatTranscript(optionalParams);
Upload File Attachment
It sends a file attachment to the current conversation.
const fileInfo = {
name: '',
type: '',
size: '',
data: ''
};
await chatSDK.uploadFileAttachment(fileInfo);
Download File Attachment
It downloads the file attachment of the incoming message as a Blob response.
const blobResponse = await chatsdk.downloadFileAttachment(message.fileMetadata);
...
const fileReaderInstance = new FileReader();
fileReaderInstance.readAsDataURL(blobResponse);
fileReaderInstance.onload = () => {
const base64data = fileReaderInstance.result;
return <Image source={{uri: base64data}}/>
}
Create Chat Adapter
:warning: Currently supported on web only
It creates a chat adapter to use with BotFramework-WebChat.
const chatAdapter = await chatSDK.createChatAdapter();
Get Voice & Video Calling
:warning: Currently supported on web only
:warning: Please ensure voice & video call is stopped before leveraging endChat SDK method
It fetches the SDK for Escalation to Voice & Video.
try {
const VoiceVideoCallingSDK = await chatSDK.getVoiceVideoCalling();
console.log("VoiceVideoCalling loaded");
} catch (e) {
console.log(`Failed to load VoiceVideoCalling: ${e}`);
if (e.message === 'UnsupportedPlatform') {
}
if (e.message === 'FeatureDisabled') {
}
}
Get Post Chat Survey Context
It gets the participant type that should be used for the survey and both the default and bot survey details.
const postChatSurveyContext = await chatSDK.getPostChatSurveyContext();
Get Agent Availability
It gets information on whether a queue is available, and whether there are agents available in that queue, as well as queue position and average wait time. This call only supports authenticated chat.
const agentAvailability = await chatSDK.getAgentAvailability();
Common Scenarios
Pre-Chat Survey
See https://docs.microsoft.com/en-us/dynamics365/customer-service/configure-pre-chat-survey?tabs=customerserviceadmincenter on how to set up pre-conversation surveys
import * as AdaptiveCards, { Action } from "adaptivecards";
...
const preChatSurvey = await chatSDK.getPreChatSurvey();
...
const renderPreChatSurvey = () => {
const adaptiveCard = new AdaptiveCards.AdaptiveCard();
adaptiveCard.parse(preChatSurvey);
adaptiveCard.onExecuteAction = async (action: Action) => {
const preChatResponse = (action as any).data;
const optionalParams: any = {};
if (preChatResponse) {
optionalParams.preChatResponse = preChatResponse;
}
await chatSDK.startChat(optionalParams);
}
const renderedCard = adaptiveCard.render();
return <div ref={(n) => { // Returns React element
n && n.firstChild && n.removeChild(n.firstChild); // Removes duplicates fix
renderedCard && n && n.appendChild(renderedCard);
}} />
}
Post-Chat Survey
See https://docs.microsoft.com/en-us/dynamics365/customer-service/configure-post-conversation-survey?tabs=customerserviceadmincenter on how to set up post-conversation surveys
β chatSDK.getPostChatSurveyContext()
needs to be called before chatSDK.endChat()
is called
await chatSDK.startChat();
try {
const context = await chatSDK.getPostChatSurveyContext();
if (context.participantJoined) {
const link = context.participantType === "Bot" ? context.botSurveyInviteLink : context.surveyInviteLink;
const locale = context.participantType === "Bot" ? context.botFormsProLocale : context.formsProLocale;
const linkToSend = link + "&lang=" + locale;
}
} catch (ex) {
}
await chatSDK.endChat();
Reconnect to existing Chat
await chatSDK.startChat();
const liveChatContext = await chatSDK.getCurrentLiveChatContext();
cache.saveChatContext(liveChatContext);
...
...
const liveChatContext = cache.loadChatContext()
const optionalParams = {};
optionalParams.liveChatContext = liveChatContext;
await chatSDK.startChat(optionalParams);
...
const messages = await chatSDK.getMessages();
messages.reverse().forEach((message: any) => renderMessage(message));
Authenticated Chat
See https://docs.microsoft.com/en-us/dynamics365/customer-service/create-chat-auth-settings?tabs=customerserviceadmincenter#create-a-chat-authentication-setting-record on how to set up an authenticated chat
const chatSDKConfig = {
getAuthToken: async () => {
const response = await fetch("http://contosohelp.com/token");
if (response.ok) {
return await response.text();
}
else {
return null
}
}
}
const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig, chatSDKConfig);
await chatSDK.initialize();
Persistent Chat
See https://docs.microsoft.com/en-us/dynamics365/customer-service/persistent-chat on how to set up persistent chat
const chatSDKConfig = {
persistentChat: {
disable: false,
tokenUpdateTime: 21600000
},
getAuthToken: async () => {
const response = await fetch("http://contosohelp.com/token");
if (response.ok) {
return await response.text();
}
else {
return null
}
}
}
const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig, chatSDKConfig);
await chatSDK.initialize();
Chat Reconnect with Authenticated User
See https://docs.microsoft.com/en-us/dynamics365/customer-service/configure-reconnect-chat?tabs=customerserviceadmincenter#enable-reconnection-to-a-previous-chat-session on how to set up chat reconnect
const chatSDKConfig = {
chatReconnect: {
disable: false,
},
getAuthToken: async () => {
const response = await fetch("http://contosohelp.com/token");
if (response.ok) {
return await response.text();
}
else {
return null
}
}
}
const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig, chatSDKConfig);
await chatSDK.initialize();
...
const chatReconnectContext = await chatSDK.getChatReconnectContext();
if (chatReconnectContext.reconnectId) {
}
const optionalParams = {};
optionalParams.reconnectId = chatReconnectContext.reconnectId;
chatSDK.startChat(optionalParams);
chatSDK.startChat();
Chat Reconnect with Unauthenticated User
See https://docs.microsoft.com/en-us/dynamics365/customer-service/configure-reconnect-chat?tabs=customerserviceadmincenter#enable-reconnection-to-a-previous-chat-session on how to set up chat reconnect
const chatSDKConfig = {
chatReconnect: {
disable: false,
},
}
const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig, chatSDKConfig);
await chatSDK.initialize();
....
const optionalParams: any = {};
const urlParams = new URLSearchParams(window.location.search);
const reconnectId = urlParams.get('oc.reconnectid');
const params = {
reconnectId
};
const chatReconnectContext = await chatSDK.getChatReconnectContext(params);
if (chatReconnectContext.redirectURL) {
window.location.replace(chatReconnectContext.redirectURL);
}
if (chatReconnectContext.reconnectId) {
await chatSDK.startChat({
reconnectId: chatReconnectContext.reconnectId
});
} else {
await chatSDK.startChat();
}
Operating Hours
See https://docs.microsoft.com/en-us/dynamics365/customer-service/create-operating-hours?tabs=customerserviceadmincenter on how to set up operating hours
const chatConfig = await chatSDK.getLiveChatConfig();
const {LiveWSAndLiveChatEngJoin: liveWSAndLiveChatEngJoin} = liveChatConfig;
const {OutOfOperatingHours: outOfOperatingHours} = liveWSAndLiveChatEngJoin;
if (outOfOperatingHours === "True") {
} else {
await chatSDK.startChat();
}
Handling chat Disconnect on Mobile platform
On mobile platforms, where users often switch the app between foreground and background, it is important to verify if the chat is still active before allowing the user to send a message after returning from the background .
window.addEventListener("visibilitychange", async () => {
if (!document.hidden) {
const optionalParams = {
liveChatContext: {},
};
const conversationDetails = await chatSDK.getConversationDetails(optionalParams);
if (conversationDetails?.state === "WrapUp" || conversationDetails?.state === "Closed") {
}
}
});
:warning: Currently supported on web only
Minimum Requirement Checklist
import OmnichannelChatSDK from '@microsoft/omnichannel-chat-sdk';
import ReactWebChat, {createStore} from 'botframework-webchat';
const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig);
await chatSDK.initialize();
await chatSDK.startChat();
const chatAdapter = await chatSDK.createChatAdapter();
const store = createStore(
{},
sendDefaultMessagingTagsMiddleware
);
<ReactWebChat
store={store}
userID="teamsvisitor"
directLine={chatAdapter}
sendTypingIndicator={true}
/>
Escalation to Voice & Video
:warning: Currently supported on web only
See https://docs.microsoft.com/en-us/dynamics365/customer-service/call-options-visual-engagement on how to set up calling options
import OmnichannelChatSDK from '@microsoft/omnichannel-chat-sdk';
...
const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig, chatSDKConfig);
await chatSDK.initialize();
let VoiceVideoCallingSDK;
try {
VoiceVideoCallingSDK = await chatSDK.getVoiceVideoCalling();
console.log("VoiceVideoCalling loaded");
} catch (e) {
console.log(`Failed to load VoiceVideoCalling: ${e}`);
if (e.message === 'UnsupportedPlatform') {
}
if (e.message === 'FeatureDisabled') {
}
}
await chatSDK.startChat();
const chatToken: any = await chatSDK.getChatToken();
if (VoiceVideoCallingSDK) {
try {
await VoiceVideoCallingSDK.initialize({
chatToken,
selfVideoHTMLElementId: 'selfVideo',
remoteVideoHTMLElementId: 'remoteVideo',
OCClient: chatSDK.OCClient
});
} catch (e) {
console.error("Failed to initialize VoiceVideoCalling!");
}
VoiceVideoCallingSDK.onCallAdded(() => {
...
});
VoiceVideoCallingSDK.onLocalVideoStreamAdded(() => {
...
});
VoiceVideoCallingSDK.onLocalVideoStreamRemoved(() => {
...
});
VoiceVideoCallingSDK.onRemoteVideoStreamAdded(() => {
...
});
VoiceVideoCallingSDK.onRemoteVideoStreamRemoved(() => {
...
});
VoiceVideoCalling.onCallDisconnected(() => {
...
});
const isMicrophoneMuted = VoiceVideoCallingSDK.isMicrophoneMuted();
const isRemoteVideoEnabled = VoiceVideoCallingSDK.isRemoteVideoEnabled();
const isLocalVideoEnabled = VoiceVideoCallingSDK.isLocalVideoEnabled();
const acceptCallConfig = {
withVideo: true
};
await VoiceVideoCallingSDK.acceptCall(acceptCallConfig);
await VoiceVideoCallingSDK.rejectCall();
await VoiceVideoCallingSDK.stopCall();
await VoiceVideoCallingSDK.toggleMute()
await VoiceVideoCallingSDK.toggleLocalVideo()
VoiceVideoCallingSDK.close();
}
Feature Comparisons
Web
Features | | |
Chat Widget UI | Not provided | Basic chat client provided |
Data Masking | Embedded | Requires Data Masking Middleware implementation |
Send Typing indicator | Embedded | Requires sendTypingIndicator flag set to true |
PreChat Survey | Requires Adaptive Cards renderer | Requires Adaptive Cards renderer |
Display Attachments | Requires implementation | Basic interface provided & Customizable |
Incoming messages handling | IC3 protocol message data | DirectLine activity data |
React Native
Features | | | Currently not supported |
Chat Widget UI | Not provided | Basic chat client provided | X |
Data Masking | Embedded | Embedded | X |
Send Typing indicator | Embedded | Requires Implementation | X |
PreChat Survey | Requires Adaptive Cards renderer | Requires Adaptive Cards renderer | X |
Display Attachments | Requires implementation | Embedded | X |
Incoming messages handling | IC3 protocol message data | IC3 protocol message data | X |
Telemetry
Omnichannel Chat SDK collects telemetry by default to improve the featureβs capabilities, reliability, and performance over time by helping Microsoft understand usage patterns, plan new features, and troubleshoot and fix problem areas.
Some of the data being collected are the following:
Organization Id | e00e67ee-a60e-4b49-b28c-9d279bf42547 |
Organization Url | org60082947.crm.oc.crmlivetie.com |
Widget Id | 1893e4ae-2859-4ac4-9cf5-97cffbb9c01b |
Browser Name | Edge |
Os Name | Windows |
Anonymized IP Address (last octet redacted) | 19.207.000.000 |
If your organization is concerned about the data collected by the Chat SDK, you have the option to turn off automatic data collection by adding a flag in the ChatSDKConfig
.
const omnichannelConfig = {
orgUrl: "",
orgId: "",
widgetId: ""
};
const chatSDKConfig = {
telemetry: {
disable: true
}
};
const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig, chatSDKConfig);
await chatSDK.initialize();
Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct.
For more information see the Code of Conduct FAQ or
contact opencode@microsoft.com with any additional questions or comments.