Cisco Troubleshooting – One Way Audio Issue

Hey everyone,

Today I’m going to talk about a common issue that I believe you’ve already faced in your infrastructure.
One way audio happens when you have a call between 2 phones (internal-internal or internal-external), and one of them cannot hear the other end.

The causes of one-way audio in IP Telephony can be varied, but the root of the problem usually involves IP routing issues.

Possible causes for the one-way audio issue:

* RTP traffic is being blocked or consumed by a Firewall or another security device.
* RTP traffic is being misrouted by a route recently added / learned, or a VRF or WAN.
* Signalling issues – Call agent is not passing the correct ports or codec, or the communication is tagged as ‘send only’ or ‘receive only’.
* RTP traffic is corrupted.
* One side is not transmitting.

A quick way to check if there’s been audio during a call is by checking its statistics.
There are 2 ways to do that. Using Jabber/Deskphones statistics, or accessing Phone’s web page and checking also the statistics.

During a call, in your Jabber, press Crtl + Shift + S to open the call statistics. A pop up window like that will appear:


If you are not part of the call, you can check that through phone’s web page. Open it (using phone’s IP Address), and go to Stream 1 in the left menu. The whole statistic will come up in the middle. Check if the IP address and port match the information on both sides. Keep pressing the ‘stream 1’ link and you will notice that the TX and RX stats keep increasing.
Here you can check the ‘Local’ and ‘Remote’ IP addresses, then you see the port. If the information is the same on the other side, (‘Local’ on one side should correspond to the ‘Remote’ on the other), then signalling is good.
You can also check if the Coded used is the one you were expecting.  If not, change the codec preference list or the available BW on the region.


Now, let’s have a  look at some scenarios and solutions, and most common issues found in the field.

  • Ensure That IP Routing Is Enabled on the Cisco IOS Gateway and Routers

Some Cisco IOS gateways, such as the VG200, disable IP routing by default. This default setting leads to one-way voice problems.
Ensure that IP routing is enabled on your router. In order to enable IP routing, issue this global configuration command on your Cisco IOS gateway:
voice-ios-gwy(config)#ip routing

  • Check Basic IP Reachability

Always check basic IP reachability first. Because Real-Time Transport Protocol (RTP) streams are connectionless (transported over UDP), traffic may travel successfully in one direction but get lost in the opposite direction. This diagram shows a scenario in which this can happen:

Subnets A and B can both reach Subnet X. Subnet X can reach Subnets A and B. This allows the establishment of TCP connections between the end stations (A and B) and the Cisco Call Manager. Therefore, signalling can reach both end stations without any problems, which allows the establishment of calls between A and B.

Once a call is established, an RTP stream that carries the audio must flow in both directions between the end stations. In some cases, Subnet B can reach Subnet A, but Subnet A cannot reach Subnet B. Therefore, the audio stream from A to B always gets lost.

This is a basic routing issue. Use IP routing troubleshooting methods in order to get to the stage at which you can successfully ping Phone A from Gateway B. Remember that ping is a bidirectional verification.

If transcoding is configured for an Intercluster trunk (ICT), ensure that a Media Termination Point (MTP) is configured in the Media Resource Group and Media Resource Group List associated with the trunk. If you specify an MTP when one is not needed, or fail to configure an MTP if it is needed, it has been known to cause one way voice issues for ICT configurations.

When the Cisco IOS gateway has multiple active IP interfaces, some of the H.323 signalling may be sourced from one IP address and other parts of it may reference a different source address. This can generate various kinds of problems. One such problem is one-way audio.

In order to get around this problem, you can bind the H.323 signalling to a specific source address. The source address can belong to a physical or virtual interface (loopback). Use the h323-gateway voip bind srcaddr ip-address command in interface configuration mode. Configure this command under the interface with the IP address to which the Cisco Call Manager points.

One-way voice can occur in Media Gateway Control Protocol (MGCP) gateways if the source interface for signalling and media packets is not specified. You can bind the MGCP media to the source interface if you issue the mgcp bind media source-interface interface-id command and then the mgcp bind control source-interface interface-id command. Reset the MGCP gateway in Cisco Call Manager after you issue the commands.

If the mgcp bind command is not enabled, the IP layer still provides the best local address.

The guidelines for the mgcp bind command are:

*   When there are active MGCP calls on the gateway, the mgcp bind command is rejected for both control and media.
*   If the bind interface is not up, the command is accepted but does not take effect until the interface comes up.
*   If the IP address is not assigned on the bind interface, the mgcp bind command is accepted but takes effect only after a valid IP address is assigned. During this time, if MGCP calls are up, the mgcp bind command is rejected.
*   When the bound interface goes down, either because of a manual shutdown on the interface or because of operational failure, the bind activity is disabled on that interface.
*   When bind is not configured on the Media Gateway Controller (MGC), the IP address that is used to source MGCP control and media is the best available IP address.

If you find that there are clock slips on the E1 or T1 interface from the show controller {e1 | t1} command, there might be some mismatch in the clocking configuration on the Voice Gateway.

Two useful commands to use in order to verify packet flow are the debug cch323 rtp command and the debug voip rtp command. The debug cch323 rtp command displays packets that are transmitted (X) and received (R) by the router. An uppercase character indicates successful transmission or reception. A lowercase character indicates a dropped packet.

voice-ios-gwy#debug cch323 rtp

RTP packet tracing is enabled

!— This is an unanswered outgoing call. !— Notice that the voice path only cuts through in the forward direction and !— that packets are dropped. Indeed, received packets are traffic from the !— IP phone to the PSTN phone. These are dropped until the call is answered.

Mar 3 23:46:23.690: ****** cut through in FORWARD direction *****


!— This is an example of an answered call:

*Mar 3 23:53:26.570: ****** cut through in FORWARD direction *****

!— At this point, the remote end picks up the phone.

*Mar 3 23:53:30.378: ****** cut through in BOTH direction *****

!— This is the end of the conversation.

Note: In Cisco IOS Software Release 12.2(11)T and later, the debug cch323 rtp command-line interface (CLI) command has been replaced by the debug voip rtp command.

voice-ios-gwy#debug voip rtp

——–cut through in BOTH direction——————-

*Mar 27 19:52:08.259: RTP(32886): fs rx d=,
pt=0, ts=4FFBF0, ssrc=8E5FC294
*Mar 27 19:52:08.275: RTP(247): fs tx d=,
pt=0, ts=5D00C8D9, ssrc=1F1E5093
*Mar 27 19:52:08.279: RTP(32887): fs rx d=,
pt=0, ts=4FFC90, ssrc=8E5FC294
*Mar 27 19:52:08.295: RTP(248): fs tx d=,
pt=0, ts=5D00C979, ssrc=1F1E5093
*Mar 27 19:52:08.299: RTP(32888): fs rx d=,
pt=0, ts=4FFD30, ssrc=8E5FC294
*Mar 27 19:52:08.315: RTP(249): fs tx d=,
pt=0, ts=5D00CA19, ssrc=1F1E5093
*Mar 27 19:52:08.319: RTP(32889): fs rx d=,
pt=0, ts=4FFDD0, ssrc=8E5FC294
*Mar 27 19:52:08.335: RTP(250): fs tx d=,
pt=0, ts=5D00CAB9, ssrc=1F1E5093
*Mar 27 19:52:08.339: RTP(32890): fs rx d=,
pt=0, ts=4FFE70, ssrc=8E5FC294
*Mar 27 19:52:08.355: RTP(251): fs tx d=,
pt=0, ts=5D00CB59, ssrc=1F1E5093
*Mar 27 19:52:08.359: RTP(32891): fs rx d=,
pt=0, ts=4FFF10, ssrc=8E5FC294
*Mar 27 19:52:08.375: RTP(252): fs tx d=,
pt=0, ts=5D00CBF9, ssrc=1F1E5093
*Mar 27 19:52:08.379: RTP(32892): fs rx d=,
pt=0, ts=4FFFB0, ssrc=8E5FC294
*Mar 27 19:52:08.395: RTP(253): fs tx d=,
pt=0, ts=5D00CC99, ssrc=1F1E5093
*Mar 27 19:52:08.399: RTP(32893): fs rx d=,
pt=0, ts=500050, ssrc=8E5FC294
*Mar 27 19:52:08.976: RTP(282): fs tx d=,
pt=0, ts=5D00DEB9, ssrc=1F1E5093
*Mar 27 19:52:08.980: RTP(32922): fs rx d=,
pt=0, ts=501270, ssrc=8E5FC294
*Mar 27 19:52:08.996: RTP(283): fs tx d=,
pt=0, ts=5D00DF59, ssrc=1F1E5093

*Mar 27 19:52:09.000: RTP(32923): fs rx d=,
pt=0, ts=501310, ssrc=8E5FC294
*Mar 27 19:52:09.016: RTP(284): fs tx d=,
pt=0, ts=5D00DFF9, ssrc=1F1E5093

This is all for today.

I hope you’ve enjoyed Smile

See ya!


Cisco Finesse – Adding 3rd Party Gadgets

Hey guys,

In this post, I’m going to show you how to add new gadgets to your Cisco Finesse.
It can be any special gadget you may have created, or even a simple web page to be opened in one of the Finesse Tabs.

Finesse itself allow you to upload third-party gadgets. It’s done via an specific user, 3rdpartygadget. This account only has permission to this directory, /files, and any directories created under it.

  • Set a Third Party Gadget account password

To start off, we need firstly to set a password for this account, via CLI.
So, connect via SSH into your UCCX server and enter the line command: utils reset_3rdpartygadget_password .
For the password, enter ciscocisco,  and confirm it.


Not, it’s time to upload your Gadget to the server.
Finesse gadgets are OpenSocial gadgets. An OpenSocial Gadget is an XML document that defines metadata for an OpenSocial Gadget container, the Finesse agent desktop. Gadgets are highly cacheable so it does not need a high-performance server.

A gadget consists of the following:

  • XML to define metadata
  • HTML for markup
  • JavaScript for interactivity
  • CSS for presentation & style

Let’s see now the steps to upload your files. For this task, I’m using FileZilla (download):

  1. Open FileZilla.
  2. In the quickconnect bar,
    in red, fill the following fields:
  • Enter the Finesse FQDN: {{web-url}} in the Host field.
  • Enter 3rdpartygadget in the Username field.
  • Enter ciscocisco in the Password field. This is the 3rdpartygadget password defined in the previous step.
  • Enter 22 in the Port field.
  • Click Quickconnect.


  • If a message pops up, about Unknown host key, select Always trust this host, add this key to the cache, and then OK.
  • You must see now a message like that in the log section: Status:     Connected to <URL>.
  • In the remote site section, confirm that you see a folder named / and you can see the files folder below.


Now you have to transfer your Gadgets file to the server:

  • Drag the EmbeddedWebApp folder (that contains the xml, js, and css) into the files folder. Note that this is the folder inside of the EmbeddedWebAppSampleGadget-Finesse-x.x.x-vx.x folder.
  • Confirm that the transfer was successful by checking that the transfers shows up in the Successful transfers tab at the bottom.


Give the right permission to the files. They have to have public read permission to be loaded o the Finesse Agent Desktop.

  • Select the EmbeddedWebApp folder.
  • Right-click on the EmbeddedWebApp folder.
  • From the dropdown menu, select File permissions….
  • A Change file attributes window appears.
  • Under Public permissions verify that Read permissions is selected.
  • Select Recurse into subdirectories.
  • Click OK.


In my case, I’ve added a new Tab to my Finesse, and added my gadget. This gadget opens a URL in my webserver, an it’s used only by Supervisors.

To do that, go to your UCCX, then go to Cisco Finesse Administrator


Go to Team Resources, and select the Team you want to change the Layout to include your new Tab/Gadget:


In the box below, you see now the Desktop Layout Configuration.
This is how your Finesse will look like, and its gadgets.
First thing you have to do, is to select the option Override System Default. So you can edit the XML.


So now, you have to copy all that XML file to a notepad, and add manually the Tab you want to have, and the gadget you just have added.
In my case, I want only Supervisors to have access to it, so in the XML, I go direct to the second block, which is related to Supervisors.

My XML will be like that:

I’ve added 2 Tabs: one called ISR Management, and a second one, TEST


After adding the new Tab to your XML, you have to Copy and Paste it back to your Desktop Layout Configuration, and Save it.

Now go back to your Finesse, and you must be able to see the 2 Tabs you just added:


In my case, when I select the Tab, it runs a page from my webserver:


I hope you like it Smile

See ya!


Cisco CUCM – Intercluster Lookup Service

Hey people,

Today I’m going to explain the concept of a good feature, called ILS (Intercluster Lookup Service).
ILS enables different CUCM Cluster to exchange directory URI with other clusters in an ILS network, allowing you to create networks of remote CUCM clusters. So, when you configure ILS on multiple clusters, it updates CUCM with the current status of remote clusters in the ILS network.
An ILS network comprises the following components:

Hub Clusters
Hub clusters form the backbone of an ILS network. Hub clusters exchange ILS updates with the other hub clusters in the ILS network, and then relay that information to and from their spoke clusters.

Spoke Clusters
A spoke cluster connects to the hub cluster in an ILS network to relay ILS updates to and from the rest of the ILS network. Spoke clusters contact only their local hub cluster and never directly contact other hub clusters or other spoke clusters. A spoke cluster can have only one hub cluster.

Global Dial Plan Imported Catalogues
To provide URI dialling compatibility with third-party systems, you can manually import a third-party directory URI or +E.164 number catalog from a CSV file into any hub cluster in the ILS network


Let’s go deep to the configuration. For that, I’m going to use this topology as example:
2 CUCM clusters in the ILS network.

  • – HQ Cluster
  • – BR Cluster


  • Activate Intercluster Lookup Service on all the CUCM Publisher

    You must activate the Intercluster Lookup Service to configure Cluster IDs and Remote Clusters.
    From Cisco Unified Serviceability, choose Tools > Service Activation.
    From the Server drop-down list, choose the node and then click Go.
    Select ‘Intercluster Lookup Service’ and Save.


  • Configure Cluster IDs

    You must configure a unique cluster ID for each cluster in the ILS network. The clusters use this unique cluster ID and peer ID when they exchange status messages.
    In Cisco Unified Communications Manager Administration, choose System > Enterprise Parameters.
    In the Enterprise Parameters Configuration window Cluster ID field, enter a name of the cluster that you
    want to configure in your network.


  • Activate ILS on the Hub Cluster

    You must configure each cluster in your ILS network as either a hub cluster or a spoke cluster. Each ILS network must have at least one hub cluster. You can connect a hub cluster to other hub clusters, or you can configure a hub cluster as the only hub cluster in the network. In addition, you can connect a hub cluster to multiple spoke clusters, or you can configure the hub cluster with no spoke clusters.

    Now, considering the Node as the ILS HUB Cluster.

    Go to Advanced Features > ILS Configuration > and configure as follows.


    Route String: This will be a unique string that will be advertised to the other clusters. Other ILS peers use Route String to route the call.

    Above configuration uses ‘Password’ based ILS authentication and synchronize the clusters every 1 minute.
    The moment you click Save, it will ask for ILS Registrar Server. Since I do not have any other HUB cluster, we can leave blank and click OK.


  • Activate ILS on the Spoke Cluster

    Now, the node will be  the SPOKE cluster.
    Go to Advanced Features > ILS Configuration > and configure as follows.

    When you click Save, enter the Registrar server as the HUB cluster


    Now refresh the ILS configuration to see the updated status.


  • Configuring URIs for the End Users

    Go to User Management > End User > and select the user you want to enable URI.
    For LDAP user, make sure the URI is synced from LDAP server. For the local users, you can set a URI as shown below.
    CUCM End User URI Configuration.png

    Then, go to the Controlled Devices section and assign the user’s desk phone as the controlled device. Also, configure the primary extension (mandatory!!).

    Primary extension for URI dialing.png

    Now go to the Directory number configuration(e.g. 1002). Now you will be able to see the URI field.

    Direcory URI configuration CUCM.png

  • Verify the Learned Directory URIs

    Go to Call Routing > Global Dial Plan Replication > Learned Directory URIs


    Any of the URI learned via ILS will be having 2 unique values. One is the Route String and another one is Cluster ID. Route String is used to route the call back to the respective cluster via a separate SIP trunk.
    ILS will only take care of advertising the URIs between clusters. They do not participate in call routing. For dialling the URI from one cluster to another, we need to have a separate SIP trunk.

  • ILS Restrictions


  • ILS Troubleshooting

    Here are some Troubleshooting Tips:

    Issue: Local Cluster Cannot Connect to the ILS Network

    To troubleshoot connection issues within the local cluster, open RTMT and run alarms and diagnostic traces on that publisher node. If you receive an error message when trying to establish ILS between your clusters, you can try to restart the Cisco Intercluster Lookup service from Cisco Unified Serviceability Administration. In addition, connection issues may arise if authentication is improperly configured between clusters. Check authentication in the following manner:
    • If you are using TLS, make sure that all clusters in the network are using TLSand that Tomcat certificates have been exchanged for all the servers that need to communicate.
    • If you are using TCP password authentication, make sure that all ILS clusters are using TCP password authentication and that the same TCP password is assigned across the network.

    Issue: Directory URIs Are Not Being Replicated Across the ILS Network

    This error can occur for a variety of reasons. Check the following:

    • Verify that all clusters in the network are configured to exchange global dial plan data. If a hub cluster is not configured to exchange global dial plan data, none of that hub’s spoke clusters will be able to exchange directory URI catalogs.
    • Allow enough time for end-to-end replication based on synchronization intervals (set on the ILS Configuration page) that are configured for all the clusters involved in the path. All clusters in an ILS network are a maximum of three hops from every other cluster in the network.
    • Use the utils ils showpeerinfo CLI command to monitor replication progress by looking at the USN values for the remote clusters.
    • Increase speed of replication by changing the ILS Sync Throttle Service Parameter. Note that a low setting can affect system performance.
    • Verify that all clusters in the ILS network have unique cluster IDs and that none of the clusters are configured with Stand Alone Cluster as its cluster ID. You can check Cluster IDs in Cisco Unified CM Administration under System > Enterprise Parameters.

    Issue: Global Dial Plan Replication Is Configured, but Unified CM Still Cannot Place a Call to A Learned Directory URI or Learned Number in a Remote ILS Cluster

    This condition can occur if ILS and Global Dial Plan Replication are enabled on all clusters in the network, but SIP route patterns that route to the route strings for the remote clusters have not been configured. Do the following:
    • In the ILS Clusters and Global Dial Plan Imported Catalogs view in the ILS Configuration window, check the route string for the remote cluster.
    • In the SIP Route Pattern configuration window, make sure that you have route patterns that map to the route strings for your remote clusters

    Hope you’ve enjoyed Smile

    See ya!


