In Cisco’s Unified Communication ecosystem, Unified Contact Center Express (UCCX) and Cisco Unified Communications Manager (CUCM) are critical systems. While these two systems often coexist in the same environment and are tightly coupled in terms of function, it is surprisingly not that easy or straightforward to make them talk to each other at the scripting level. This is because they rely on fundamentally different API paradigms:
- UCCX provides a REST API with both JSON and XML compatible payloads, adhering to modern standards of web service design.
- CUCM, by contrast, only offers a SOAP-based AXL API, a more traditional XML-based protocol.
This technological mismatch presents a critical integration challenge, especially when we are trying to control operations like the following in CUCM through a UCCX based IVR system:
- Making routing changes in CUCM
- Updating user configurations
- Updating device configurations
So, how do we fix this ? That’s exactly what we are going to discuss in the rest of the article.
- Why a Middleware?
- Use Case
- Solution Workflow
- UCCX Script
- Django Python Code
- Github Repo
- YouTube Demo
- Final Thoughts
Why a Middleware?
One of the solutions to overcome the challenge we highlighted earlier, is to use Java to code SOAP functionality in the UCCX script so that it has a way to communicate in the SOAP language. The other viable option is to code a “middleware application” in some other language. A middleware application can basically act as a translator between the two systems:
- On one side, it accepts REST calls from UCCX or any other system for that matter.
- On the other, it performs SOAP requests to CUCM or any other SOAP-only system, and then converts the responses back into REST-compatible JSON.
This middleware becomes the glue layer, decoupling the API logic while ensuring the business logic remains tightly integrated.
There are multiple options & frameworks like Django and Flask to build such a middleware application. I prefer to use Django framework since that’s what I have experience on. This app will act as a gateway that allows UCCX to retrieve (GET) and update (PUT) user information from CUCM without needing any native SOAP handling on the UCCX side.
Use Case
We want to have a solution that does the following two tasks. Both these tasks need to be done via self-service UCCX IVR. The idea is that end users should be able to call a DID and get these details without opening a ticket with the service desk or the voice operations team.
- GET – Get extension detail from a device associated with an End User account in CUCM.
- PUT – Reset the extension mobility pin.
Since the objective of this post is only to demonstrate what can be acheived through such middleware applications, I have kept the explanation to only two REST methods here – GET and PUT. You can expand it further & customize the application to add POST and DELETE as well based on your own use case.
Solution Workflow
The high level workflow is shown below followed by details on the actions performed within the middleware application.

- UCCX Initiates REST API Call
- Django Middleware Application:
- Processes the Request and parses path and payload
- Maps endpoint to the corresponding SOAP method
- Constructs SOAP XML requests.
- Sends SOAP XML request to CUCM’s AXL endpoint
- Parses the response back from CUCM
- Converts it to JSON format and sends it to UCCX
Source Database
In the initial stage, the UCCX script has to validate whether the “Employee ID” entered by the caller is valid or not. There are multiple ways to go about it. You could have an
- API integration between UCCX and some sort of HR Database which UCCX will dip into to capture the details.
- Integration with Active Directory like Azure AD to do the same etc.
In this particular demonstration, I’ve created a simple JSON file with following details. This will act as the “source database” from where the script will fetch relevant details and also validate whether the “Employee ID” entered by the caller is correct or not.
In a real production environment though, you will definitely need to integrate it with some sort of AD or HR Database that has much more employee details. If you have worked in UCCX before then that wouldn’t be a challenge or you can use REST integration with these third party systems (if available) from within the UCCX script. I won’t drag this any further and leave this particular discussion on the “Source Database” here. I hope you get the gist!!
If you need any specific clarifications or assistance on that specific type of integration then please feel free to reach out to me directly.
{
"employees": [
{ "empid": "48291", "firstName": "Alice", "lastName": "Morgan", "userid": "amorgan" },
{ "empid": "15937", "firstName": "Brian", "lastName": "Thompson", "userid": "bthompson" },
{ "empid": "30482", "firstName": "Carla", "lastName": "Jenkins", "userid": "cjenkins" },
{ "empid": "92713", "firstName": "David", "lastName": "Kim", "userid": "dkim" },
{ "empid": "67845", "firstName": "Elena", "lastName": "Singh", "userid": "esingh" },
{ "empid": "51389", "firstName": "Frank", "lastName": "Zhao", "userid": "fzhao" },
{ "empid": "76420", "firstName": "Grace", "lastName": "Lee", "userid": "glee" },
{ "empid": "84029", "firstName": "Hassan", "lastName": "Ahmed", "userid": "hahmed" },
{ "empid": "23714", "firstName": "Ivy", "lastName": "Patel", "userid": "ipatel" },
{ "empid": "39102", "firstName": "Jake", "lastName": "Sullivan", "userid": "jsullivan" }
]
}
UCCX Script
- We start off by capturing the “Employee ID” from the caller.

