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!


Azure Files – Part 2 – Creating a SMB Share


Hello everyone! As promised in the first post about Azure File, today I will demonstrate how to create an Azure Files SMB share. However, first it is necessary to say that when we implement SMB shares with Azure, there are 2 basic scenarios. The first would be server to server and/or applications, in which case you can use standard admin account and access key. If you wanted to use your Active Directory domain identity with Azure Files, you will need to extend your domain to Azure (You can do this in 2 ways), that is, basically add the domain service in the Microsoft cloud, only in this one scenario you can integrate your storage account with identities, your users can each use their own domain account to use their file access privileges.

That said, let’s get down to the minimum requirements for using Azure Files on Windows machines (MacOS and Linux are also supported, but they’re not in the scope of this post).


Let’s get started!

Initially, to create an Azure Files you need to create a new Storage Account, because if you try to search Azure Files when creating a new resource, you will notice that nothing will be found.


Of course you can use an existing Storage Account, but for this post I will create a new storage account.

The important steps here is to create a resource group and the storage account itself, everything else you can customize according to your need or leave everything as default (If your don’t know how to create a Storage Account go to this post).


Hit ‘Review + Create’ and within 2 or 3 minutes you will have everything you need to create your Azure Files, Then click go to the resource. Once you have your new storage account open, hit the ‘File Share’ blade in the vertical menu on the left side.


 Just for observation, at the top of the screen above it says that the Active Directory is not configured, i.e. in this scenario I could not use the identity service without first enabling the domain service in Azure.

Continuing with our configuration, hit ‘+ File Share’, type the name, put the amount of GiB needed then select the access tier needed. For this post I selected the cheapest for demonstration purposes, but you must select it according to your need (You can access here the Microsoft link explaining about each tier and pricing).


Now that the share has been created, navigate to the one we just created and you can see that there aren’t many options here. The main option is the ‘Connect’ option.

Hit the ‘Connect’ option and you will see that Azure will provide a script for Windows, Linux and MacOS.

Basically you will need to choose which operating system you will have the driver mapped, the driver letter (For Windows OS only) and which authentication method will be used.


To finish the only thing to do is run this PowerShell script on the machine where you want to have the driver mapped, with the only requirement being port 445 open for communication with Azure. In this script provided by azure, it already contains the account and password to access the resource, and at the time of execution of the script there is no need to elevate your PowerShell session.


The result I hope after executing the script will be the driver mapped as in the example below.


It is also possible to add the mapping manually, you just need to follow the following steps.

1 – On the machine you want to map the drive, open Windows Explorer and hit ‘This PC’, then right click on the white space and select Add network location, after that hit ‘Next’ twice and you will end up to a windows that you need to specify the address for the location you want to add.

2-Go back to Azure Files on Azure portal and hit ‘Properties’.

3- Copy URL without the HTTPS and paste on your Windows Explorer screen, but don’t forget to add the ‘\\’ and also change all others for back slash ‘\’.  The result should be this:


4- Hit ‘Next’ and give a name for your network location, and hit ‘Next’ again.

5- Finally, it should ask for the user name and password to access the network location, so to grab that you need to go to Azure Portal again and grab it from your Azure Files Storage Account.


The credentials accessing format will be:

User: localhost\StorageAccountName

Password: StorageAccountAccessKey

Now you will be able to put your files these will be automatically synced to the cloud or your on-premises environment (Depending on where you create the file).


I would also like to demonstrate the features of Azure Files snapshot and how the backup works, but this post is already too big, for this reason I will reserve these subjects for the next ones. If you have any questions, leave in the comments, see you soon!

Joao Costa

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!!


Az-Predictor – How to install and use it

In the day-to-day work in IT, we often need to deal with manipulation of massive resources, and often this task ends up becoming difficult, especially if you are not very familiar with PowerShell cmdlets, after all the PowerShell has approximately 4000 cmdlets. Well, thinking about it, the Microsoft team created this incredible tool that is undoubtedly here to stay, Az-Predictor.

What is Az-Predictor?

Az-Predictor, an intelligent command completion module for Azure PowerShell. Az-Predictor helps our Azure developers/administrators find the cmdlet they are looking for efficiently, identify the required parameters quickly, and experience fewer errors

Of course it is possible to use the auto-complete feature already built into the PowerShell (by pressing the TAB), so that the command is automatically completed by the PowerShell, but Az-Predictor is a few steps beyond that, as the tool will not only complete the cmdlet , but also suggest all the parameters that need to be configured, ie Az-Predictor will basically do all the work based on prediction using all the documentation pages already available and the user leverage in PowerShell.

The idea behind this amazing tool is to assist in the correct syntax of commands and parameters based on AI prediction suggesting all commands to be used, including letting you use historical cmdlets, ie cmdlets already used previously.

Getting started with preview 3 (Keep in mind that at the time of this post we are talking about a preview version, so by the time you read this post there may already be a new update or a different way to install the product.)

  • If you have installed the first preview:
  • Close all your PowerShell sessions
  • Remove the Az.Tools.Predictor module

Install PowerShell 7.2-preview 3 – Go to:

Select the binary that corresponds to your platform in the assets list.

Launch PowerShell 7.2-preview 3 and Install PSReadline 2.2 beta 2 with the following:

Install-Module -Name PSReadLine -RequiredVersion 2.2.0-beta2 -AllowPrerelease

More details about PSReadline:

Install Az.Tools.Predictor preview 3

Install-module -name Az.Tools.Predictor -RequiredVersion 0.3.0

More details about Az.Tools.Predictor:

Enable Az Predictor

Enable-AzPredictor –AllSession (This command will enable Az-Predictor in all further sessions of the current user.)

Enable the list view mode (Optional) – Believe me, you want to enable this!

Set-PSReadLineOption -PredictionViewStyle ListView

If you want to load Az-Predictor every time you start PowerShell (and trust me you want that ), you can add the last three commands to your PowerShell profile.


OK, now that we know how to make this amazing tool work, let’s have some fun.

Start by typing known commands and see how Az-Predictor starts trying to predict what you want to achieve based on your history and documentation already available. It’s pretty cool!

See the example below, I just typed “set” and see how many possibilities Az-Predictor “suggested”, cool isn’t it?


If you use the up and down arrow keys on the keyboard you can choose the command you want to use as if you have a drop down menu,

If you use the up and down arrow keys or you can choose the cmdlet you want to use as if you have a drop down menu, also if you use the right arrow your cmdlet will be completed by Az-Predictor based on the cmdlet that you “selected”, and finally, if you want/need to change parameters of the cmdlets presented, press Alt+A and you start to select each changeable parameter of the command.

See in the command below what happens if I press Alt+A


You can explore at will and try many other possibilities. I hope you enjoyed it, see you soon!

Joao Costa

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!


Creating a storage on Azure


Today I’m going to show you how to create a storage in the Microsoft Azure portal. So straight to the point, let’s get start: First log on your Azure Portal, next go to the “Search Bar” and type “Storage Accounts“, after that select Storage Accounts and finally click “Create“.


Now let’s add the necessary information for each Storage, remembering which organization will have Storages according to your needs. I will detail each configuration (Required ones):

Basics tab

  • Subscription – Select the subscription for the new storage account.
  • Resource group – Create a new resource group for this storage account, or select an existing one. For more information, see Resource groups.
  • Storage account name – Choose a unique name for your storage account. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.
  • Region – Select the appropriate region for your storage account. Not all regions are supported for all types of storage accounts or redundancy configurations.
  • Performance – Select Standard performance for general-purpose v2 storage accounts (default). This type of account is recommended by Microsoft for most scenarios. Select Premium for scenarios requiring low latency. After selecting Premium, select the type of premium storage account to create. The following types of premium storage accounts are available:
  • Redundancy – Select your desired redundancy configuration. Not all redundancy options are available for all types of storage accounts in all regions. If you select a geo-redundant configuration (GRS or GZRS), your data is replicated to a data center in a different region. For read access to data in the secondary region, select Make read access to data available in the event of regional unavailability.
  • Advanced tab

    Networking tab
    • Connectivity method – By default, incoming network traffic is routed to the public endpoint for your storage account. You can specify that traffic must be routed to the public endpoint through an Azure virtual network. You can also configure private endpoints for your storage account. For more information, see Use private endpoints for Azure Storage.
    • Routing preference – The network routing preference specifies how network traffic is routed to the public endpoint of your storage account from clients over the internet. By default, a new storage account uses Microsoft network routing. You can also choose to route network traffic through the POP closest to the storage account, which may lower networking costs. For more information, see Network routing preference for Azure Storage.

    Then click Create.


    After creation check it in your Storage accounts and by clicking on settings you can see all the parameters used in the Storage settings.


    Thanks guys and see you on the next post!

    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


    How to authenticate AzCopy on Azure

    AzCopy should now be downloaded to your computer (If you don’t know how to do this, go back to the last post here). But before you can perform any tasks, it is necessary to authenticate to your Azure subscription to access Azure Storage first.

    There are two ways to authenticate AzCopy to your Azure storage accounts – Azure Active Directory or by a Shared Access Signature (SAS) token. In this article, we’ll focus on using Azure AD.

    The most common method to authenticate AzCopy is via Azure AD. When using Azure AD, you have several options. Some of these options are:

    • Interactive Login – User is prompted to log in using the browser.
    • Service Principal + password – For non-interactive login. Recommended for automation and scripting.
    • Service Principal + certificate – For non-interactive login. Recommended for automation and scripting.

    In this article, you will learn how to authenticate via interactive login. To do so, first, open a command prompt or PowerShell and run the below command. The –tenant-id parameter is optional but recommended, especially if your login account is associated with more than one Azure tenant.


    Once executed, you will be asked to open a browser and navigate to and enter the displayed code. You can see what that will look like below.

    05Enter the code from AzCopy into the browser

    Once you’ve entered the code into the browser, click Next and proceed to sign in to your account.


    When sign-in is done, you should see the status shown in the browser and in the terminal similar to what’s shown in the screenshot below.


    Now that you have all this knowledge, you should now be ready to put AzCopy in action! See you soon folks!

    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: