IVR with PIN + API query

Hello Community!
I would like to have an after hours IVR that says “for after hours support please input your six-digit PIN” the customer will then enter a variable number (example: 753741), this number then will have to be send to an HTTP API to get a 200 OK if the number is valid (meaning real customer) and only after that, forward the call to 800-222-4444. Otherwise the call will be drop after a disconnection message.

In other words
#1) “for after hours support please input your six-digit PIN”
#2) 753741
#3) API Call
#4a) forward to 800-222-444
#4b) “Invalid PIN, goodbye”
#5) disconnect call

I think it is possible based on these two post, but they code and the terminology is way above me at the moment and I cannot put all the pieces together.

Multiple key input

API Call

Could you guide me a little bit here?


PD: I already have a working freepbx with SIP trunk, extensions and IVR with only one option (press 1 to go to extension 2000 since I cannot find another way to made it support multiple keys.

Will each customer have their own PIN? If not, I think it would be easier just to have a 6 digit keys pressed in the IVR for destination.

My astricon talk next week is about custom dialplan to make API calls. Put it on your calendar: https://www.asterisk.org/community/astricon-user-conference/schedule/


Would also like to see something on like making changes to the PBX with API like create, or delete extensions for example.

1 Like

@GeekBoy - Yes, there is a database of 50+ PINs so a static 6 digit option is not an alternative.

@lgaetz - your conference is in my calendar. any pointers before then so I can start testing?

Get your API call working on the Linux command line, then bring it into dial plan using shell.

Here is an example that sets the curl result to ${result}, which you can then use in later dialplan:
exten => s,n,Set(result=${SHELL(curl https://website.com/message/send -d contacts=ptn:/${ALT_NUMBER} -d session=${APIKEY} --data-urlencode body="${msgBody}")})

An alternate “All FreePBX” approach, would be to put the numbers in your phonebook then you can lookup from there. We do this putting additional call routing variables in the caller id name field.
exten => s,n,Gotoif($[${DB_EXISTS(cidname/${CALLERID(dnid)})}]?match:nomatch)

Benefit to this approach is everything is local (assuming your DB is not) and ensures correct routing, even if the API is not reachable. The phonebook is easily modified from the GUI or by some more complex automated process.

Thank you comtech!
The shell part is easy (PIN=237182 && curl “website/id=$PIN”) how do I make the IVR to allow the customer to input more than one key?

Also, how do you evaluate result?
if result = X

Also, I get confused with the “exten => s,n,Set” is this a fancy way to express how the IVR is configured on the GUI or it must be set on a particular file?

Set is setting the result(response) of my curl command to the dialplan variable ${result}. I can use ${result} in evaluations and/or routing.

I believe the “IVR” would need to be programmed in dialplan (vs. the GUI) to accomplish what you are outlining. You would need to build your own context and point the route there.

In dialplan you would use the read command to accept a predefined number of digits, and assign that to a variable name of your choosing for use in following steps.

exten => s,n,Read(CBA,custom/cba,10,n,3,10)

My example stores the 10-digit input from the caller in the ${CBA} variable. In this example you would then use a gotoif to determine where to send the call.

I see, so if I’m reading all the documentation right I need to manually update the extensions.conf (/etc/asterisk/) with a script similar to this:

#answer the call and request a PIN
exten => 1,1,Answer(500)
	same => n,Playback(RequestPIN)
#read 6 digits 
exten => 2,1,Read(CBA,custom/cba,6,n,1,10)
	same => n,WaitExten(5)
#send digits to API using CBA
exten => 3,1,Set(result=${SHELL(curl website/id=${CBA})})
	same => n,Goto(5,$result)
#result = 0 good PIN, forward to 800-555-1234
exten => 5,0,Dial(PJSIP/[email protected])
	same => n,Hangup
#result = 1 bad PIN, play message and hangup.
exten => 5,1,Playback(WrongPIN)
	same => n,Hangup

Thank you so much for all the help.

I am not sure your dialplan would work (No way for me to know for sure) as you have it above.

That being said, you would put the working [off-hours-pin] context in /etc/asterisk/extensions_custom.conf

extensions.conf = Made by FreePBX, not permanently editable.
extensions_custom.conf = Lets you add contexts that will not be overridden. Not editable via the GUI.
extensions_override.conf = Lets you override contexts that might already exist in the GUI or FreePBX system dialplan. Not editable via the GUI.

You also have the option to define the context as a custom destination. Making your custom code accessible in the GUI.

If you need help with dialplan, this helped me greatly when I began:

Below is a good way to debug your call flow, when you get to that.

:frowning: nothing that I try seems to work and every little change cause another problem to solve.

Current state:

  • In VirtualBox I have FreePBX up to date, with two extensions. Calls between extensions work.
  • I cannot dial an IVR created on the GUI with just one option because I do not have an active trunk (you would imagine that internal dialing should work) for testing on the Outbound route I set the “Optional Destination on Congestion” to the IVR.
  • I updated the /etc/asterisk/extensions_custom.conf with a simple Playback on extension 500 and it keeps saying “your call cannot be completed as dialed” [Reference]. The article in question does not mention the need of adding an outbound route so I do not have one, but if I create one, the missing trunk is a problem once again.

exten => 500,1,Answer(500)
exten => 500,n,Playback(RequestPIN)
exten => 500,n,WaitExten(5)

  • No matter what do I dial, none of the log life increase in size so I do not think anything is getting logged, this stated happening after the system upgrade [Reference]
  • I already watched all these videos and besides not setting a valid trunk, I do not think I’m missing any steps.

Every single test I try results in an unexpected behavior/error, I cannot imagine trying to accomplish the IVR I wanted with the 6 digits and API call :frowning:

Create a Misc Application with a feature code to access the IVR internally.

Possibly this bug: Bug in Asterisk Logfiles Module ver 15.0.4

Thank you @lgaetz
I was definitely hitting that bug.

The Misc App worked for the “GUI IVR” and I think it works for the Custome Extension if I do a Misc App + Custom Destination but I do not heard anything so I’ll continue testing.


1 Like

You are closer than you were before. If you keep at it you will succeed.

1 Like

I think I finally got it!! Thank you so much both for all the help!!! The dial-plan below is working for me (expect the dial part since I do not have a trunk, but at this point that should be the easy part.
If the API returns 1, the PIN is valid and we can forward the call, otherwise the call is terminated.

What do you think?

;answer the call and set variables
exten => 500,1,Answer(500)
exten => 500,n,Set(IF_CORRECT=0)
exten => 500,n,Set(RESULT=0)

;request a 6 digit PIN
exten => 500,n,Read(PIN,custom/RequestPIN,6,n,1,10)
exten => 500,n,Playback(you-entered)
exten => 500,n,SayDigits(${PIN})
exten => 500,n,Read(IF_CORRECT,custom/IfThisIsCorrect,1,n,1,3)
exten => 500,n,GotoIf($[ ${IF_CORRECT} = 1 ]?600,1:700,1)

;send PIN to API using cURL
exten => 600,1,Set(RESULT=${SHELL(curl -s website_here)})
exten => 600,n,GotoIf($[ ${RESULT} = 1 ]?:700,1)
exten => 600,n,Dial(SIP/[email protected])
exten => 600,n,Goto(700,1)

;hangup the call
exten => 700,1,Hangup()

Again, thank you so much for all the patience.

1 Like

I’d rather use something like

exten => 600,n,Dial(local/[email protected])
1 Like

And 600 and 700 are unnecessary use of possibly other wise useful endpoint identifiers, I suggest you use OK and NOTOK instead

exten => 500,n,GotoIf($[ ${IF_CORRECT} = 1 ]?OK,1:NOTOK,1)
exten => OK,1,Set(RESULT=${SHELL(curl -s website_here)})
exten => same,GotoIf ($[ ${RESULT} = 1 ]?:NOTOK,1)
exten => NOTOK,1,Hangup()
1 Like

I’m sure this works, but there is probably some value in using the native dialplan function for ‘curl’:

 exten => 600,1,Set(RESULT=${CURL(https://website_here)})

Again, not wrong, but convention suggests that when writing dialplan with an arbitrary extension, you use the s character, i.e.:

exten => s,1,Answer(500)
exten => s,n,Set(IF_CORRECT=0)
exten => s,n,Set(RESULT=0)

Thank you all for all the suggestions, I did the necessary adjustments for my use case and we can consider this solved.

Thanks again.

Share your final result :slight_smile:

1 Like