- We then load the source JSON document with employee details and store relevant values in the different variables. In a production environment, this is where you will be utilizing the internal database or some sort of APi communication to talk to the backend employee DB application.

- We then give 2 options to the caller – One is to capture their extension details and the second is to reset their extension mobility pin. Both these options send their own API request to the Django web server

- Here is a glimpse of the GET REST request.

Django Python Code
Define REST Endpoints
The views.py file:
from rest_framework.response import Response
from rest_framework import status
from .soap import get_User_Details, update_User_Details
import logging
logger = logging.getLogger(__name__)
@api_view(['GET'])
def get_User_View(request):
try:
userid = request.query_params.get('userid')
result = get_User_Details(userid)
return Response({"extension" : result}, status=status.HTTP_200_OK)
except Exception as e:
return Response(
{"status": "error", "message": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['PUT'])
def update_User_View(request):
raw_data = request.body.decode('utf-8') # Decoding from bytes to string
result = update_User_Details(request.data)
return Response(
{"status": "success", "axl_response": str(result)},
status=status.HTTP_200_OK
)
URL Routing
The urls.py file:
from django.contrib import admin
from django.urls import path
from translator.views import get_User_View, update_User_View
urlpatterns = [
path('admin/', admin.site.urls),
path('get-user/', get_User_View, name='get_user'),
path('update-user/', update_User_View , name='update_user')
]
SOAP Client with Zeep
The soap.py file:
from zeep import Client, Settings
from zeep.transports import Transport
from requests.auth import HTTPBasicAuth
import requests, os, logging
from requests import Session
logger = logging.getLogger(__name__)
host = "<Your CUCM IP>"
CUCM_WSDL_PATH = r"<Enter the path to the WSDL file"
CUCM_LOCATION = f"https://{host}:8443/axl/"
CUCM_USERNAME ="<Use your CUCM AXL username>"
CUCM_PASSWORD = "<Use your CUCM AXL password>"
binding = "{http://www.cisco.com/AXLAPIService/}AXLAPIBinding"
def get_User_Details(userid):
try:
session = Session()
session.verify = False
session.auth = HTTPBasicAuth(CUCM_USERNAME, CUCM_PASSWORD)
transport = Transport(session=session, timeout=20)
client = Client(wsdl=CUCM_WSDL_PATH, transport=transport)
service = client.create_service(binding, CUCM_LOCATION)
resp = service.getUser(userid=userid, returnedTags={'primaryExtension': {'pattern': ''}})
print(resp['return']['user']['primaryExtension']['pattern'])
return resp['return']['user']['primaryExtension']['pattern']
except Exception as e:
logger.error(f"Error fetching user {userid}: {e}", exc_info=True)
return {
"status": "error",
"message": str(e)
}
def update_User_Details(data):
session = Session()
session.verify = False
session.auth = HTTPBasicAuth(CUCM_USERNAME, CUCM_PASSWORD)
transport = Transport(session=session, timeout=20)
client = Client(wsdl=CUCM_WSDL_PATH, transport=transport)
service = client.create_service(binding, CUCM_LOCATION)
userid = data['userid']
pin = data['newPin']
resp = service.updateUser(userid=userid, pin=pin)
print(resp)
return resp
App Settings
The settings.py file:
Add the name of the webapp in the “Installed_Apps” section. In our case, the name of the app is “translator”. It would look like this
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'translator',
]
Github Repo
The Github repo is linked below. Please go through its own detailed Read-Me file to understand what all needs to be in place in your environment to make this setup work.
https://github.com/simranjit-uc/CUCM-UCCX-Automation.git
YouTube Demo
The video showing the demo of this application with more extensive details is given below. You can check it out and also subscribe to the channel, if you wish. I’ll be adding more video tutorials and lectures in coming weeks.
Final Thoughts
Cisco UCCX and CUCM are both powerful tools but integration limitations can hinder automation. This Django-based middleware fills that gap, allowing REST-first applications (like UCCX or any other tool) to talk to CUCM’s SOAP-based infrastructure without needing to rearchitect anything.
This kind of decoupling also sets the stage for future agentic automation and self-service portals where bots or automated workflows can trigger CUCM changes via REST calls.
I hope this helps you in your own automation journey and spark new ideas in you. Please feel free to share your feedback/suggestions, if any.
Until then, Happy Learnings!!
