First time re:Invent attendee. First impressions.

The conferences I typically attend are Cisco Live (CLUS) and Enterprise Connect (EC). Occasionally, I manage to get into CCW if the dates overlap, but CLUS and EC are my primary focus, and I’ve probably attended them for over 10 years.

This year, as a company, we decided it made sense to check out AWS re:Invent. Our AWS practice has become a significant part of our business—especially around Amazon Connect—so this seemed like the perfect time. Below are some takeaways and things I’d love to see improved.

The Good:

  1. The Scale of the Event:

    • The sheer size of re:Invent was staggering. The largest conference I’ve attended previously had around 30,000 attendees, but this one was estimated to have 60,000. That’s insane! Yet, everything was well-organized—there was always someone nearby to guide you, and while it felt overwhelming, it was controlled chaos.
  2. Session Content:

    • Overall, the session content was pretty good. I had some gripes about certain topics, but the workshops (labs) ran incredibly smoothly. It was fantastic to spin up a fresh AWS account, have prebuilt resources, and dive into the learning tasks. I absolutely loved working through the labs.
  3. Networking:

    • The networking opportunities were excellent. I got to reconnect in person and meet many new, interesting people—customers, AWS partners, and AWS employees alike. The amount of engagement opportunities was impressive.
  4. The Weather:

    • While I didn’t spend much time outdoors, the weather was perfect. Unlike previous visits, I didn’t have to go back to my room for a midday shower.

The Bad:

  1. Vegas:

    • Need I say more?
  2. Finding Sessions:

    • Searching for sessions was frustrating. For example, looking up “Amazon Connect” workshops in the catalog led to unrelated results like a Direct Connect workshop as the first hit, with an actual Amazon Connect workshop showing up fifth. Surely Q can help improve this?
  3. Session Locations:

    • Sessions were scattered across multiple hotels, making it nearly impossible to attend all the ones I wanted. On Monday, there were four non-overlapping sessions I wanted to attend, but they were spread across three hotels with a maximum of 30 minutes between them. Even at top speed, attending three was a stretch. While replays help, not all sessions are recorded.
  4. Workshop Access:

    • Considering the logistical challenges, I wish workshops posted their URLs so attendees could complete them later. Even better would be a centralized way to find these directly on https://workshops.aws.
  5. Meals:

    • The mobile app didn’t provide menu information ahead of time, making it difficult to decide whether to grab a boxed lunch. It would also have been great to have boxed lunch stations outside the main meal areas to save time when rushing between hotels.
  6. Conference Cost:

    • Attending re:Invent is pricey. All-in, you’re looking at around $5,000 at least, though you might be able to cut it down to $3,500 if you’re frugal. Still, the tickets alone are the most expensive I’ve seen for a conference.
  7. December Timing:

    • While the weather was nice, December is a tough time for conferences. Many attendees have just come off Thanksgiving travel, head to this event, and then face more travel in 1–2 weeks for the holidays.

