FreePBX Asterisk IVR dial in – no conference mapper API

Sharing this in case it helps save anyone time :slight_smile:

I was recently tasked with setting up an Asterisk Dial-in method with some objectives that I think many other corporate environments would also be looking for.

Through this tutorial , I’m starting from the basis that you have already added a SIP extension for Jigasi/Jitsi , and you’re able to dial the extension and successfully connect to the default Jitsi room.

• Hosts must have assigned , static meetings.
• Ability to quantify calls & duration per Host for billing/reporting
• Allow entry of static meeting (and pin if applicable)
• Provision up to 2min wait time if Host hasn’t arrived.
• Record participant name and playback to the conference upon joining.
• Ability to play an optional tone or whisper to participants upon joining
• No hosting of JSON, PHP, or usage of external API’s

The most constraining is the last part about not being able to host files or use external API. This means no meeting lookup for pin code retrieval.

Luckily, we can still pass our own conference room headers. So this becomes more about strategy and communication around meeting usage.


  1. Meeting rooms names.
    • Keep them numeric for IVR compatibility. IVR can collect a numeric meeting room name and add as the conference header for jitsi.

  2. Communication to the Hosts.
    • Hosts are advised that their assigned meeting room is their office phone number. (could just as easily be last 4 of phone or an employee ID)
    • Hosts are provided a template with link and dial-in number to use in their Outlook email when setting up meetings

  3. IVR
    • With the first 2 pieces in mind, we can setup an IVR to manage the rest

Setting up the IVR with FreePBX (should be similar for other asterisk systems)

  1. Create a custom destination.
  • FreePBX > Admin > Custom Destination
  • Target: Jitsi-Conference-Entry,s,1
  1. Use the custom destination with an inbound route
  • Note: This assumes that you already have a trunk setup for inbound calling
  • FreePBX > Connectivity > Inbound Routes
  • DID: add the DID callers will dial to reach your conference
  • Set Destination: This will be the custom destination we created above

  1. FreePBX > Config Edit > extensions_custom.conf
  • paste in the below dial plan code
  1. Change the 888 to whatever you’re using as your Jitsi SIP extention
  • exten => s,2,Set(Jitsi=888)
exten => s,1,Answer()
;Set the extension used for Jitsi
exten => s,2,Set(Jitsi=888)
;set variable to prevent looping
exten => s,3,Set(Attempts=0)
exten => s,4,Set(Attempts=${MATH(${Attempts}+1,i)})
;Test for invalid entries. On 4th attempt go to error sub
exten => s,5,ExecIf($["${Attempts}" = "4"]?Gosub(Attempts-Error,s,1))
exten => s,6(begin),NooP()
;system listens for DTMF and sets variable "confid"
;10=MAX DIGITS, 10=timeout
exten => s,n,Read(confid,conf-getconfno,10,,,10)
;If blank value, start over
exten => s,n,ExecIf($["${confid}"=""]?goto(Jitsi-Conference-Entry,s,4))
;systems plays back the number entered
exten => s,n,Playback(you-entered)
exten => s,n,SayDigits(${confid})
;system asks to press 1 to accept or 2 to retry
exten => s,n,Read(digi,if-this-is-correct-press&digits/1&otherwise-press&digits/2,1,,1,10)
;If blank value, start over
exten => s,n,ExecIf($["${digi}"=""]?goto(Jitsi-Conference-Entry,s,4))
;if user presses 1 to confirm, system moves onto to check for passcode, if applicable
exten => s,n,ExecIf($["${digi}"="1"]?goto(passcode))
;if callers presses any other digit, system will re-ask them to enter in their number
exten => s,n,goto(Jitsi-Conference-Entry,s,begin)
;speeding this up for the password, but you could mirror the process above if you want the extra verification...
;system listens for DTMF and sets variable "confpin"
;6=MAX DIGITS, 10=timeout
exten => s,n(passcode),Read(confpin,conf-getpin&vm-then-pound&vm-tocancel,6,,,10)
;User will be sent onto the conference whether confpin is blank or not
exten => s,n,goto(enterconf)
;Add SIP Headers based on caller's entries
exten => s,n(enterconf),SIPAddHeader(Jitsi-Conference-Room: ${confid})
exten => s,n,SIPAddHeader(Jitsi-Conference-Room-Pass: ${confpin})
;Sets CDR "userfield" with the Conference ID
;CDR can now be used to track number of calls and durations associated to the Conference ID
exten => s,n,Set(CDR(userfield)=${confid})
;Record Caller's Name
exten => s,n,Set(__rnum=${RAND()})
exten => s,n,Playback(vm-rec-name)
exten => s,n,Record(/tmp/name-${rnum}.gsm,3,10)
;set spygroup to be used for injecting whisper
exten => s,n,Set(SPYGROUP=1000)
;Dial Jitsi extension and play recorded name to the jitisi conference channel
;3 = seconds to ring , m = play music on hold , A = announcement for dialed channel M = Macro
;I created a 'silence' MOH group with a blank (silent) recording. 
;If you don't create this, the system will play your default MOH instead of 'silence'
exten => s,n,Dial(SIP/${Jitsi},3,m(silence)A(/tmp/name-${rnum})M(Jitsi-join))
exten => s,n,Verbose(0, Contacting ${Jitsi}... Status is ${DIALSTATUS} );
;exten => s,n,Dial(SIP/${Jitsi},3,m(silence)A(/tmp/name-${rnum}))
;Take actions based on dialstatus 
;exten => s,n,GotoIf($["${DIALSTATUS}" = "ANSWER"]?conf-answer)
exten => s,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?unknown)
exten => s,n,GotoIf($["${DIALSTATUS}" = "NOANSWER"]?conf-busy)
exten => s,n,GotoIf($["${DIALSTATUS}" = "CANCEL"]?unknown)
exten => s,n,GotoIf($["${DIALSTATUS}" = "CONGESTION"]?unknown)
exten => s,n,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL"]?unknown)
exten => s,n,GotoIf($["${DIALSTATUS}" = "DONTCALL"]?unknown)
exten => s,n,GotoIf($["${DIALSTATUS}" = "TORTURE"]?unknown)
exten => s,n,GotoIf($["${DIALSTATUS}" = "INVALIDARGS"]?unknown)
;Hangup if condition is not matched (in the event a new dialstatus is added with an update
exten => s,n,Hangup()
;Jitsi is not reachable, play error message
;This is also a good place to send an SMS to your lead system admins
exten => s,n(unknown),playback(please-contact-tech-supt&vm-goodbye)
exten => s,n,Hangup()
;Initial attempt was ok, but Jitsi didn't pickup the call. 
;It's likely that the host hasn't yet authenticated / started the meeting
;Indicate that we're waiting for the leader to join and keep trying
exten => s,n(conf-busy),Playback(conf-waitforleader)
;120 = seconds to ring , m = play music on hold , A = announcement for dialed channel M=Macro
exten => s,n,Dial(SIP/${Jitsi},120,m(default)A(/tmp/name-${rnum})M(Jitsi-join))
exten => s,n,Hangup()

;system plays message and hangs up
exten => s,1,playback(sorry-youre-having-problems)
exten => s,n,playback(tt-monkeys)
;exten => s,n,playback(hangup-try-again)
exten => s,n,Hangup()

exten => s,1,Originate(Local/999@JitsiWhisper,app,Playback,confbridge-join)

exten => _X.,1,Answer()
exten => _X.,n,ChanSpy(,g(1000),qw)
exten => _X.,n,Hangup()