Cisco UCCX – Sending email using Script

Hi people!
In today’s post, I will show you how to configure your UCCX Script to send emails.

This can be easily a complement of my last Post (Cisco UCCX – Recording Prompt Script). In this post, I showed you how to create a script where you can record your own audio prompts and upload them to UCCX. Now, with this post, you can add some steps to send the recorded audio via email to improve a little bit your achievement.

Well, let’s go to the configuration!

Configure Email on UCCX Server
Go to your UCCX,  Subsystem Menu, then Email.
You have to add the SMTP and an email account which will be the source.


You can have as many variables as you want to easy your life. But there is one you must create.
Create a Contact Type variable, give a name,  “EmailContact” in my example, and give a null value.


Steps to be used in the Script

  • Create eMail
    Here you generate the subject line and body of the e-mail message.
  • Subject: Variable or expression that you want to use for the subject line of the message.
    Body: Variable or expression that you want to use for the body of the e-mail message
    eMail Contact: Variable that identifies the email. The variable “EmailContact”  we’ve created above.


  • Attach To eMail
    Use the Attach To eMail step to attach a document to an e-mail.
    Before you use an Attach To eMail step, you must use a Create eMail step to create the e-mail message.

    I’m showing you now how to attach that audio prompt we’ve just recorded, as an example.
    In that case, the variable Result contains the recorded audio, and I’m attaching it to the email using this Step.


  • Send eMail
    Use the Send eMail step to send an e-mail message you have created with the Create eMail step.
    When a script reaches the Send eMail step, it immediately sends the e-mail message to the e-mail server, and keeps the client waiting until the message is accepted by the e-mail server. If the server is unavailable because of server or network problems, the client must wait until the transaction times out.


So, your code must be like that.


It’s a really short code, but the idea is to have that attached to your main script, as a feature, to help you to handle queues, or other things, like sending recorded audios..

Hope you’ve enjoyed!
See ya!


Cisco UCCX – Recording Prompt Script

Hey Guys,

Today I’m going to show you the step-by-step to create a Script on UCCX, where you can record your own Audio Prompt. The audio is automatically saved in a Folder on UCCX.

It’s not an advanced Script, but it’s very useful.
Audio will be saved with the User’s ID, plus the date/time. I found this way easy to be found in the Folder, for instance: 1234567_07_19 – 10018am.wav.

Let’s see the steps to create the Script:

  • Defining Date/Time
    We are going to get the current Date and Time, and then, split it into 4 variables: second, minutes, hour, month and day. Then, add then in a variable sSlotTime, which will be part of the name of the Audio.
    These steps Switch are add the “0” in a number. For example, from “1” to “01”, so we keep all with 2 digits. We do the same for day, month, hour, minutes and second (always from 0 to 9).
    For the period, the result will give us 1 or 0. If it’s 1, I set the period to pm. If it’s 0, I set the period to am.




  • Getting User ID
    Now we will ask the user to enter their ID’s. The only reason here is to name the audio according to the ID. If you want, you can ask the user to enter ID and PIN as an authentication.
    You are saving the ID in a variable called ID.
    Then, we are setting the variable sFileType with ID + “_”.



  • Recording
    Time now to Record the audio. To do that, there is a step called Recording, where you indicate which variable you will save the result, audio, maximum duration, etc..
    So we are saving it in a variable called Result.


  • Manipulating the Result
    After recording the audio(with success), I’m giving 3 options to the user, through a Menu:

Press 1 to Listen the recorded audio – The audio saved in the variable Result will be played.
Press 2 to Save – We will give a full name to the audio. Variable sFileName will receive the Folder + sFileType + sSlotTime + “.wav”
Press 3 to Record it again – Go back to the Recording Step


  • Authentication
    To be able to save the audio to UCCX, we need authenticate, with an admin credential.
    So firstly, we need to Get the User, then you have to authenticate it.


  • Saving the audio
    After authenticating, we have to save the file in the audio repository.
    We use the step Upload Prompt to send the audio to the Server. If the Upload is done successfully, user will listen to a Menu, asking to press 1 to Record another audio, or any digit to finish.image
    The file name is that variable sFileName we’ve prepared, and the Document is the audio saved in the variable Result.


Basically, this is the code!

I hope you enjoyed!

See ya!



Cisco CUCM – Controlling Phones Remotely

Hey guys,

Today I’m going to talk about a very useful feature which gives us power to control remotely a Deskphone, and even make calls from it.


First things first, you will need to install an extension to your Browser, to be able to control the phones.

If you are using Google Chrome, download the extension HERE.
If you are using Mozilla Firefox, download the extension HERE.

CUCM Configuration

With the Browser extension installed, let’s check now what do we need to do on CUCM.
It’s pretty easy!

  • Phone Web Access
    Make sure the deskphone is enabled for Web Access. Go to the Device’s Page, scroll down till you see the Web Access option. It must be Enable.


  • NEW End User
    We could use any End User for that. But, as I’m centralize all requests in one user, I decided to create a new one only for that.
    Each phone you want to control, you have to associate to that End User.
    So, create a new user, associate as many phones as you want to Control, and add a Rule you have in your CUCM which gives them ability to control the phones.


  • Remote Control
    After installing the Browser extension, and configuring the Phone and End User, now it’s time to test the Remote Control.
    Go to CUCM, find the Phone you want to access and get the IP Address (Phone must be registered)
    PS: If you are controlling a phone which is MRA registered, you will need to be able to route to its real IP Address


As soon as you click on the IP Address, to access the Phone’s information, you will notice now something different. The option Control Me will be displayed.


Then, you will be asked to enter the Username and Password, from the End User we created above.


And now you have the Phone’s screen being displayed, with all available commands next to it.
From there, you can access the Settings, change configuration, and even Make calls…


Hope you enjoyed this quick, but useful and interesting Tip! Smile

See ya!!


Cisco Unity Connection Provisioning Interface (CUPI) API

Hey people,

Coming back to the DEV topics, today I’m going to give you a quick overview of Unity API.
It’s very simple, but it will give different perspectives of what you can automate on Unity.

CUPI is a provisioning API for Cisco Unity Connection that has been designed to be stable and simple to use. It is based on leading industry standards for web-based API development, and provides access to the most commonly provisioned data on Connection systems (users, contacts, distribution lists, and call handlers).

By using CUPI, you can securely do the following:

  • Create, read, update, and delete users and user configurations
  • Reset passwords
  • Create, read, and update distribution lists
  • Create, read, update, and delete call handlers
  • Create, read, update, and delete contacts

Getting the Schema Details

All the schema details for all supported object types can be obtained by going to the REST schema page using the URL:
                                                          http://{server name}/vmrest/schema

In the case of users the schema shows what will come back when fetching the full user data using a URL like this:
                                                   http://{server name}/vmrest/users/{object_id}


CUPI uses the same authentication and authorization scheme that the administration console uses. This means that the objects an administrator has access to when authenticated are determined by the roles to which the administrator is assigned.

Basic Operations

Searching For an user:
This request gives us all details about an specific user, searching it by the alias.

To search for a user account, do the following GET request:
GET http://<connection-server>/vmrest/users?query=(alias%20startswith%20ab)

You can have your results in XLM or JSON. It’s up to you. The only difference will be on how you manipulate that, to achieve your goal.
In XML, you will see this:


If you want to change, or create something new, you need to send a PUT or a POST request.
And, depending on your request, you have to search the user via user-objectID, instead of Alias. In this case, you have to send a request to get information from an user, save its ObjectID, and then use it in another request. See some samples below:

Listing User PIN Settings
Shows information about user’s PIN:  

                           GET https://<Connection-server>/vmrest/users/<user-objectid>/credential/pin

The following is the response from the above *GET* request and the actual response will depend upon the information given by you:


Changing PIN
In this case, we are going to use PUT, and we also have to send the new PIN in the Body:                    

PUT https://<Connection-server>/vmrest/users/<user-objectid>/credential/pin

This must be your response, indicating a success: Response Code: 204

Creating a User

To create a user account, do the following POST request:

POST http://<connection-server>/vmrest/users?templateAlias=voicemailusertemplate
<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”?>

The following is the result of the above POST request: 201 Created

Modifying a User

To modify a user account, do the following PUT request:

PUT http://<connection-server>/vmrest/users/{objectid}

<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”?>

The following is the result of the above PUT request: 204 Accepted

Deleting a User

To delete a user account, do the following DELETE request:

                                           DELETE http://<connection-server>/vmrest/users/{objectid}

The following is the result of the above DELETE request: 200 OK

These were only some samples of what you can do using CUPI. Of course, there are dozes, or hundreds of types of request.
Any specific request you want to do and don’t know how, let me know in the comments.

I hope you enjoy this post! Smile

See ya!


Cisco CME – B-ACD and Auto Attendant services

Hey people,

Today I’m going to change the Topic and talk about other thing…… which is the CME.
I particularly like CME, and enjoy working on it Smile

There is a feature on CME, which seems not to be so popular, but it’s really useful: B-ABC.
In case you only have a CME in your IPT Infrastructure, B-ACD can easily be a good alternative for Cisco UCCX or Cisco Unity (for a basic IVR).

The only limitation we have is related to Codec. You must use same codec on incoming and outgoing dial peers when transferring calls.

B-ACD is a Basic automatic call distribution (B-ACD) and auto-attendant (AA), and it provides:

  • Automatic answering of outside calls with greetings and menus that allow callers to select the appropriate department or to dial known extension numbers.
  • Managed call queues for hunt groups that route calls for different menu options.

Each Cisco Unified CME B-ACD application consists of one or more auto-attendant (AA) services and one call-queue service. From version 11.5 onwards, B-ACD introduces support for voice hunt group that includes SIP, SCCP, PSTN, and FXS.

Cisco Unified CME B-ACD Service Call Flow

Configuring Cisco Unified CME B-ACD

  • Downloading TCL Script and Prompts
    Download the Cisco Unified CME B-ACD tar archives to a TFTP server that is accessible to the Cisco Unified CME router (Download it HERE).

    Go to your CME and extract it using this command:
    archive tar /xtract tftp://X.X.X.X/cme-b-acd-X.X.X.X.tar flash:

  • Dial Peer
    Let’s configure the dial-peer that will be used reach the Application we are going to create. I’m going to add 2 Dial-peers, as usually we have one only for all incoming calls, and the second one will be used to send the call to the application.

    Router(config)#dial-peer voice 11 pots
    Router(config-dial-peer)#incoming called-number .
    Router(config-dial-peer)#port 0/1/0:15
    Router(config-dial-peer)#forward-digits all

  • Router(config)#dial-peer voice 222 voip
    Router(config-dial-peer)#service aa     ! — Enables AA service on dial-peer
    Router(config-dial-peer)#destination-pattern 6000
    Router(config-dial-peer)#session target ipv4:
    Router(config-dial-peer)#incoming called-number 6000
    Router(config-dial-peer)#dtmf-relay h245-alphanumeric
    Router(config-dial-peer)#codec g711ulaw
    Router(config-dial-peer)#no vad

