Showing results for 
Search instead for 
Did you mean: 

[UCCX] Enhancing the Prompt Substitution Operator


The Problem

In a UCCX script, there exists a nice Prompt operator called the Prompt Substitution Operator.  Its intended purpose, as I read it to be, is to attempt to queue a Prompt for playback, and if that fails, then attempt to queue a second Prompt for playback instead.

The example given in the documentation is as follows:

For example the main prompt could represent a TTS prompt which in cases where the system has not be en installed or licensed with TTS support, one would want to fallback to a pre-recorded prompt. In this case, queuing a TTS prompt would fail and the substitute would be used instead.


The problem is that the substitution only happens for very specific circumstances, one of which is Prompt Not Found.  However, what about prompts that meet other criteria, such as: Prompt length too short?  As is the case when silencing a prompt from a Prompt Management script.

Which, if you didn't know, at the time of this writing, there is no only one way (REST API) to delete a Prompt from a script, causing a Prompt Not Found exception.  The API was introduced in UCCX 10.0.  Therefore, unless you're on UCCX 10.0, the only option we have to us is to overwrite the Prompt, which in my case, I do with the smallest possible valid WAV file UCCX will play.  I have written about that special file before, if you wanted to learn more.  You may choose to do it another way, such as pressing the mute button on the recording phone, and then terminating the recording step with # (pound/hash/octothorpe) to create a short and silent file.

And there may be other criteria you are interested in, but I am mainly interested in: Prompt Not Found and Prompt Length Too Short.

The Solution

The solution is to use something called a Prompt Template.  A Prompt Template is basically a text file full of Java code (Enhanced, Premium and IP-IVR only) that evaluates to either a Prompt directly, or to some other type that can be converted into a Prompt.  E.g., A Document.

Here is my Prompt Template, which I use, to replace the Prompt Substituion Operator, enhancing my scripts to also handle Prompt Content Length Too Short exceptions.

