The Amazon Connect Contact Control Panel (CCP) & WebAgent Navigator

The Amazon Connect Streams API (Streams) gives you the power to integrate your existing web applications with Amazon Connect. Streams gives you the power to embed the Contact Control Panel (CCP) UI components into your page, and/or handle agent and contact state events directly giving you the power to control agent and contact state through an object oriented event driven interface. You can use the built in interface or build your own from scratch: Streams gives you the power to choose.

Amazon Connect Streams Documentation

Amazon Connect CCP in WebAgent Navigator

WebAgent Navigator takes tight control of the softphone it has embedded in the application. WebAgent directly controls the phone functions from the call scripts and can dial, pause, hold, mute, and transfer calls. In the transfer process, WebAgent can place the original call on hold, dial a 2nd call, conference the calls and either cancel the transfer (drop the 3rd party call) or complete the transfer (drop the agent from the call). The goal is that the agent uses a common interface and doesn’t have to directly interface with the softphone.

Of course, this depends largely on the functionality of the softphone software being used. With both Avaya OneX and Amazon Connect Streams, I was not able to control the transfer functions from the WebAgent script. Here I’ll talk about some of the issues and work-arounds I discovered with the Amazon CCP.

First, a little background on the WebAgent phone… WebAgent uses a controlling phone script that has all of the methods or functions needed. When WebAgent wants to dial an outbound call, for example, the script simply executes “Phone.Dial(‘number’). The phone script then calls the “dial” function of the currently loaded plug-in. The plug-in is specific to the softphone being used, in this case, Amazon CCP. The plugin then communicates with Amazon CCP through the Amazon Connect Streams API instructing Amazon CCP to dial.

Logging In

When an agent logs in to WebAgent Navigator, he or she chooses an environment that they are logging in to. The environment determines what phone they are using, what queues they connect to and what calls can be routed to them. When WebAgent loads, the environment determines what softphone plug-in is loaded. When the plugin for Amazon Connect loads, it loads the Amazon CCP in an iframe with this bit of code…

        connect.core.initCCP(target, {
            ccpUrl: ccpUrl,
            loginPopup: true,
            softphone: {
                allowFramedSoftphone: true
            }
        });

Amazon Connect CCP Login

This will trigger the CCP to open a new window or tab with the CCP login screen… and this is the first draw-back I ran into. WebAgent already knows the user and password to log in to the CCP. In fact, WebAgent may have created the user in Amazon Connect in the first place. However, there is no getting around this login screen. Amazon CCP requires this manual log in. So the next best thing I can do is to automatically close the popup window once the agent has logged in to the CCP.

The Amazon Streams API has callback methods for the agent (connect.agent) and for contacts (connect.contact). In the WebAgent initialization code, I add both callbacks and within the agent callback, I have code to close the popup window…

        connect.agent(function(agent) {
            // close the ccp login window when agent is available
            var w = window.open('', connect.MasterTopics.LOGIN_POPUP);
            if (w) {w.close();}
            // catch agent events
            agent.onRefresh(function(a){return awsConnect.onAgentRefresh(a);});
            agent.onStateChange(function(s){return awsConnect.onAgentState(s);});
            agent.onRoutable(function(a){return awsConnect.onAgentRoutable(a);});
            agent.onNotRoutable(function(a){return awsConnect.onAgentNotRoutable(a);});
            agent.onOffline(function(a){return awsConnect.onAgentOffline(a);});
            agent.onAfterCallWork(function(a){return awsConnect.onAgentACW(a);});
        });
        connect.contact(function(contact) {
            awsConnect.Contact = contact;
            // catch contact events
            contact.onRefresh(function(call){return awsConnect.onCallRefresh(call)});
            contact.onIncoming(function(call){return awsConnect.onCallIncoming(call)});
            contact.onAccepted(function(call){return awsConnect.onCallAccept(call)});
            contact.onConnected(function(call){return awsConnect.onCallConnect(call)});
            contact.onEnded(function(call){return awsConnect.onCallEnded(call)});
        });

The connect.agent() method is called once the agent has successfully logged in to the CCP. The first action is to open a window using the window name programmed in the Amazon Streams code (connect.MasterTopics.LOGIN_POPUP). Since the window is already opened, it won’t open a new window but will return the handle of the existing window - which is then used to close the window. This makes for a more seamless login process to the agent… once they click the “login” button in the CCP, the Streams API calls the “connect.agent” method (above) which then closes the window automatically and the agent is returned to the WebAgent window.

The rest of the code shown above maps my methods (coded in the “awsConnect” object) to the callback methods available from the Amazon Streams API, allowing me to catch events triggered by the Amazon Streams API.

While I’m on logging in, there’s one bit of code I discovered and added to catch the possibility of the agent logging out of the Amazon CCP…

        var eventBus = connect.core.getEventBus();
        eventBus.subscribe(connect.EventType.TERMINATED, function () {
            // catch the agent logging out of ccp
            awsConnect.onAgentLogout();
        });
 

This captures a logout event from the Steams API and calls my onAgentLogout() method where I force a logout from WebAgent Navigator. Of course, it would be a bit easier if the Amazon Streams API simply had a “agent.onLogout” method, but, at least as of today, it does not.

The connect.contact() method is called whenever a new contact (call) is initiated. Although I capture the onIncoming() and onAccept() methods, I currently don’t do anything with them. The onConnected() method tells me when a new call is connected and the onEnded() method tells me when the call is disconnected. These are pretty strait forward. The onRefresh() method may be called numerous times during a call. Through the onRefresh() method, I can determine if a call was put on/taken off hold, or if a third party call (transfer) was connected or dropped.

Pausing

Paused for After Call Work

When an agent is logged in to WebAgent and the Amazon CCP but not accepting calls, they are said to be paused. There are a number of reasons an agent may be paused; most often, it’s “After Call Work” when a call has been terminated but the agent is wrapping up work on that call (completing the script, taking a sip of water, etc.). After Call Work may be set automatically through the Amazon Connect console for a limited period of time so that when a call disconnects, the agent automatically “pauses” for After Call Work for, say, 10 seconds, giving the agent time to complete the script and be ready for the next call.

Other reasons an agent may be paused are for a break, for a meal, for training, or for supervision. The reasons may be specific to the client/organization and can be programmed in WebAgent and Amazon Connect. The point is that the agent can pause from the Amazon CCP or from WebAgent and in either case, both systems must be aware of the pause and the reason.

In Amazon CCP parlance, an agent is either “routable” (accepting calls) or “not routable” (not accepting calls), “not routable” would be paused. In the code above, there are 3 methods I capture for pausing… onStateChange(), onRoutable() and onNotRoutable(). onStateChange() is called often and includes many states we’re not really interested in. onRoutable() is called whenever the agent state changes from “not routable” to “routable” and onNotRoutable() is called for the reverse. However, the agent may be in a “not routable” state and change to a different “not routable” state/reason. For example, an agent may go on break after completing a call thus changing their state reason from “After Call Work” to “Break” This is why I capture the onStateChange() method in which I check the current state to the new state and “re-pause” if necessary. That way both Amazon CCP and WebAgent have the same status.

Note 1. There can be only 1 routable state.
Note 2. Although I capture the event, I do nothing in the onAfterCallWork() method; I handle that in the onNotRoutable() method.

Lastly, there is an “offline” state in Amazon CCP. This occurs when the agent clicks the “Offline” button in the CCP window. In my point of view, offline is the same as logged out, not paused, so I capture that event (onOffline()) and log the agent out of both the CCP and WebAgent.

Transfers

Transfer screen (call on hold)

One valuable feature is WebAgent is that when a call is transferred internally, the state of the call, including any information collected, and the point in the call script are also transferred. Thus the 2nd agent receiving the transfer can pick up the call at the point the first agent left off.

And here’s where things get more complicated. Ideally, the agent should be able to initiate a transfer from a button or action within the WebAgent script. The agent shouldn’t have to enter, or even know, the phone number to dial. With an external transfer, this can easily be done; the WebAgent script just initiates a dial command passing the phone number and the phone script dials using this bit of code..

        var endpoint = connect.Endpoint.byPhoneNumber(vcall.Number.dial, vcall.Number.cname || null);
        Calls.Active.Object.addConnection(endpoint,{});

This creates an endpoint used by Amazon Streams and creates a new connection to the active call using the phone number passed and the caller id name (if present). This initiates a 3rd party call. [Note: Calls.Active.Object is where I store the call (or contact) object passed from Amazon Streams when the call was initiated.]

But what if the transfer is internal… to a queue or a specific agent? There is no apparent way to do this other than to use Quick Connects built into the Amazon CCP. You set up a Quick Connect in the Amazon Connect console pointing the call destination to a) an agent, b) a queue, or c) an outside number. Then the agent can access the quick connect through the CCP. However, this takes any control away from WebAgent and the agent must know which Quick Connect to use for any specific transfer.

In WebAgent, I can get a list of all of the Quick Connects (and all of the queues) but there is no way to automatically/programmatically dial a Quick Connect (or queue). I’ve tried, to no avail, variations of the endpoint code, looking through the Amazon Streams code at byPhoneNumber(), trying to create my own byQueue or byQuickConnect code. So the best I can do is to put instructions in the WebAgent script telling the agent something like “Go to the Amazon Connect CCP window and click on the Transfer button then click the Xyzzy Quick Connect” rather than adding a button that would do it for them.

Through a lot of debugging, I was able to determine when an agent uses a Quick Connect or when an agent dials through the CCP window. I watch the onRefresh() callback and look at getSingleActiveThirdPartyConnection(); if I have a new connection, the agent initiated a 3rd party call; if the phone number for that call is “INTERNAL-TRANSFER”, it’s a Quick Connect to an agent or a queue.

I would also like to give the agent buttons within the WebAgent script to conference the calls (3 way call) or drop the 3rd party call or to drop out of the 3 way call without having to go to the Amazon CCP window, but, again, there is no way to do this and the agent must manage the transfer process through the CCP window. However, through the onEnded() and onRefresh() methods, I can detect when the agent drops the 3rd party (cancelling the transfer) or drops out of the 3 way call (completing the transfer) so I know when a call has or hasn’t been transferred. I do not know if the agent ever conferenced the calls or if the agent did a cold transfer.

Data Collection

WebAgent Navigator collects a lot of data for reporting and analysis. As far as phone interaction, every call (inbound or outbound), every transfer, every hold, and every pause is tracked in WebAgent and passed to QueueMetrics and Google Analytics. The good news is that all of this works. The bad news is that it doesn’t work quite as seamlessly (for the agent) as I would like. Being able to programmatically log in to the Amazon CCP and to dial a Quick Connect would be real wins for me and for the agents using WebAgent Navigator.

 
image.png

I certainly didn’t cover the details of programming the Amazon Connect Contact Control Panel, nor did I intend to (perhaps I will in the future). I did cover a few of the problems I ran into and a few undocumented tricks I discovered. If you’re working with Amazon Connect and have any questions or insights, feel free to comment below or contact me directly.