Skip to main content

Notifications

Community site session details

Community site session details

Session Id : MTIizryttWPxtvmL1ieg2l
Copilot Studio - General
Unanswered

Agent offs to Omnichannel for Customer Service via Direct Line

Like (0) ShareShare
ReportReport
Posted on 28 Dec 2023 19:55:25 by

Hello - 

I'm having an issue connection my Copilot Studio bot to Omnichannel for Customer Service (Agent Handoffs)

 

Context: 

I've built a chat bot using copilot studio and integrated it into my web application. I had to use Direct Line and custom canvas for this bot because:

  1. I wanted to total customize the look and feel of the bot to match the application styles

  2. I wanted to initialize the chat (and have the bot message first) when the user clicked a button I created on the page. 

  3. I also did this because I needed to pass a token from my application to the chat bot on initialization - this token is taken in by the        bot and a power automate flow is called that decodes the token and stores a bunch of variables in global context (most notably          an auth token for my api that the bot can then use to make calls to my api and respond to the client with client specific.     information)

 

Here's a snippet of the working code to set that up:

 

```

useEffect(() => {
const getFromLocalStorage = async () => {
const channel = await AsyncStorage.getItem(DIRECT_CHANNEL_STORAGE_KEY);
if (channel) {
setDirectLineChannel(JSON.parse(channel));
setIsChatWindowOpen(true);
}
};
getFromLocalStorage();

return () => AsyncStorage.removeItem(DIRECT_CHANNEL_STORAGE_KEY);
}, []);

useEffect(() => {
if (!isChatWindowOpen) return;
const styleSet = window.WebChat.createStyleSet(webChatStyleSet);

const handleDirectLineToken = async () => {
const now = Date.now();
if (isEmpty(directLineChannel)
|| loanNumber !== directLineChannel.id
|| now > directLineChannel.expiration
) {
try {
const res = await fetch(process.env.PVA_TOKEN_ENDPOINT);
const result = await res.json();
const newDirectLineChannel = {
expiration: now + result.expires_in * 1000,
id: loanNumber,
token: result.token,
};
setDirectLineChannel(newDirectLineChannel);
await AsyncStorage.setItem(
DIRECT_CHANNEL_STORAGE_KEY, JSON.stringify(newDirectLineChannel)
);
} catch (err) {
setDirectLineChannel({});
await AsyncStorage.removeItem(DIRECT_CHANNEL_STORAGE_KEY);
}
}
};

const store = window.WebChat.createStore({},
({ dispatch }) => (next) => (action) => {
const { type } = action;
if (type === 'DIRECT_LINE/CONNECT_FULFILLED') {
new Promise((resolve) => {
dispatch({
payload: {
name: 'pvaSetContext',
value: { token: pvaToken },
},
type: 'WEB_CHAT/SEND_EVENT',
});
setTimeout(() => resolve(1), 1000);
}).then(() => {
dispatch({
meta: { method: 'keyboard' },
payload: {
activity: {
channelData: { postBack: true },
name: 'startConversation',
type: 'event',
},
},
type: 'DIRECT_LINE/POST_ACTIVITY',
});
});
}
return next(action);
});

const renderChat = async () => {
if (prevLoanNumber === loanNumber && isChatConnected) return;
try {
await handleDirectLineToken();
if (!isEmpty(directLineChannel)) {
const { token } = directLineChannel;
window.WebChat.renderWebChat(
{
cardActionMiddleware: () => (next) => async ({ cardAction }) => {
const { type, value } = cardAction;
if (type === 'openUrl') {
const { pathname } = new URL(value);
return handleNav(pathname);
}
return next({ cardAction });
},
directLine: window.WebChat.createDirectLine({ token }),
store,
styleOptions,
styleSet,
},
document.getElementById('webchat')
);
if (prevLoanNumber !== loanNumber) {
setIsChatConnected(false);
setShouldShowSkeleton(true);
setShouldShowCloseConfirmationScreen(false);
} else {
setIsChatConnected(true);
dispatchPVAChatInitiated();
}
}
} catch {
await handleDisconnectChat();
}
};

renderChat();
}, [
directLineChannel,
dispatchPVAChatInitiated,
handleNav,
isChatConnected,
isChatWindowOpen,
loanNumber,
prevLoanNumber,
pvaToken,
styleOptions,
webChatStyleSet,
]);

useEffect(() => {
if (shouldShowSkeleton) {
setTimeout(() => {
setShouldShowSkeleton(false);
}, 2500);
}
}, [shouldShowSkeleton]);

const handleDisconnectChat = async () => {
setIsChatWindowOpen(false);
setShouldShowCloseConfirmationScreen(false);
setIsChatConnected(false);
setDirectLineChannel({});
await AsyncStorage.removeItem(DIRECT_CHANNEL_STORAGE_KEY);
};

const handleInitiateChat = () => {
setShouldShowSkeleton(true);
setIsChatWindowOpen(true);
};
```

I have the app with the chatbot deployed and works great.

 

Now, I want to implement Agent Handoffs if an escalation topic is hit. And this is where I'm having the issue. I'm able to connect Omnichannel for Customer Service to that chat bot(as far I can tell) and I'm given an embed script.

 - If i embed that script in my code I'm able to start a chat and connect to a live agent in the Omnichannel dashboard, however, the     problem is the bot will not function without being provided the token that it needs. Also, none of the styling I have setup will be present. (using this script is not a viable solution)

  - If I use my bot as is and initiate an agent handoff, it never connects to Omnichannel (it just hangs on the "transferring you to a live agent..." text. 

 

So, my question is how can I make this work with my current implementation? Clearly, there must be something that I'm missing. I'm able to intercept the handoff activity from the bot (`handoff.initiate`) but I don't know what to do with it once I have. 

 

Thank you in advance.

 

-Maurice 

  • HenryJammes Profile Picture
    on 03 Jan 2024 at 13:36:19
    Re: Agent offs to Omnichannel for Customer Service via Direct Line

    Hi @baezm1 

     

    I'm not sure this can work, at least natively. When using the native integration between Copilot Studio and Dynamics 365 Omnichannel for Customer Service, it's using a specific channel, omnichannel, not Direct Line.

    To handle the hand off, Dynamics 365 Omnichannel offers its own chat widget so that omnichannel is that the front and Copilot Studio at the back (and this is how a live agent can take over a conversation).

     

    There are multiple resources you could use to customize the widget to your needs as well as passing additional context variables to your bot:

     

    Henry

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Understanding Microsoft Agents - Introductory Session

Confused about how agents work across the Microsoft ecosystem? Register today!

Warren Belz – Community Spotlight

We are honored to recognize Warren Belz as our May 2025 Community…

Congratulations to the April Top 10 Community Stars!

Thanks for all your good work in the Community!

Leaderboard > Copilot Studio - General

#1
Ekta Gupta Profile Picture

Ekta Gupta 4

#2
ricardodesouza Profile Picture

ricardodesouza 2

#2
L-1234567-0 Profile Picture

L-1234567-0 2

Overall leaderboard
Loading started