The Mobile App:

  1. Session Search:

    • The app was great at finding sessions but struggled with everything else. For example, while Amazon Q did an okay job recommending sessions, it couldn’t tell me where to pick up my badge at the airport or what was being served for lunch each day.
  2. Performance:

    • The app was slow to load. Despite having a phone that’s less than a year old, it felt like the app was loading for the first time every time I opened it. You’d think AWS could prioritize app traffic on their Wi-Fi or use a local CDN.

    Final Thoughts:

    Overall, I’d attend AWS re:Invent again, but I’m not sure it would be a yearly event for me. The cost and scheduling challenges make it a less-than-ideal fit for an annual commitment.

    ~david

    Basics of Amazon Connect Views and Routing Transfers with Agent Proficiencies

    This post is going to cover a few disparate things as well as present a customer scenario we were trying to solve for. The post will focus on the specifics I struggled with in hopes it helps others. The highlights of what is covered in this post are:

    • Displaying an attribute in the Agent Workspace with a view.
    • Building a form view and passing data between two views.
      • Getting your view data into a flow and updating contact attributes based on view form input.
    • How to route queue transfers when working with proficiencies.

    The use case: Customer has agents across the United States and calls are handled based on two things: caller’s time zone and the caller’s state. The caller chooses their time zone and state in the IVR and we then queue the call to the time zone that is chosen and set a proficiency of state based on the caller’s second selection. Proficiencies allow us to not have to create a queue for every permutation of time zone and state.

    What happens if the caller chose the wrong state in the IVR? One solution is to ask the agent or the caller to go through the IVR again and choose the right destination, however experience wise this is just not ideal and there’s a lot of room for errors. Another possible solution is to create 50 queues, one per state and configure 50 quick connects. However, think about the impact this will have on your routing profiles. We’re not talking just 50 routing profiles as some agents could handle multiple states. We’re talking potentially as many routing profiles as you have agents. This is just not scalable and a management nightmare. One more option is to create a custom CCP. Now this is probably the most elegant solution, but also the one with the most amount of overhead and if your customer doesn’t already have a custom CCP it opens a whole new can of worms you will want to avoid.

    I had a feeling that views would help here, but I had only done half of an AWS workshop about them in the past and felt like the presentation was too advanced and I was mostly lost. I also posted in LinkedIn in hopes I was missing something with how proficiencies worked and got some good ideas. Here’s how it was solved using views/step by step guides.

    First let’s look at the queues:

    Screenshot 2024-08-28 at 9.12.55 AM

    Second, let’s look at the inbound flow that sets the queue. You get the time zone and set it a few attributes and use one of the attributes to set the working queue (shown above).

    Screenshot 2024-08-28 at 9.12.26 AM

    Third, let’s get the state and set the routing criteria where we’ll set State >= 1 for the type of agent we want the call to go to. You can see my State attribute, remember this as it’s important.

    Screenshot 2024-08-28 at 9.14.46 AM

    Now, let’s look at what an agent will look like to get a specific type of call. We need to set the state or states they are proficient in.

    Screenshot 2024-08-28 at 10.45.43 AM

    So far we’ve not done anything new. We’ve selected the queue and routing criteria we configured a routing profile (not shown) and set our agent proficiencies. This will get a call to the Pacific Time Zone queue for an agent that can handle California (CA) calls.

    Views

    The issue we have now is how do we let the agent transfer the call to another state (e.g. Oregon (OR)? First, let’s build a view to set the transfer destination.

    Screenshot 2024-08-28 at 10.49.27 AM

    This view has the pieces that are important.

    • Attribute Bar: Will dynamically show the state that was selected in the IVR.
    • Dropdown: Holds every possible state an agent could transfer to and has a name of StateDropdown.
    • Button: Click action triggers the event flow to move forward and update attributes and sends the agent to a confirmation view.

    The confirmation view:

    Screenshot 2024-08-28 at 10.54.18 AM

    This view has a single text box where the contents will be dynamically set.

    Event Flow

    We have to two views we need to allow the agent to specify where the call needs to be transferred to. Now we need to make a few updates. The first thing is to create a new flow which will serve as the event flow when the call arrives to the agent and will render the views in the agent’s workspace.

    Screenshot 2024-08-28 at 10.57.37 AM

    The event flow is pretty simple. Make sure you enable logging so you can see what’s going on in CloudWatch. Next you choose your Set Transfer view and in the input pass the JSON string below to set the attribute bar to show the IVR selected state. Remember, you want this attribute set on related contacts to make sure it’s available on the inbound contact. This is very important.

    {“Attributes”:[{“Value”:”$.Attributes.State”}]}

    After the agent sets the transfer destination we want to set a new contact attribute called AgentTransfer which will hold the new State. Next, we’ll show the confirmation view letting the agent know the transfer can happen now and we’ll show the agent what state they selected in the drop down. In the show view blow you’ll pass the JSON string below.

    {“Attributes”:[{“Value”:”You call is ready to be transferred to $.Views.ViewResultData.StateDropdown.0″}]}

    Now in the inbound flow we need to set an event flow to trigger the view workflow.

    Screenshot 2024-08-28 at 11.17.15 AM

    Now to test the inbound side. Below is my agent workspace receiving a call for Pacific Time Zone queue for State = CA.

    Screenshot 2024-08-28 at 11.23.58 AM

    My agent now wants to transfer the call to Oregon and uses the drop down.

    Screenshot 2024-08-28 at 11.29.04 AM

    Agent sees a confirmation that the call is ready to be transferred.

    Screenshot 2024-08-28 at 11.27.58 AM

    At that point the agent can use the appropriate quick connect to transfer the call.

    Screenshot 2024-08-28 at 11.51.34 AM

    One last piece, in the transfer flow we need to update the routing criteria to go to the new state. We can easily do that by checking that we have a value in AgentTransfer attribute.

    Screenshot 2024-08-28 at 11.53.50 AM

    And updating the routing criteria dynamically base on this same attribute.

    Screenshot 2024-08-28 at 11.54.09 AM

    Your call is now queued for an agent who handles OR calls.

    I hope this was helpful, I spent a few hours trying to wrap my head around passing data to the view and getting data out of the view to update attributes. This was the biggest learning from all this and solved the use case without having to create a custom CCP or build any extra queues or routing profiles.

    ~david

    Troubleshooting UCCX Outbound Dialer Agent Based Preview Campaigns

    It had been years, maybe 10 years, since I had done dialer campaigns on Cisco Contact Center Express (UCCX). I recently got a chance to do a deep dive to some SFDC integration I did and I wanted to capture how to track a call via the UCCX logs. What we will be coving is an UCCX outbound campaign that is agent based and preview. Next you need a CSV or use the API to load a new call list. Login the agent to Finesse and a preview call will be presented to the agent. If the agent accepts the preview and the customer is then dialed out. Otherwise the agent could end the call or schedule a callback if it was accepted. The steps below walk you through every single step.

    Trace levels you should have set in UCCX Serviceability:
    SS_OB: Debugging, XDebugging1, XDebugging2
    SS_RM: Debugging, XDebugging1
    SS_RMCM: Debugging

    You’re going to need RTMT and Notepad++.

    • Start with getting the ANI of the record that you were going to dial and downloading the MIVR and JTAPI logs.
    • Using RTMT download the Cisco Unified CCX Engine and Cisco Unified CCX JTAPI Client logs from your server for the time frame needed.
    • Go to the logs downloaded MIVR directory and unzip all files and leave in place.
    • Open Notepad++ Find in Files, choose the location C:\YourServer\YourTimeStamp\uccx\log and search for the ANI sent to the dialer.

    Notepad++ Search Settings

    Notepad++ Search Settings

    • Start with any hits in your JTAPI folder. This will tell you the calls placed to your ANI and correlate it to an agent extension. The below example show 3 total calls made to that ANI by an agent. This is across 3 different JTAPI logs.
      CiscoJtapi041.log

    17467378: Sep 07 13:53:41.861 CST %JTAPI-JTAPI-7-UNK:(P2-ccxrmcm)[pool-26-thread-1193][(P2-ccxrmcm) GCID=(1,15759614)->IDLE]Request: connect (CSFdavidmacias, 155551, 912145551234featurePriority=1)17467379: Sep 07 13:53:41.861 CST %JTAPI-PROTOCOL-7-UNK:(P2-1.1.1.30) [pool-26-thread-1193] sending: com.cisco.cti.protocol.LineCallInitiateRequest {sequenceNumber = 41195lineCallManagerID = 4lineID = 35553globalCallManagerID = 1globalCallID = 15759614callingAddress = 155551destAddress = 912145551234userData = nullmediaDeviceName = mediaResourceId = 0featurePriority = 1}

    CiscoJtapi043.log

    17485516: Sep 07 14:00:54.648 CST %JTAPI-JTAPI-7-UNK:(P2-ccxrmcm)[pool-26-thread-1196][(P2-ccxrmcm) GCID=(1,15759713)->IDLE]Request: connect (CSFdavidmacias, 155551, 912145551234featurePriority=1)17485517: Sep 07 14:00:54.648 CST %JTAPI-PROTOCOL-7-UNK:(P2-1.1.1.30) [pool-26-thread-1196] sending: com.cisco.cti.protocol.LineCallInitiateRequest {sequenceNumber = 41214lineCallManagerID = 4lineID = 35553globalCallManagerID = 1globalCallID = 15759713callingAddress = 155551destAddress = 912145551234userData = nullmediaDeviceName = mediaResourceId = 0featurePriority = 1}

    CiscoJtapi046.log

    17516022: Sep 07 14:15:45.225 CST %JTAPI-JTAPI-7-UNK:(P2-ccxrmcm)[pool-26-thread-1200][(P2-ccxrmcm) GCID=(1,15759923)->IDLE]Request: connect (CSFdavidmacias, 155551, 912145551234featurePriority=1)17516023: Sep 07 14:15:45.225 CST %JTAPI-PROTOCOL-7-UNK:(P2-1.1.1.30) [pool-26-thread-1200] sending: com.cisco.cti.protocol.LineCallInitiateRequest {sequenceNumber = 41258lineCallManagerID = 4lineID = 35553globalCallManagerID = 1globalCallID = 15759923callingAddress = 155551destAddress = 912145551234userData = nullmediaDeviceName = mediaResourceId = 0featurePriority = 1}

    • Next we need to see what the engine sees for that ANI on the same search you did before open the earliest MIVR log that shows the ANI in question. You need to find the record id for your specific call, this record id is how the outbound subsystem tracks the call. This means that while you start the search with your ANI, subsequent searching will be based on this record id. Below are two examples of how to locate the record id: 15541. Additionally, you see what was imported into UCCX. You can now validate that the data received by UCCX was the data you sent.

    125439796: Sep 07 13:53:16.034 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_ReadContactsThread-750-0-ReadContactsThread] com.cisco.config.impl.ConfigManagerImpl ConfigManagerImpl-getAll():CIR[0]=ConfigImportRecord[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,implClass=class com.cisco.crs.outbound.DialingListConfig,desc=,values=[15541, 30, 5008DM, Other.wav, Agent, 912145551234, , , 92, true, -1, true, -1, true, , 2024-03-05 19:53:13.0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, 0, null, null, null],evalues=null]125439797: Sep 07 13:53:16.034 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_ReadContactsThread-750-0-ReadContactsThread] com.cisco.config.impl.ConfigManagerImpl ConfigManagerImpl-getAll():configObjs[0]=DialingListConfig[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,desc=,recordID=0,dialingListID=15541,campaignID=30,accountNumber=5008DM,firstName=Other.wav,lastName=Agent,phone01=912145551234,phone02=,phone03=,gmtZonePhone01=92,dstPhone01=true,gmtZonePhone02=-1,dstPhone02=true,gmtZonePhone03=-1,dstPhone03=true,callbackNumber=,callbackDateTime=2024-03-05 19:53:13.0,callStatus=1,callResult=0,callResult01=0,callResult02=0,callResult03=0,lastNumberDialed=0,callsMadeToPhone01=0,callsMadeToPhone02=0,callsMadeToPhone03=0,numMissedCallback=0,retry=false,callsMadeToCallbackNum=0]

    • Next we want to find out who all was offered this call. Using Notepad++ Find in Files, same location, using a regular expression with your record id: associateContactToReservedAgent.*dlc:15541.

    Notepad++ Search Settings Regular Expression

    Notepad++ Search Settings Regular Expression

    • This will give you who the call was sent to and at what time. In my case you can match these 3 calls to the JTAPI calls above.

    125443434: Sep 07 13:53:39.146 CST %MIVR-SS_OB-7-UNK: [MIVR_SS_OB_OutboundMgrMsgProcessor-192-0-OutboundMgrMsgProcessor] com.cisco.wf.subsystems.outbound.PreviewDialer PreviewDialer:associateContactToReservedAgent() rsrc:Rsrc Name:David Macias ID:dmacias IAQ Extn:155551 for contact:OutboundContactInfo: dlc:15541 (phoneNumber:912145551234 unformattedPhoneNumber:912145551234 timezone -360 callStartTime 0 answeringMachine false )

    125505817: Sep 07 14:00:49.286 CST %MIVR-SS_OB-7-UNK: [MIVR_SS_OB_OutboundMgrMsgProcessor-192-0-OutboundMgrMsgProcessor] com.cisco.wf.subsystems.outbound.PreviewDialer PreviewDialer:associateContactToReservedAgent() rsrc:Rsrc Name:David Macias ID:dmacias IAQ Extn:155551 for contact:OutboundContactInfo: dlc:15541 (phoneNumber:912145551234 unformattedPhoneNumber:912145551234 timezone -360 callStartTime 0 answeringMachine false )

    125632982: Sep 07 14:15:39.532 CST %MIVR-SS_OB-7-UNK: [MIVR_SS_OB_OutboundMgrMsgProcessor-192-0-OutboundMgrMsgProcessor] com.cisco.wf.subsystems.outbound.PreviewDialer PreviewDialer:associateContactToReservedAgent() rsrc:Rsrc Name:David Macias ID:dmacias IAQ Extn:155551 for contact:OutboundContactInfo: dlc:15541 (phoneNumber:912145551234 unformattedPhoneNumber:912145551234 timezone -360 callStartTime 0 answeringMachine false )

    As you saw in the JTAPI logs and above, the same record was called multiple times. One likely reason is that an agent scheduled a callback, but how do we confirm this? This part is not as clean as I would like, but the following process should give you a decent idea if a callback was rescheduled or not.

    #This is the original record being set. Notice the callbackDateTime.
    125445090: Sep 07 13:53:43.726 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_SaveContactsMsgProcessor-198-0-SaveContactsMsgProcessor] com.cisco.config.impl.ConfigStubImpl configStubImpl-replace() exporting object: DialingListConfig[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,desc=,recordID=0,dialingListID=15541,campaignID=30,accountNumber=5008DM,firstName=Other.wav,lastName=Agent,phone01=912145551234,phone02=,phone03=,gmtZonePhone01=92,dstPhone01=true,gmtZonePhone02=-1,dstPhone02=true,gmtZonePhone03=-1,dstPhone03=true,callbackNumber=,callbackDateTime=2024-03-05 19:53:13.0,callStatus=3,callResult=1,callResult01=1,callResult02=0,callResult03=0,lastNumberDialed=1,callsMadeToPhone01=1,callsMadeToPhone02=0,callsMadeToPhone03=0,numMissedCallback=0,retry=false,callsMadeToCallbackNum=0]
    #Notice that the callbackDateTime is now set to 20:15:33. One thing to note is that while the time stamp says the server’s time zone it’s actually UTC and the callback will happen at 20:15:33 UTC.
    125451126: Sep 07 13:54:33.558 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_SaveContactsMsgProcessor-198-0-SaveContactsMsgProcessor] com.cisco.config.impl.ConfigStubImpl configStubImpl-replace() exporting object: DialingListConfig[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,desc=,recordID=0,dialingListID=15541,campaignID=30,accountNumber=5008DM,firstName=Other.wav,lastName=Agent,phone01=912145551234,phone02=,phone03=,gmtZonePhone01=92,dstPhone01=true,gmtZonePhone02=-1,dstPhone02=true,gmtZonePhone03=-1,dstPhone03=true,callbackNumber=,callbackDateTime=Tue Mar 05 20:15:33 CST 2024,callStatus=4,callResult=8,callResult01=8,callResult02=0,callResult03=0,lastNumberDialed=1,callsMadeToPhone01=1,callsMadeToPhone02=0,callsMadeToPhone03=0,numMissedCallback=0,retry=false,callsMadeToCallbackNum=0]
    #Repeat of the above change.
    125451194: Sep 07 13:54:33.853 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_SaveContactsMsgProcessor-198-0-SaveContactsMsgProcessor] com.cisco.config.impl.ConfigStubImpl configStubImpl-replace() exporting object: DialingListConfig[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,desc=,recordID=0,dialingListID=15541,campaignID=30,accountNumber=5008DM,firstName=Other.wav,lastName=Agent,phone01=912145551234,phone02=,phone03=,gmtZonePhone01=92,dstPhone01=true,gmtZonePhone02=-1,dstPhone02=true,gmtZonePhone03=-1,dstPhone03=true,callbackNumber=912145551234,callbackDateTime=Tue Mar 05 20:15:33 CST 2024,callStatus=4,callResult=8,callResult01=8,callResult02=0,callResult03=0,lastNumberDialed=1,callsMadeToPhone01=1,callsMadeToPhone02=0,callsMadeToPhone03=0,numMissedCallback=0,retry=false,callsMadeToCallbackNum=0]
    #Repeat of the above change.
    125508618: Sep 07 14:01:06.354 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_SaveContactsMsgProcessor-198-0-SaveContactsMsgProcessor] com.cisco.config.impl.ConfigStubImpl configStubImpl-replace() exporting object: DialingListConfig[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,desc=,recordID=0,dialingListID=15541,campaignID=30,accountNumber=5008DM,firstName=Other.wav,lastName=Agent,phone01=912145551234,phone02=,phone03=,gmtZonePhone01=92,dstPhone01=true,gmtZonePhone02=-1,dstPhone02=true,gmtZonePhone03=-1,dstPhone03=true,callbackNumber=912145551234,callbackDateTime=2024-03-05 20:15:33.0,callStatus=3,callResult=1,callResult01=8,callResult02=0,callResult03=0,lastNumberDialed=4,callsMadeToPhone01=1,callsMadeToPhone02=0,callsMadeToPhone03=0,numMissedCallback=0,retry=false,callsMadeToCallbackNum=1]
    #New callbackDateTime set.
    125514552: Sep 07 14:01:30.954 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_SaveContactsMsgProcessor-198-0-SaveContactsMsgProcessor] com.cisco.config.impl.ConfigStubImpl configStubImpl-replace() exporting object: DialingListConfig[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,desc=,recordID=0,dialingListID=15541,campaignID=30,accountNumber=5008DM,firstName=Other.wav,lastName=Agent,phone01=912145551234,phone02=,phone03=,gmtZonePhone01=92,dstPhone01=true,gmtZonePhone02=-1,dstPhone02=true,gmtZonePhone03=-1,dstPhone03=true,callbackNumber=912145551234,callbackDateTime=Tue Mar 05 20:30:30 CST 2024,callStatus=4,callResult=8,callResult01=8,callResult02=0,callResult03=0,lastNumberDialed=4,callsMadeToPhone01=1,callsMadeToPhone02=0,callsMadeToPhone03=0,numMissedCallback=0,retry=false,callsMadeToCallbackNum=2]
    #Repeat of the above change.
    125514631: Sep 07 14:01:31.247 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_SaveContactsMsgProcessor-198-0-SaveContactsMsgProcessor] com.cisco.config.impl.ConfigStubImpl configStubImpl-replace() exporting object: DialingListConfig[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,desc=,recordID=0,dialingListID=15541,campaignID=30,accountNumber=5008DM,firstName=Other.wav,lastName=Agent,phone01=912145551234,phone02=,phone03=,gmtZonePhone01=92,dstPhone01=true,gmtZonePhone02=-1,dstPhone02=true,gmtZonePhone03=-1,dstPhone03=true,callbackNumber=912145551234,callbackDateTime=Tue Mar 05 20:30:30 CST 2024,callStatus=4,callResult=8,callResult01=8,callResult02=0,callResult03=0,lastNumberDialed=4,callsMadeToPhone01=1,callsMadeToPhone02=0,callsMadeToPhone03=0,numMissedCallback=0,retry=false,callsMadeToCallbackNum=0]
    #Repeat of the above change.
    125635076: Sep 07 14:15:54.719 CST %MIVR-CFG_MGR-7-UNK: [MIVR_SS_OB_SaveContactsMsgProcessor-198-0-SaveContactsMsgProcessor] com.cisco.config.impl.ConfigStubImpl configStubImpl-replace() exporting object: DialingListConfig[schema=DialingListConfig#4,time=2024-03-05 13:53:13.0,recordId=15541,desc=,recordID=0,dialingListID=15541,campaignID=30,accountNumber=5008DM,firstName=Other.wav,lastName=Agent,phone01=912145551234,phone02=,phone03=,gmtZonePhone01=92,dstPhone01=true,gmtZonePhone02=-1,dstPhone02=true,gmtZonePhone03=-1,dstPhone03=true,callbackNumber=912145551234,callbackDateTime=2024-03-05 20:30:30.0,callStatus=3,callResult=1,callResult01=8,callResult02=0,callResult03=0,lastNumberDialed=4,callsMadeToPhone01=1,callsMadeToPhone02=0,callsMadeToPhone03=0,numMissedCallback=0,retry=false,callsMadeToCallbackNum=1]

    • So now how do you confirm that the record is complete and there will be no more callbacks. Let’s look at the log lines above and zero in on callStatus and callResult. Status (3:Closed, 4:Callback). Result (1:Voice, 8:Callback). You need to look at all the lines to get a picture if there will be another call or not. In our case the last log found has a status of close and a result of voice with no new records. Signifying that this is now close and done. I wish it was more clear, but this is the best I could find.

      125445090: …callStatus=3,callResult=1,callResult01=1…
      125451126: …callStatus=4,callResult=8,callResult01=8…
      125451194: …callStatus=4,callResult=8,callResult01=8…
      125508618: …callStatus=3,callResult=1,callResult01=8…
      125514552: …callStatus=4,callResult=8,callResult01=8…
      125514631: …callStatus=4,callResult=8,callResult01=8…
      125635076: …callStatus=3,callResult=1,callResult01=8…

      You can find all values here.

    ~david

    Two Days of Innovation and Insights at Enterprise Connect 2024: A Personal Overview

    I was saddened that I could only do 2 days at EC24, but those 2 days were jam-packed. Here’s recap of what I saw and some thoughts on the some specific vendor announcements I got a chance to hear on the stage and see on the expo floor.

    Overall Recap

    This year brought a nice change to some of the main stage sessions with both vendors and customers sharing the stage. This added a nice dynamic to the conversation and helped balance some things out. In one of the sessions while all the vendors were saying single vendor is best a customer said the total opposite. The flip side of this is that some times the stage looked like a public school classroom with the number of people on stage. There were too many panelist and not enough time to really dive deeper into any one topic. Maybe break up the sessions to separate topics to avoid this.

    It was interesting to hear that large orgs have task forces convened around how AI could be applied to their business. Sounds like use cases are brought to them and they assess the merits of using AI and put together a plan. While this all sounds like a great idea, instead of jumping head first and sprinkling AI on everything, I do wonder how much education and knowledge these tasks forces actually have. Is AI consulting a thing now? I suspect it is.

    Burnout was a bigger talking point than the crunch on the potential employee pool size. While no one outright said that jobs will be eliminated, the reality is that they will. If you can reduce workload by a percentage, then there’s no reason why you couldn’t reduce your workforce by a similar amount. While we talk about cross skilling and shifting agents to do other tasks, I wonder how much that really happens. I suspect it might be easier to just hire for the new role than try to train up. Of course this is not universal, but if even half of these AI promises come through we will see a large number of agent seats evacuated. The burnout message didn’t seem aligned with what I project how AI will impact employment in the next 5-10 years.

    AI dominated every session I attended and while I understand why it really felt like the same use cases over and over again. While I am sure it’s hard to come up with truly revolutionary use cases it does feel very repetitive seeing the same use case over and over again from all the vendors. It was also interesting to not see Edify be an exhibitor I wonder if this is a signal to how they are doing business wise, anyone know?

    Vendor Specific Recap:

    Microsoft

    Live Keynote Recap: https://www.linkedin.com/posts/dmacias_enterpriseconnect-activity-7178398994623078400-YD-c?utm_source=share&utm_medium=member_desktop

    I was expecting MS to open the floor with Copilot, then go into the demos with more Copilot, and finally bring it home with Copilot. However, this was not the case. The first part of their keynote was focused on hybrid work, which is a message that Cisco has been carrying for a few years.  Intelligent recap for phone calls was very cool and then being able to have Copilot use other assets and share outputs from the conversation with others was very cool. The voice isolation demo was a bit rough. I wonder if this is because MS has to do it all on software (Teams) while Cisco demos this on hardware.

    The new to me Teams Queue App was of most interest to me. This is the greatest thread to every CCaaS provider out there. The introduction of the Queues App is the first clear signal of a native contact center like feature and while the person who gave me the demo of this feature said 3 times that “this is not a call center”, it sure smelled like a call center to me. Having an agent and supervisor view, ability to queue voicemails, ability to monitor agents and take over calls, and access to raw data make this feature very compelling and a no brainier to deploy if you want to keep everything on Teams. If I was a vendor that has bolted on contact center functionality on top of Teams I would be worried. Now, I really don’t think we’re going to see a full fledged contact center from MS next year, but I do think in the next 5 years they will finally be there and would have done it incrementally anchoring everything around Teams.

    Cisco

    Live Keynote Recap: https://www.linkedin.com/posts/dmacias_entepriseconnect-activity-7178417256744329217-WOUg?utm_source=share&utm_medium=member_desktop

    The items that stood out were dropped call detection and summarization. Not sure if this is based on conversation context or PSTN signaling. It was also interesting to see Cisco talking about going beyond LLMs to what they called real time media model (RTMM). In short, giving AI other dimensions to make decisions. Their noise suppression demo was much better’s than Microsoft’s, but their noise was being produced by toys which had repetitive audio which might be much easier to filter out as well as the fact that they own the hardware which could allow for better suppression all around. Finally, Cisco still has some of the most attractive hardware out there. Their designs are just slick and the release of the 9800 (https://www.webex.com/us/en/devices/phone-series/cisco-phone-9800-series.html) phone continues that trend.

    Zoom

    I was not there for the Zoom keynote, but I did sit down on one of the expo sessions and went to the floor to bug them about what all they were releasing. First, VM transcription feature was pretty cool. The ability to automatically crate tasks from the content and have the system automatically bring certain VMs to the top for immediate attention. One feature I had not seen before and I believe is new is email routing, which is something none of the other big players are doing natively. Finally, licensing seems very straightforward and easy to understand… looking at your MS.

    Amazon Connect Quota Visibility

    Not being able to see what your applied quota settings were has been a huge sore spot with Connect. We’ve resorted to keeping track of quotas on a spreadsheet and having AWS support provide updated numbers every 6 months. However, this is not scalable and generally a pain. I was very please when the following announcement was made, which states:

    You can now view applied quota values for resources within each of your Amazon Connect instances using AWS Service Quotas. …

    I immediately went to one of our Connect instances and was very disappointed at first. Because, the way AWS decided to present the applied quota as it’s not obvious. If you go to the Service Quotas > AWS Services> Amazon Connect. You see all applicable quotas for Connect. In this case let’s focus on Quick Connects. Notice that the “Applied quota value” is still the default.

    Amazon Connect Service Quotas

    Amazon Connect Service Quotas

    If you click on the actual quota. You will still see the default at the top, but at the bottom right you’ll see the true applied number.

    Quick Connects Per Instance Service Quota

    Quick Connects Per Instance Service Quota

    I’m happy this is here, but I wish AWS would just go the extra mile and not require us to click on the actual quote to see the true number. I’ve not validated this via the CLI, but I am hopeful it’s more obvious there.

    ~david

    Read Only Script Editor Access in PCCE

    This question came up this past week and I had a nagging suspicion it wasn’t the case and spent some time trying to get it to work. In the good old days of UCCE Config Manager had the Feature Control set capability which allowed users to have a limited view to the ICM Script Editor. This was great for those type of users who understood the scripting, but were not trained up enough to make changes.

    The PCCE 11 documentation seems to hint at this being possible. You create a read-only Administrator and then you give them Script Editor access. I understand this is a huge leap, but it is not an unreasonable assumption. However, the 12.6 documentation removed that information as well removed the ability of having read-only administrator. It appears that your only option in CCE Admin > Users > Roles is to remove all access to Script Editor The following role:

    Capture

    Has this effect:

    Capture1

    If you add the Script Editor role again your user has full access to Script Editor again. So to summarize, it’s not possible to have a read only Script Editor user. I would love to be proven wrong. For now I hope this helps others out there looking for the answer.

    ~david

    A Deeper Dive Into the Cisco Finesse Layout

    I’ve been helping a customer migrate from Cisco’s Unified Contact Center Enterprise (UCCE) 11 to UCCE 12 and Amazon Connect. Depending on the complexity and needs of the business they might move to UCCE 12 or they might be moved to Connect. This has caused me to spend some time thinking of the most efficient way to migrate configuration between multiple environments and ensure everything is up to date.

    One of the components which I’ve done a lot of work with, but never really looked at too closely is the Finesse layout. In this post we’ll break down what layouts are, how they work, and provide some interesting pieces of trivia around Finesse. This information is up to date as of Finesse 12.5. Make sure you reference the documentation for your specific version.

    BACKGROUND

    To start, the Finesse front end, what the agent and administrators see, are based on the OpenSocial specification. What this specification provides is a runtime environment where trusted and untrusted web components or gadgets can interact with the hosting platform, 3rd party services, or with other gadgets. Said differently, this specification sets the rules. It dictates how components will look, where they are placed, and what they can do. While the OpenSocial foundation no longer exists. At one point the foundation was moved under W3C where it was then set to inactive in 2018. The latest specification can be found living here.

    An interesting bit of trivia. One of the original developers of OpenSocial was none other than MySpace. Remember them?

    You will hear the word gadget multiple times and it is worth defining as it is a core block of Finesse. The best way to think about a gadget is as an application which can be embedded inside another application. If you’re familiar with widgets, you should consider gadgets to be very similar in nature. Now, something a lot of us fail to remember is that all of Finesse is actually one big gadget with a lot of smaller gadgets inside. While you can’t replace the whole gadget that runs on the Finesse server, you can add your own gadgets inside the Finesse gadget using a layout.

    XML

    The very first thing you need to understand is that like all things Cisco Contact Center Enterprise and Express you must be very familiar with working with XML. While XML has lost a lot of favor in the last few years, when it comes to desktop layouts, using XML makes a lot of sense. While editing a layout can be confusing, Cisco does a good job of assisting with basic syntax checking to catch simple errors. In an ideal world there would be a visualizer that previews the changes you’re making to your layout before saving them. Maybe one day.

    The XML file loaded must conform to this schema. The schema is what dictates what tags your layout can have as well as naming conventions for components. 

    The XML schema has the following elements defined. These are not in the order they appear in the schema, but I’ve ordered in a hierarchical way to better highlight their relationship. Additionally, there are other elements included in the schema which are not covered below. The list below shows the most important elements to read through to better understand a Finesse layout:

    • finesseLayout: Think of this as the outer boundaries of the whole desktop.
    • layout: The actual layout.
    • role: Role definition.
    • tabs: Grouping of tabs.
    • tab: Each single tab.
    • page: The grouping of gadgets within that page referenced by a tab.
    • columns: Grouping of columns.
    • column: Each single column.
    • gadget: The actual gadget URL.

    Another interesting bit of trivia. The XML schema allows for three types of Finesse users: Agent, Supervisor, and Admin. While the desktop layout never references Admins the schema has an additional role that Cisco could later enable or currently uses for publicly restricted functions.

    DESKTOP LAYOUTS

    The layout is what dictates what will be loaded when an agent or supervisor login to Finesse. More importantly, it allows for the organization of different gadgets on the page to fit your contact center requirements. At its core the layout includes the following sections:

    • Horizontal Header: This section is the top bar across Finesse. And includes a Logo, Product Name, Agent State for Voice, State for Digital Channels, Dialer Component, and Identity Component. For most installations you’ll only ever see the logo, name, voice state, and identity components.
    • Alternate Hosts
    • Title and Logo in Header
    • Headless Configuration: If your gadget does not require a UI.
    • Customized Icons

    Interesting tip: Most gadgets can be dragged and dropped and resized by agents, however out of the box this feature is disabled. To enable it look at the enableDragDropAndResizeGadget config key.

    DEFAULT GADGETS

    Out of the box you should see the following gadgets:

    • Call Control
    • Queue Statistics
    • Agent Call History
    • Agent State History
    • Customer Context
    • Team Data (Supervisor)

    Additionally there are a good bit of disabled gadgets that you can use they all revolve around bringing CUIC data, specifically Live Data, to the agent desktop.

    I hope to have time to dive deeper on this topic. There are so many little morsels of information every where you look, you just have to dig a little deeper to find them.

    ~david

    Find specific phone number in Finesse phone books through the Finesse API using Python

    Here’s a quick Python script that will allow you to go through each phone book in Finesse and identify the phone book that has the phone number(s) you’re after. Just fill in your information ‘<>’ and update the numbers you want to find.

    import requests # for using API
    from requests.auth import HTTPBasicAuth
    import xml.etree.ElementTree as ET # for parsing XML

    requests.packages.urllib3.disable_warnings() #If using a self signed certificate

    rootURL = ‘<>’ # e.g. http://finesse.company.com
    phonebooksURL = ‘/finesse/api/PhoneBooks’
    username = ‘<>’ #Finesse admin username
    password = ‘<>’

    headers = {‘Accept’: ‘application/xml’, ‘Content-Type’: “application/xml”}

    phonenumbers = [‘5555551234’, ‘5555551235’, ‘5555551236’, ‘5555551237’] #phone numbers you want to find.

    res = requests.get(rootURL+phonebooksURL, headers= headers, verify=False, auth=HTTPBasicAuth(username, password))
    print(res.status_code)

    root = ET.fromstring(res.content)

    for phonebooks in root:
    phonebookId = phonebooks[3].text
    res = requests.get(rootURL+phonebookId+’/Contacts’, headers=headers, verify=False, auth=HTTPBasicAuth(username, password))
    print(res.status_code)
    root = ET.fromstring(res.content)
    for contacts in root:
    for number in phonenumbers:
    if number in contacts[3].text:
    print(phonebooks[2].text, contacts[3].text)

    ~david

    Audacity Export Encoding

    Generating telephony audio prompts with Audacity on Windows and Mac

    This blog post should be pretty basic, but in the last few months I’ve had two different parties ask me about this so I figured I would capture this for posterity. If you want to use Audacity to convert your audio file to a compatible format that can play in your Cisco UCCX or CCE call center or any system that uses CCITT u-Law 8.000 Khz, 8 Bit, Mono format.

    Assuming you’re running the latest version of Audacity (I’m using version 3.1.3 on both Windows and Mac). Open the audio file you wish to convert. There are 3 settings you must change.

    • The format needs to be set to PCM 16-bit.
    • Project Rate (Hz) needs to be set to 8000.

    Audacity Format and Rate

    Audacity Format and Rate

    • File > Export > Export as WAV and ensure you set the right encoding.

    Audacity Export Encoding

    Audacity Export Encoding

    That’s it, you’re good to go.

    ~david

    2022 Cisco Designated VIP

    I am pretty jazzed for making this awesome list for the second year in a row. The amount of brain power that you can find in the Cisco community forums is insane and being grouped with them is an honor. I really appreciate the recognition from Cisco.

    As an added bonus I looked back for my very first post on the community forums. It happened on 02/14/2006, almost 16 years ago. I don’t remember the specific project I was working on, but it involved IPCC Express 3.x… :)

    ~david