Routing an IVR in FusionPBX
I'm not going to lie. I found the process of getting an external number to route to an interactive voice response (aka IVR, aka "auto attendant") menu was really really frustrating. It is supposed to be the simplest thing in FusionPBX, but I banged my head against it for tens of hours.
The version of FusionPBX is still 3.7.1 running on Debian Wheezy.
Here is what I have learned the hard way.
IVRs and IVR Menus
FreeSWITCH has the concept of an "IVR" and a concept of an "IVR Menu". These are not the same thing. An IVR is a program. It can do anything, but most commonly it contains the logic about how to read telephone button presses, transfer extensions, and so on.
An "IVR Menu" is a special kind of configuration file that contains settings to use for a particular organization: what prompts should play, what should happen when users press numbers, and so on. It does NOT contain logic about how to actually process button presses.
FreeSWITCH comes with an IVR called the "demo IVR", which is
associated with extension 5000. It is written as a series of XML
commands. For FusionPBX, ignore this file. FusionPBX does not use
it, and you should probably not try to create your own IVR (in XML or
Javascript or anything else). Instead, FusionPBX comes with its own
IVR program written in Lua (on my system, this is in
/usr/local/freeswitch/scripts/ivr_menu.lua
(aka
scripts/ivr_menu.lua
).
The next lesson is that you never call the ivr command directly in FusionPBX. In FreeSWITCH you need to make a dialplan entry that calls an IVR. FusionPBX handles this for you (in a confusing way).
FusionPBX has some GUI to help you make IVR menus. Since FusionPBX
3.3, the project went off the rails and implemented the IVR menu
functionality completely differently from standard FreeSWITCH. In
standard FreeSWITCH, you add XML files to the conf/ivr_menus
folder
to indicate which IVR menus exist, and use a command called ivr
in
your incoming routes to call this IVR. This does not apply in
FusionPBX. Instead, IVR menus are stored in the FusionPBX database,
and the ivr_menu.lua
script queries the database dynamically, as
documented here:
https://code.google.com/p/fusionpbx/issues/detail?id=474&can=1&q=ivr_menu
.
If you try to make a dialplan that uses the ivr
command you will end
up with errors like the following in the FreeSWITCH logs:
sofia/external/5195551234@64.4.6.100 ivr(aa_test01_ivr)
6deabebc-ac2b-11e4-869d-5b1cf34b86ba 2015-02-04 00:05:26.759537 [ERR] mod_dptools.c:1959 Unable to find menu
and you will tear your hair out trying to make stupid FusionPBX find the menu.
Creating a Working IVR
I found getting an IVR to work with an inbound route to be tricky and frustrating, but I am dumber than you (and apparently everybody else who uses FusionPBX). Here are the steps that worked for me:
Start by creating an IVR menu in
Apps -> IVR Menu
. At minimum, you should specify the following:- a name
- an extension (which should NOT be the same as any of the extensions or users you have defined so far. It does not matter what the extension is.)
- a
Greet Long
so that you know the IVR is working. I just picked a built-in WAV file. (Don't use the "soccer moms" one or you will be sorry.) Direct Dial
set to true (which is not strictly necessary, but I found useful)Enabled
set to true
In addition I set a couple of options that would ring handsets when I pressed 1 and 2. The destination were transfers:
<extension> xml default
.Now make an inbound route in
Dialplans -> Inbound Routes
. Do this ONLY after you have the IVR menu defined.- Give the IVR a name
- Do not click the "Add" button in
Destination Number
, because it doesn't work. It takes me to someDestinations
screen which does not even let me create fields. Instead, click theAdvanced
button in the top right corner of the page. This will expose two more bolded fields. - For
Condition 1
choosedestination_number
as the field and an incoming DID as the expression. (There are other ways to do this, of course, but I was trying to test calls from a particular DID.) - For
Action 1
choose the IVR menu you created.
This should be enough to get you going.
If you look at the dialplan after creating it you will see that the
Number
is blank, but that there are the following settings:
condition
context
ofpublic
condition
destination_number
of your DIDaction
oftransfer
with something like998 XML default
, where998
is the extension you chose for the IVR menu.
The transfer is the hard part, because the IVR menu XML file is
created in the default
context, and the dialplan is created in the
public
context. Connecting the two took me a long time to figure out.
XML Files
Here are the XML files that get created as a result:
In conf/dialplan/default/333_v_<ivr_menu_name>.xml
is the IVR menu
entry, where <ivr_menu_name>
is the name you assigned to the IVR
menu. Mine looks something like this:
<extension name="aa_test01_ivr" >
<condition field="destination_number" expression="^998$" >
<action application="answer" />
<action application="sleep" data="1000" />
<action application="set" data="hangup_after_bridge=true" />
<action application="set" data="ringback=${hold_music}" />
<action application="set" data="transfer_ringback=${hold_music}" />
<action application="set" data="ivr_menu_uuid=5lkgh1f4-b979-4754-b656-2448b72805ac" />
<action application="lua" data="ivr_menu.lua" />
</condition>
</extension>
You can see that FusionPBX does not use the ivr
command at all, but rather calls a custom IVR program manually. The "998" is the extension I gave this IVR menu entry.
<extension name="aa_test02_dialplan" >
<condition field="context" expression="public" />
<condition field="destination_number" expression="5195551234" >
<action application="set" data="call_direction=inbound"/>
<action application="set" data="domain_uuid=5567619e-af21-4a14-9f78-857da972104b"/>
<action application="set" data="domain_name=nb-freeswitch"/>
<action application="set" data="domain=nb-freeswitch"/>
<action application="transfer" data="998 XML default" />
</condition>
</extension>
"5195551234" was the incoming DID I was matching against. You can see the magical "transfer" line at the end of the dialplan.
This stuff is supposed to be easy, but it's hard for me! I hope that by writing this out somebody else can benefit from my suffering.