Prompt Template File: play.tpl (Attached as

/* ==========================================================================

   This Prompt Template will validate if a requested Prompt to be played is

   both found in the repository and does not have a content length equal to

   the silence is golden Prompt.

   The silence is golden Prompt is the smallest possible g711ulaw wav file

   supported by UCCX with a payload size of 0, for a total file size of 56B.

   The template takes two arguments:

       1. A requested Prompt to be played back

       2. A default Prompt to play if the requested Prompt is not valid

   ========================================================================== */

(Prompt requested_prompt, Prompt default_prompt) {

    // Initialize the local variable which will hold the content length of

    // the silence is golden wav file

    long silence_is_golden_length = 56L;

    // Initialize the local variable which will hold the contents of the

    // requested Prompt.

    Document cache = DOC[];

    // Initialize the local vairable which will hold the content lenthd of

    // the requested Prompt.

    long cache_length = 0L;

    // Initialize the local variable which will hold the resulting Prompt

    // from the outcome of this function.

    Prompt given_prompt = P[];

    // Attempt to work with the requested Prompt as if it were valid.

    // If an Exception is thrown, catch it, then use the default Prompt.

    try {

        // Casting the requested Prompt to a Document is a way to check if

        // the Prompt exists within the Prompt repository

        cache = (Document) requested_prompt;

        // At this point, the Prompt exists

        // Get its content length value.

        cache_length = cache.getContentLength();

        // Compare its content length value to that of silence is golden.

        if (cache_length > silence_is_golden_length) {

            // Its content length is greater than that of silence is golden.

            // We'll use the requested Prompt.

            given_prompt = requested_prompt;

        } else {

            // Its content length is less than or equal to that of silence

            // is golden.  We'll us the default Prompt.

            given_prompt = default_prompt;


    } catch (Exception e) {

        // Something went wrong reading the requested Prompt.

        // Use the default Prompt.

        given_prompt = default_prompt;


    // Return the outcome of this function to the script.

    return given_prompt;


The Sub Problem

The reason I have attached it as a ZIP archive is an interesting one.  If you were to try an upload the TPL file directly into UCCX's Prompt Management Web Interface, you will be met with an error message like the following:


Why are you scolded for trying to upload a perfectly valid file to the repository?  Well, here's the JavaScript embedded in the page that decides to shame you in front of your cube mates:

if( opener.document.form1.promptFolders.value != 'Root Folder' && (pFileExt!= '.wav' && pFileExt!='.zip' )) {

     alert('File name does not have a valid extension. Only .wav files can be uploaded');


     return false;


Now we can see that the only valid extensions are .wav and .zip, despite there being three other valid extensions as described in documentation.

The extension of the prompt file can be omitted in which case the search looks at all supported extensions (<.wav>, <.ssml>, <.tts>, <.tpl>) in order until one is found.


The Sub Solution

I have actually addressed this issue with a working solution on UCCX version 7x.  I don't recall where, why, when, who, or how I was prompted to solve this problem, but the solution I had at the time was to modify the client side JavaScript as can be seen in a YouTube video I made of the process.

There is actually a much simpler solution though, one that I overlooked two years ago as I created that video.  As Occam's razor would have it, this solution is one I would recommend to you.

Are you ready?  It's so simple.  You may have already guessed it.

You ZIP the TPL file, and then upload the ZIP file.  Simple right?  I know, I slapped myself on the forehead when I realized it.

EDIT: I just discovered another way.  You use the Upload Prompt step within a script to upload the TPL to the repository.  I'd still recommend the ZIP file option, as there is still a challenge with getting your TPL file contents into the script in the first place.  One option is to save it in the Document repository first, then upload it with the Upload Prompt step to the Prompt repository.  The Document repository does not prohibit the TPL file extension.

Back to the Solution

Ok, now that we have that out of the way, here is how you use the Prompt Template in your script.  Actually, before I show you that, let's take a look at how the Prompt Substitution Operator looks.


If you hadn't seen this syntax before, it basically means: "try to play first_choice.wav and if you can't then play second_choice.wav"

It's actually quite nice.  Think about a retail chain of 150 stores where there's a default set of prompts for all stores, but if a store chooses to be more specific or stray from the norm, then they can simply upload their store prompt as first_choice.wav.  For those stores who wish to use the default, they simply do nothing and allow the Prompt Sub'ing to work its magic.

But again, with a Prompt Management solution built on solid UCCX scripting, we cannot delete prompts, only overwrite them.  Here is how you use the Prompt Template I provided to account for the Prompt Content Too Short scenario.


Let's break it down...

Play Prompt

You should already know that this is the Step name.  Nothing special here.

--Triggering Contact--

You may even know this is the default reference to the Contact created for the caller.  It could be something different, but it's not messed with very often outside of Call Back scripts and Trigger Application steps.

P[String, Prompt, Prompt]

This is where it gets tricky.  The P[] on the outside is the User Prompt literal syntax.  Inside of it are three arguments:

  1. The first one is the Prompt Template Name.
  2. The second is your first choice Prompt
  3. The third is your second choice Prompt


Your Prompt Template Name.  I named my Prompt Template "play.tpl"  If you rename yours or create others, be sure to change this value to reflect the real name of the file.  File extensions are optional as noted above.


This is just a User Prompt literal.  It could be a System Prompt (SP[welcome.wav]) a TTS Prompt (TTS[Hello CSC]) or anything else Playable.  This works the same as the Prompt Substitution Operator.


This is exactly the same as the first choice prompt, and works the same here as it does in the Prompt Substitution Operator.

The Conclusion

Prompt Templates are a powerful tool for abstracting the Prompt selection process from the script.  It keeps your script code short and clean, while providing powerful, reusable code for your whole system.

The Challenge

I challenge the community to create an even better Prompt Substitution Operator replacement out of Prompt Templates.  What about wrong encoding of the WAV file given the System Parameter setting?  I.e., UCCX is set to G729 and the Prompt is G711.

I challenge Cisco to the following:

  1. Enhance the Prompt Substitution Operator to include handling of more Exceptions
  2. Enhance the web interface to allow uploading of all five Prompt file extensions: .zip, .wav, .tpl, ssml, and .tts
  3. Enhance the Documentation around Prompt Templates.  It took me about 4 hours and a lot of trial and error to figure this out.
Gergely Szabo

Hmm, that's pretty cool. Excellent article (as usual) ;-)


Anthony Holloway
Cisco Employee

Thanks!  I'm not quite satisified with my documentation, and I'm evolving my writing style each time I make a new document.

Ryan LaFountain
Cisco Employee

Challenge accepted!

Prompt Templates – I've filed CSCum35585 on AppAdmin to allow for .tpl, .ssml and .tts files to be uploaded to the Prompt Repository. Code changes have already been committed against the defect to allow for these types of files to be uploaded, no testing or further discussion yet. Provided all is well, it'll go into 10.5(1) and 10.0(1)SU1. If there is a strong case for backporting it into 9.x, unicast me and we can look into it.

Prompt Substitution on UnsupportedPromptException – I started writing a bug on this using your use case to allow for the prompt substitution to operate when an UnsupportedPromptException was thrown, but as I typed up the use case, I was thinking the better solution would be to allow for deletion of prompts from a script. This seems to be the crux of the issue. You want a management script to manage these per-store prompts, but the current functionality doesn't allow you to remove prompts, with no way of forcing the PromptNotFoundException and the substitution in the playback script. This causes you to then replace/overwrite with this 56b file which causes the UnsupportedPromptException and the issue with the Prompt Substitution operator.

So, if you fixed the capability to remove prompts from the repository via a script, that should solve the use case right? This should also allow for other functionality that can be gained and less trickery to get around some inadequacy. 

I then went looking in the 10.0 REST API guide.

In UCCX 10.0, we expose the ability to manage repository items from the REST configuration API. One of these API functions is to Delete Prompt files/folders.

Method: DELETE

URL: http://uccx-server/adminapi/prompt/<path>/<filename>.wav

The way this helps out is that you can call this from the Make REST Call step from the script back to UCCX.

Your use case can now be accomplished as follows:

1.Playback script is Play Prompt (--Triggering Contact, P[<storenum>.wav] ||| P[Default.wav])...Ex: Play Prompt (--Triggering Contact, P[1125.wav] ||| P[Default.wav])

2. Calling the playback script for store 1125 results in the Default.wav being played because 1125.wav throws PromptNotFound.

3. Store 1125 wants to record a special prompt for the holidays.

4. Store 1125 calls into prompt management script and records a prompt, naming it 1125.wav.

5. Calling the playback script for store 1125 results in the 1125.wav file being played.

6. Store 1125 wants to remove this special prompt after the holidays.

7. The prompt management script gives the option to remove a prompt instead of overwriting. This branch calls the Make REST Call step using the method and URL above.

8. Calling the playback script for store 1125 results in the Default.wav file being played because 1125.wav throws PromptNotFound.

I haven't thought through the other reasons you may want the substitution to occur for the UnsupportedPromptException. Do you have any other use cases? What do you think of the above? This doesn't help you for non-10.0 customers, but this may be a solution instead of rejigging the conditions under which the substitution occurs or putting in another step to remove prompts through a script (the opposite of Upload Prompt step).

Rising star

Very cool work!! I was kind of aware that Prompt Templates existed, but I have never tried to use one and never knew of a good use case.

I have solved similar customer needs by [ab]using sub-languages. Many people know UCCX will search something like en_US, then en, then default for a Document or Prompt. You can actually add another level on top of that. We have mainly used this to support situations where we want more than one "voice talent", either permanently or in transition from one to another. It comes in especially handy when we want to have them record updated system prompts like numbers and ordinals and dates.

If you set your Language to "en_US_Alice", your search path looks something like this:

  1. en_US_Alice
  2. en_US
  3. en
  4. Default

I'm not sure how many extra levels it will search; I've only had a need for one. It has the advantage of keeping your scripts very simple. All you have to do is set your Language, either at the Trigger level or using Set Contact Info in the script.

Taking the example of your 150 store retail situation, if you have a prompt "mainmenu.wav" in en_US_Store5039, that gets played. Otherwise we play it from en_US or whatever. This affects Documents as well, so if you have XML config files that might vary by store, you have default/override options.

Prompt nulling or Prompt Templates are probably better solutions if you need to automate deletion or inactivation of a prompt from a script. However, the new REST API call for deletion might be useful.

Anthony Holloway
Cisco Employee

Thanks for the feedback jasyoung!

The prompt substitution solution is mainly just to work around the fact that a Prompt Substitution Operator only works for PromptNotFound exceptions.  My scripted solution can check boolean logic for any and all conditions about the prompt, to include throwing in some extra conditionals such as: prompt length, time of day, day of week, etc.

Now that the API exists for deleting prompts, I'm happy that the Prompt Substitution Operator will be able to handle it, however, why not just keep using the prompt template?  It also handles the PromptNotFound exception, and so much more!

Rising star

I think the Prompt Template code above is a fantastic demo of how the feature works. As you say, you could go nuts in there with any conditional you have available in the script. It's a definite bookmark for me.

For the particular business case you talked about (mostly-identical bulk sites) I think the Language feature provides a nice set of tools to do that in a simple, well-organized and self-explanatory way. I think the Prompt Template feature is a big hammer to use on that particular nail, and Languages are beneficial for code other than Play Prompt. In addition to Documents, you could use DTMF-only Grammars to tweak menu options and ordering at individual sites. However, that's definitely my artistic opinion and not objective fact.

I like your cast-to-Document method for measuring prompt length. I've wanted to be able to measure prompt length in the past and couldn't find a good way to do it. I also like your 56-byte BIN[] null Prompt, but shouldn't DP[0] be functionally identical? I don't have a UCCX system available at the moment to test.

Anthony Holloway
Cisco Employee

Agreed.  Sub languages is a great option as well; especially for Documents et al.

The 56B file is actually a valid WAV RIFF file header, with 0 bytes of payload.  If you simply did BIN[00] (it has to be at least one byte) it would be an invalid WAV file.  Effectively, they should be the same, except that the 56B file I created will not fail a media step. Granted, the default setting is "Continue On Prompt Errors", but at least now you don't have to compromise that feature with my solution.  With the BIN[00] you would.

I appreciate the back and forth discussion, thanks for commenting.

Rising star

Sure, BIN[00] would not be valid for playback, but I was referring to Delay Prompt objects. DP[milliseconds] produces a playable silent prompt of the requested length. I assume that DP[0] is valid but I have not tried it. Unfortunately I have been stuck on a year-long project with a customer that doesn't have UCCX and my lab isn't up at the moment.

Anyway, I don't mean to detract from your fine work here, just chatting.

Anthony Holloway
Cisco Employee

Please accept my apology jaysyoung.  I missread the DP[0], being in the same sentence as BIN[], as being BIN[0].

Technically, DP[0] should work, since the Engine plays it without error, but in reality it creates an empty wav file once saved and a prompt exception is thrown.


However, if you do a DP[1], it does create a valid wav file, unfortunately it's 852B.  So far my 56B file is the smallest valid wav file possible.  Of course, I'm not going to argue that a 1 millisecond silent file is way too large to put into production.  That would be silly.  I'm just in it for the science!

I appreciate our chat.  Reply anytime.

Recognize Your Peers
Content for Community-Ad