Hunt Groups
In the below example, I’m creating Hunt Groups for SCCP phones.
A maximum of ten hunt groups can be associated with Cisco Unified CME B-ACD call-queue service. The final command is not used with hunt groups that are part of Cisco Unified CME B-ACD services. Instead, the param voice-mail command specifies the alternate destination for calls that cannot be connected to a hunt group because all hunt-group agents are unavailable or because a hunt-group agent does not become available within the configured maximum retry time.

Router(config)#ephone-hunt 1 sequential
Router(config-ephone-hunt)#pilot 6100
Router(config-ephone-hunt)#list 6001, 6002

Router(config)#ephone-hunt 2 sequential
Router(config-ephone-hunt)#pilot 6101
Router(config-ephone-hunt)#list 6001, 6002

Queues are responsible for routing the call to a Hunt Group and queue the call when members of the Group are all busy.

  service queue flash:app-b-acd-
     !  — Point to where you extracted the files
   param number-of-hunt-grps 3                     ! — Max number of hunt groups
   param queue-len 15                                        !  — Size of the queue (1 to 30)
   param aa-hunt1 6100                                      !  — Option 1 – Goes to Hunt 6100
   param aa-hunt2 6101                                     !  — Option 2 – Goes to Hunt 6100

Auto Attendant
Time to configure the auto attendant part of the script.

  service aa flash:app-b-acd-aa-      !  — Point to where you extracted the files
    paramspace english location flash:      ! – Defines the languages and where the files are
   paramspace english language en           ! —  Defines the code (en) to the audio files
   param service-name queue                  ! —  Associate AA with the Queue we configured above
   param handoff-string aa                  
! –Specifies the name of the service
  param aa-pilot 6000                      
! —  Declares the Pilot Number (Must be the same as the Dial Peer)
   param welcome-prompt   
! —  Prompt of the Welcome Message
   param menu-timeout 5                    
! — Sets the number of times the AA service will loop the menu prompt
   param dial-by-extension-option 9         
! —  Enables callers to dial extension numbers after dialing the specified menu number
   param max-extension-length 4             
! —  Restricts the number of digits that can be dialed
   param number-of-hunt-grps 3
   param queue-overflow-extension 3999      
! —  If queue is full, sends the call to 3999
   param second-greeting-time 45            
! —  Defines the time delay before the second greeting is played
   param call-retry-timer 10               
! —  Assigns the  time that calls must wait between retries to connect to a hunt group pilot number or to the alternate destination number.
   param max-time-call-retry 90             
! —  This is the maximum period of time for which a call can stay in a call queue
   param max-time-vm-retry 2                
! —  Assigns the number of times that calls can attempt to reach the alternate destination number.
   param voice-mail 3999                    
! —  Defines an alternate destination for calls that are not answered by a hunt group
   param send-account true                   ! —  Generates call detail records for calls that are handled by B-ACD

Hope you enjoyed this post!

See ya Smile


Cisco CUCM – AXL API requests using Python

Hey guys,

Following my post about the overview of Cisco CUCM – SOAP (read it HERE), I’m going to show you now how to send some basic requests using Python.

To be able to do that, you will need to have:

  • Python installed (download it here)
  • AXLSQLToolkit
  • Python Libraries (Zeep, urllib3 , requests – installed via PIP)

After installing Python and its libraries, let’s go to the codes!

To run my codes, I use PyCharm….but you can use any other software of your preference.

Firstly, you have to declare your libraries:
*Code will be passed at the end of the article Smile


Now you have to enter your CUCM information, such as IP, username and password.
We are going to use ZEEP to create SOAP requests. In case of any fault, Zeep will show what SOAP envelope that was sent and the response from CUCM AXL.
If you’re not disabling SSL verification, host should be the FQDN of the server rather than IP.


To start with a simple request, I’ll show you how to list Phones.
Have in hands the  Cisco DevNet AXL Schema Reference. It will help you to understand each request, which argument you must send as a searchCriteria and which arguments you must expect as returnedTags.
Only declared arguments in the returnedTags will be displayed. The rest will be showed as none.

For example, I want to list a phone, based on the Device Name, and want to have the arguments namedescription, model and device pool being returned to me.
The code will look like this:


The result will like this:

     ‘return’: {
         ‘phone’: [
                 ‘name’: ‘SEP0004F2F01F1A’,
                 ‘description’: ‘Meeting Room’,
: None,
                 ‘model’: ‘Cisco 7937’,
                 ‘class’: None,
                 ‘protocol’: None,
                 ‘protocolSide’: None,
                 ‘callingSearchSpaceName’: None,
                 ‘devicePoolName’: {
                     ‘_value_1’: ‘BE_KNO_DP’,
                     ‘uuid’: ‘{960A36D4-C7ED-49B8-A53C-B188BE30635A}’
                 ‘commonDeviceConfigName’: None,
                 ‘commonPhoneConfigName’: None,
                 ‘networkLocation’: None,
                 ‘locationName’: None,
                 ‘mediaResourceListName’: None,
                 ‘networkHoldMohAudioSourceId’: None,
                 ‘userHoldMohAudioSourceId’: None,
                 ‘automatedAlternateRoutingCssName’: None,
                 ‘aarNeighborhoodName’: None,
                 ‘loadInformation’: None,
                 ‘traceFlag’: None,
                 ‘mlppIndicationStatus’: None,
                 ‘preemption’: None,
                 ‘useTrustedRelayPoint’: None,
                 ‘retryVideoCallAsAudio’: None,
                 ‘securityProfileName’: None,
                 ‘sipProfileName’: None,
                 ‘cgpnTransformationCssName’: None,
                 ‘useDevicePoolCgpnTransformCss’: None,
                 ‘geoLocationName’: None,
                 ‘geoLocationFilterName’: None,
                 ‘sendGeoLocation’: None,
                 ‘numberOfButtons’: None,
                 ‘phoneTemplateName’: None,
                 ‘primaryPhoneName’: None,
                 ‘ringSettingIdleBlfAudibleAlert’: None,
                 ‘ringSettingBusyBlfAudibleAlert’: None,
                 ‘userLocale’: None,
                 ‘networkLocale’: None,
                 ‘idleTimeout’: None,
                 ‘authenticationUrl’: None,
                 ‘directoryUrl’: None,
                 ‘idleUrl’: None,
                 ‘informationUrl’: None,
                 ‘messagesUrl’: None,
                 ‘proxyServerUrl’: None,
                 ‘servicesUrl’: None,
                 ‘softkeyTemplateName’: None,
                 ‘loginUserId’: None,
                 ‘defaultProfileName’: None,
                 ‘enableExtensionMobility’: None,
                 ‘currentProfileName’: None,
                 ‘loginTime’: None,
                 ‘loginDuration’: None,
                 ‘currentConfig’: None,
                 ‘singleButtonBarge’: None,
                 ‘joinAcrossLines’: None,
                 ‘builtInBridgeStatus’: None,
                 ‘callInfoPrivacyStatus’: None,
                 ‘hlogStatus’: None,
                 ‘ownerUserName’: None,
                 ‘ignorePresentationIndicators’: None,
                 ‘packetCaptureMode’: None,
                 ‘packetCaptureDuration’: None,
                 ‘subscribeCallingSearchSpaceName’: None,
                 ‘rerouteCallingSearchSpaceName’: None,
                 ‘allowCtiControlFlag’: None,
                 ‘presenceGroupName’: None,
                 ‘unattendedPort’: None,
                 ‘requireDtmfReception’: None,
                 ‘rfc2833Disabled’: None,
                 ‘certificateOperation’: None,
                 ‘authenticationMode’: None,
                 ‘keySize’: None,
                 ‘keyOrder’: None,
                 ‘ecKeySize’: None,
                 ‘authenticationString’: None,
                 ‘certificateStatus’: None,
                 ‘upgradeFinishTime’: None,
                 ‘deviceMobilityMode’: None,
                 ‘roamingDevicePoolName’: None,
                 ‘remoteDevice’: None,
                 ‘dndOption’: None,
                 ‘dndRingSetting’: None,
                 ‘dndStatus’: None,
                 ‘isActive’: None,
                 ‘isDualMode’: None,
                 ‘mobilityUserIdName’: None,
                 ‘phoneSuite’: None,
                 ‘phoneServiceDisplay’: None,
                 ‘isProtected’: None,
                 ‘mtpRequired’: None,
                 ‘mtpPreferedCodec’: None,
                 ‘dialRulesName’: None,
                 ‘sshUserId’: None,
                 ‘digestUser’: None,
                 ‘outboundCallRollover’: None,
                 ‘hotlineDevice’: None,
                 ‘secureInformationUrl’: None,
                 ‘secureDirectoryUrl’: None,
                 ‘secureMessageUrl’: None,
                 ‘secureServicesUrl’: None,
                 ‘secureAuthenticationUrl’: None,
                 ‘secureIdleUrl’: None,
                 ‘alwaysUsePrimeLine’: None,
                 ‘alwaysUsePrimeLineForVoiceMessage’: None,
                 ‘featureControlPolicy’: None,
                 ‘deviceTrustMode’: None,
                 ‘earlyOfferSupportForVoiceCall’: None,
                 ‘requireThirdPartyRegistration’: None,
                 ‘blockIncomingCallsWhenRoaming’: None,
                 ‘homeNetworkId’: None,
                 ‘AllowPresentationSharingUsingBfcp’: None,
                 ‘confidentialAccess’: None,
                 ‘requireOffPremiseLocation’: None,
                 ‘allowiXApplicableMedia’: None,
                 ‘enableCallRoutingToRdWhenNoneIsActive’: None,
                 ‘ctiid’: None,
                 ‘uuid’: ‘{81F827A6-3B58-F7F0-39BF-DBA51E81B606}’
     ‘sequence’: None

As I mentioned, if you don’t declare you want to have your argument being returned, it will be displayed as None.

Right. Now, you have to use your Python skills to take any action based on your output.
For example, if you want to isolate the returned tags to save them in a variable, you can use a For Loop to do something like that:

And the result will be this:


Now, you can use the Cisco DevNet AXL Schema Reference to explore all possibilities you have.

You can, for example, add new phones, new lines…

Adding Lines

According to the Schema, you don’t have Search Criteria or Returned Tags in the addLine request.
So, the code you be like this:


This is the line we’ve just added:


As I always say…now, sky is the limit!
You can do whatever you want by following the Schema….like add/delete/list Phones, lines, Device Pool, Device Profile, etc, etc, etc…

Hope you liked it Smile

See you!

Whole Code

from zeep import Client
from zeep.cache import SqliteCache
from zeep.transports import Transport
from zeep.exceptions import Fault
from zeep.plugins import HistoryPlugin
from requests import Session
from requests.auth import HTTPBasicAuth
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
from lxml import etree


username = ‘admin’
password = ‘Cisco123’

hostIP = ‘’
location = ‘’.format(host=hostIP)
binding = “{}AXLAPIBinding”
wsdl = ‘file://C:/Users/user123/AppData/Local/Programs/Python/Python38-32/axlsqltoolkit/schema/11.5/AXLAPI.wsdl’

session = Session()
session.verify = False
session.auth = HTTPBasicAuth(username, password)

transport = Transport(cache=SqliteCache(), session=session, timeout=20)
history = HistoryPlugin()
client = Client(wsdl=wsdl, transport=transport, plugins=[history])
service = client.create_service(binding, location)

def show_history():
     for hist in [history.last_sent, history.last_received]:
         print(etree.tostring(hist[“envelope”], encoding=”unicode”, pretty_print=True))

     resp = service.listPhone(searchCriteria={‘name’: ‘SEP0004F2F01F1A’},
                              returnedTags={‘name’: ”, ‘description’: ”,
                                            ‘model’: ”, ‘devicePoolName’: ”})
except Fault:

phone_list = resp[‘return’].phone
for phone in phone_list:

     resp = service.addLine(line={‘pattern’: ‘707080’, ‘usage’: ‘Device’,
                                  ‘description’: ‘Test’, ‘routePartitionName’: ‘ONCLUSTER’})
except Fault:

UCCX – Queries using Python Script

Hey guys,

As promised in my post about ODBC Connection, (you can read it HERE), I’m going to show you how to create a basic Script using Python to query some information from UCCX, which can be useful to create some personalized Dashboards.

Even though we have many types of reports on CUIC, sometime they don’t meet our expectations by having too much unnecessary information or by lack of information.

I’ have decided to use Python, along with HTML, to create my own Dashboard. So I can have only information I know is 100% useful.

First of all, you have to create the ODBC connection to the server where you are going to place the script.
Again, you can use THIS POST to help you out.

Once you have the ODBC Connection working, it’s time to work on your script.

To be able to connect your script to your ODBC, you need to have a PYODBC python Library installed. To be able to better manipulate date and time, I’m also using datetime library.

The first part of the script is used to establish a connection to your ODBC. So you need to fill all its information in the connection strings. It’s important to mention that pyodbc does not even look at the connection string. It is passed directly to the database driver.

To start off my code, I’ll call the libraries and use the command conn = pyodbc.connect to connect to my ODBC.


Connection is now ready!
Now it’s time to choose a query to be sent. That query is sent using SQL commands.
This means you can use your SQL skills to play with queries and create interesting reports


In the below example, I wanted to know how many licenses are being consumed daily.
To do that, I’ll use the SQL command: ” {call sp_license_utilization(‘2021-05-05 00:00:01′,’2021-05-05 23:00:01′,’0′,’1’)}”. The line in the script will be like that:

cursor.execute(” {call sp_license_utilization(‘2021-05-05 00:00:01′,’2021-05-05 23:00:01′,’0′,’1’)}”)

If you print the result, you will see something like that:

[(datetime.datetime(2021, 4, 16, 0, 0, 1), 1, 0, 3), (datetime.datetime(2021, 4, 16, 1, 0, 1), 0, 0, 4), (datetime.datetime(2021, 4, 16, 2, 0, 1), 0, 0, 4), (datetime.datetime(2021, 4, 16, 3, 0, 1), 0, 0, 4), (datetime.datetime(2021, 4, 16, 4, 0, 1), 1, 0, 6), (datetime.datetime(2021, 4, 16, 5, 0, 1), 0, 0, 18), (datetime.datetime(2021, 4, 16, 6, 0, 1), 2, 0, 43), (datetime.datetime(2021, 4, 16, 7, 0, 1), 4, 0, 58), (datetime.datetime(2021, 4, 16, 8, 0, 1), 9, 0, 63), (datetime.datetime(2021, 4, 16, 9, 0, 1), 6, 0, 64), (datetime.datetime(2021, 4, 16, 10, 0, 1), 5, 0, 62), (datetime.datetime(2021, 4, 16, 11, 0, 1), 4, 0, 51), (datetime.datetime(2021, 4, 16, 12, 0, 1), 5, 0, 51), (datetime.datetime(2021, 4, 16, 13, 0, 1), 4, 0, 49), (datetime.datetime(2021, 4, 16, 14, 0, 1), 4, 0, 39), (datetime.datetime(2021, 4, 16, 15, 0, 1), 3, 0, 27), (datetime.datetime(2021, 4, 16, 16, 0, 1), 2, 0, 15), (datetime.datetime(2021, 4, 16, 17, 0, 1), 0, 0, 10), (datetime.datetime(2021, 4, 16, 18, 0, 1), 1, 0, 8), (datetime.datetime(2021, 4, 16, 19, 0, 1), 0, 0, 6), (datetime.datetime(2021, 4, 16, 20, 0, 1), 0, 0, 6), (datetime.datetime(2021, 4, 16, 21, 0, 1), 0, 0, 6), (datetime.datetime(2021, 4, 16, 22, 0, 1), 0, 0, 5), (datetime.datetime(2021, 4, 16, 23, 0, 1), 0, 0, 5)]

Then, use Python to manipulate the results according to your needs. In my case, I’m using the datetime to get today’s date. I also created a list to save the values, as this code will check the license each hour, and give me the maximum as a final result.

The full code for this sample is:



import pyodbc
from datetime import datetime

conn = pyodbc.connect(‘DRIVER={IBM INFORMIX ODBC DRIVER};’
cursor = conn.cursor()

listItem = []
listLicCCX = []
timestampStr =“%Y-%m-%d”)

cursor.execute(” {call sp_license_utilization(‘” + str(timestampStr) + ” 00:00:01′,'” + str(timestampStr) + ” 23:59:59′,’0′,’1’)}”)
rows = cursor.fetchall()
LicenseUsage = rows
for hourly in LicenseUsage:
if hourly[3] != None:
listItem.insert(0, hourly[3])
listItem.insert(0, 0)
except pyodbc.Error as ex:
print(“An exception occurred”)
listItem.insert(0, 0)
listLicCCX.insert(0, (max(listItem)))


And this is the final result:


Remember you can use any SQL Query!

For example, this is the SQL query to get a list of Agents by Team:select s.resourceLoginID,s.resourceFirstName,s.resourceLastName,s.extension, t.teamname from Resource s inner join team t on s.assignedTeamID = t.teamid where = ‘t’ and = ‘t’ and t.teamname = ‘UCCX_TEAM’ order by t.teamname, s.resourceloginid

Using a simple Select * from rtcsqssummary here csqname = ‘<CSQ Name>’ query you can display more information as this query will return the following information.


The sky is the limit!


Now that you now how to use SQL queries in Python, you can start creating your own script!