Bridging UCCX and CUCM: Building a Django Middleware for REST-SOAP Integration

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.

  1. Why a Middleware?
  2. Use Case
  3. Solution Workflow
    1. Source Database
  4. UCCX Script
  5. Django Python Code
    1. Define REST Endpoints
    2. URL Routing
    3. SOAP Client with Zeep
    4. App Settings
  6. Github Repo
  7. YouTube Demo
  8. 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.

Fig 1 : UCCX to CUCM API integration workflow

  • 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.
Capturing "Employee ID" in UCCX
Fig 2 : Capturing “Employee ID” in UCCX
  • 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.
Loading source JSON DB document
Fig 3 : Loading source JSON DB document
  • 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
Offering Menu options and forming REST requests
Fig 4 : Offering Menu options and forming REST requests
  • Here is a glimpse of the GET REST request.
Sample GET REST API request
Fig 5 : Sample GET REST API 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!!

Let’s connect on LinkedIn