VoIP Configuration Guide

How to Build an IVR Auto Attendant
in Asterisk

Create a professional phone menu system with Asterisk dialplan. Complete asterisk IVR setup guide with multi-level menus, business hours routing, and SIP trunk integration.

January 21, 2026 18 min read

Table of Contents

1 What Is an IVR Auto Attendant?

An IVR (Interactive Voice Response) auto attendant is a phone system feature that greets callers with a recorded message and presents them with menu options. When a caller dials your business number, instead of hearing endless ringing or going directly to a receptionist, they hear something like: "Thank you for calling Acme Corp. Press 1 for Sales, Press 2 for Support, or Press 0 for the operator."

With a proper asterisk IVR setup, even a one-person business can project the image of a larger, more established company. Callers get routed to the right person or department immediately, reducing hold times and improving the overall customer experience.

Key Benefits of an Auto Attendant

  • Professional greeting for every caller, 24/7
  • Automatic call routing without a live receptionist
  • After-hours handling with voicemail or alternate routing
  • Dial-by-name directory for callers who know their party
  • Scalable -- easily add departments as you grow

Asterisk, the open-source PBX framework, provides all the building blocks you need to create a fully customized asterisk menu system. Combined with a reliable SIP trunk for PSTN connectivity, you can build an enterprise-grade phone system at a fraction of the cost of proprietary solutions.

2 Planning Your Call Flow

Before writing a single line of dialplan, sketch out your entire call flow on paper or a whiteboard. A well-planned IVR is the difference between happy callers and frustrated ones who hang up. Map every path from the initial greeting through each option to its final destination.

Example Call Flow Diagram

  Incoming Call
       |
  [Check Business Hours]
       |
   +---+---+
   |       |
 OPEN    CLOSED
   |       |
[Main    [After-Hours
 Menu]    Message]
   |       |
+--+--+   [Voicemail]
|  |  |
1  2  3
|  |  |
Sales Support Directory
 |      |
[Queue] [Queue]
 |      |
[Agent] [Agent]

Call Flow Checklist

Questions to Answer

  • - How many departments or options?
  • - What happens on timeout?
  • - What happens on invalid input?
  • - Is there an after-hours message?
  • - Do callers need a dial-by-name option?
  • - Should option 0 reach a live person?

Best Practices

  • - Keep the main menu to 4-5 options max
  • - Put the most popular option first
  • - No more than 2 levels of sub-menus
  • - Always provide a way to reach a human
  • - Keep prompts under 30 seconds
  • - State the option before the number

Pro Tip: Say "For Sales, press 1" rather than "Press 1 for Sales." Callers hear the department name first and remember the number better.

3 Recording IVR Prompts

Quality audio prompts are essential for a professional auto attendant asterisk system. Poor recordings reflect badly on your business. Asterisk supports several audio formats, but the telephony standard is 8kHz, 16-bit, mono PCM (WAV) or GSM.

Method 1: Recording via SOX (Command Line)

Use SOX to record directly from a microphone and convert to the correct format:

# Record from microphone (Ctrl+C to stop)
rec -r 8000 -c 1 -b 16 main-menu.wav

# Convert an existing recording to Asterisk format
sox input.wav -r 8000 -c 1 -b 16 main-menu.wav

# Convert to GSM format (smaller file size)
sox input.wav -r 8000 -c 1 main-menu.gsm

# Normalize volume levels
sox main-menu.wav main-menu-normalized.wav gain -n -3

Method 2: Recording via Asterisk Dialplan

Create a recording extension that lets you record prompts by dialing in:

[record-prompts]
; Dial *99 to record, press # when done
exten => *99,1,Answer()
 same => n,Playback(beep)
 same => n,Record(/var/lib/asterisk/sounds/custom/recording%d:wav)
 same => n,Wait(1)
 same => n,Playback(/var/lib/asterisk/sounds/custom/${RECORDED_FILE})
 same => n,Hangup()

; Record a specific prompt by name
exten => *98,1,Answer()
 same => n,Read(FILENAME,vm-enter-num-to-call,20)
 same => n,Playback(beep)
 same => n,Record(/var/lib/asterisk/sounds/custom/${FILENAME}:wav)
 same => n,Wait(1)
 same => n,Playback(/var/lib/asterisk/sounds/custom/${FILENAME})
 same => n,Hangup()

Audio Format Requirements

Format Sample Rate Channels Notes
WAV (PCM) 8000 Hz Mono (1) Best quality, larger files
GSM 8000 Hz Mono (1) Compressed, good trade-off
SLN16 16000 Hz Mono (1) Wideband, HD voice
ulaw/alaw 8000 Hz Mono (1) Native codec format

Professional Recording Services

For the best impression, consider professional voice-over artists. Services like Snap Recordings, VoiceJungle, or Fiverr voice-over sellers can produce studio-quality prompts for $50-200. They will deliver files in the exact format Asterisk needs. Place recorded files in /var/lib/asterisk/sounds/custom/.

4 Basic IVR Dialplan

Here is the core asterisk IVR setup dialplan. This creates a main menu with options for Sales, Support, a company directory, and an operator fallback. Add this to your extensions.conf file.

/etc/asterisk/extensions.conf
[ivr-main]
exten => s,1,Answer()
 same => n,Wait(1)
 same => n,Set(TIMEOUT(response)=5)
 same => n,Set(TIMEOUT(digit)=3)
 same => n(menu),Background(custom/main-menu)
 same => n,WaitExten(5)

; Option 1: Sales Department
exten => 1,1,Goto(sales,s,1)

; Option 2: Technical Support
exten => 2,1,Goto(support,s,1)

; Option 3: Company Directory
exten => 3,1,Directory(default,ivr-main)

; Option 0: Operator / Receptionist
exten => 0,1,Dial(PJSIP/100,30)
 same => n,VoiceMail(100@default,u)

; Timeout: Replay the menu
exten => t,1,Goto(s,menu)

; Invalid input: Notify and replay
exten => i,1,Playback(invalid)
 same => n,Goto(s,menu)


[sales]
exten => s,1,Playback(custom/sales-greeting)
 same => n,Dial(PJSIP/200&PJSIP/201&PJSIP/202,60,t)
 same => n,VoiceMail(200@default,u)
 same => n,Hangup()


[support]
exten => s,1,Playback(custom/support-greeting)
 same => n,Dial(PJSIP/300&PJSIP/301,60,t)
 same => n,VoiceMail(300@default,u)
 same => n,Hangup()

Key Applications Explained

Background()

Plays the audio file while simultaneously listening for DTMF key presses. This is what makes the menu interactive -- the caller can press a key at any point during the prompt.

WaitExten()

Waits for the specified number of seconds for the caller to press a key after the background audio finishes. If no key is pressed, the t (timeout) extension is triggered.

Directory()

Provides a dial-by-name directory. Callers enter the first few letters of a person's name on their keypad, and Asterisk matches it against the voicemail directory.

exten => t

The timeout extension. Triggered when no DTMF input is received within the configured timeout period.

exten => i

The invalid extension. Triggered when the caller presses a key that does not match any defined extension in the current context.

5 Multi-Level IVR Menus

Larger organizations need sub-menus for departments. A multi-level asterisk menu system uses separate contexts for each department's IVR, with the main menu routing callers to the appropriate sub-menu.

Multi-Level IVR with Sub-Menus
[ivr-main]
exten => s,1,Answer()
 same => n,Wait(1)
 same => n(menu),Background(custom/main-menu)
 same => n,WaitExten(5)

exten => 1,1,Goto(ivr-sales,s,1)
exten => 2,1,Goto(ivr-support,s,1)
exten => 3,1,Goto(ivr-billing,s,1)
exten => 0,1,Dial(PJSIP/100,30)
exten => t,1,Goto(s,menu)
exten => i,1,Playback(invalid)
 same => n,Goto(s,menu)


[ivr-sales]
; "For new accounts press 1, for existing accounts press 2,
;  to return to the main menu press 9"
exten => s,1,Background(custom/sales-menu)
 same => n,WaitExten(5)

exten => 1,1,Dial(PJSIP/200&PJSIP/201,60,t)
 same => n,VoiceMail(200@default,u)
exten => 2,1,Dial(PJSIP/202&PJSIP/203,60,t)
 same => n,VoiceMail(202@default,u)
exten => 9,1,Goto(ivr-main,s,1)
exten => t,1,Goto(s,1)
exten => i,1,Playback(invalid)
 same => n,Goto(s,1)


[ivr-support]
; "For hardware support press 1, for software press 2,
;  for network issues press 3, main menu press 9"
exten => s,1,Background(custom/support-menu)
 same => n,WaitExten(5)

exten => 1,1,Queue(hardware-support)
exten => 2,1,Queue(software-support)
exten => 3,1,Queue(network-support)
exten => 9,1,Goto(ivr-main,s,1)
exten => t,1,Goto(s,1)
exten => i,1,Playback(invalid)
 same => n,Goto(s,1)


[ivr-billing]
exten => s,1,Background(custom/billing-menu)
 same => n,WaitExten(5)

exten => 1,1,Dial(PJSIP/400,60,t)
 same => n,VoiceMail(400@default,u)
exten => 2,1,Playback(custom/payment-info)
 same => n,Goto(s,1)
exten => 9,1,Goto(ivr-main,s,1)
exten => t,1,Goto(s,1)
exten => i,1,Playback(invalid)
 same => n,Goto(s,1)

Important: Never go deeper than two levels of sub-menus. Research shows callers become frustrated and hang up after navigating more than two levels of menus. Always provide option 9 (or *) to return to the main menu.

6 Business Hours Routing

One of the most important features of any auto attendant asterisk system is time-based routing. Use GotoIfTime() to route calls differently based on the day and time, sending after-hours callers to voicemail or a different menu.

Business Hours Routing with GotoIfTime()
[incoming]
; Entry point for all inbound calls
exten => s,1,Answer()
 same => n,Wait(1)

; Check business hours: Mon-Fri, 8am-6pm
 same => n,GotoIfTime(08:00-18:00,mon-fri,*,*?ivr-main,s,1)

; Check Saturday hours: 9am-1pm
 same => n,GotoIfTime(09:00-13:00,sat,*,*?ivr-saturday,s,1)

; Check holidays
 same => n,GotoIf($[${DB_EXISTS(holidays/${STRFTIME(${EPOCH},,%Y%m%d)})}]?ivr-holiday,s,1)

; If none matched, it's after hours
 same => n,Goto(ivr-afterhours,s,1)


[ivr-afterhours]
; "Our office is currently closed. Our hours are Monday through
;  Friday 8am to 6pm Eastern. Press 1 to leave a message,
;  or press 2 for our emergency line."
exten => s,1,Background(custom/after-hours-greeting)
 same => n,WaitExten(5)

exten => 1,1,Playback(vm-intro)
 same => n,VoiceMail(100@default,u)
 same => n,Hangup()

exten => 2,1,Dial(PJSIP/on-call-phone,60)
 same => n,VoiceMail(911@default,u)
 same => n,Hangup()

exten => t,1,VoiceMail(100@default,u)
 same => n,Hangup()
exten => i,1,VoiceMail(100@default,u)
 same => n,Hangup()


[ivr-holiday]
exten => s,1,Playback(custom/holiday-greeting)
 same => n,VoiceMail(100@default,u)
 same => n,Hangup()


[ivr-saturday]
exten => s,1,Background(custom/saturday-menu)
 same => n,WaitExten(5)

exten => 1,1,Dial(PJSIP/200,60,t)
 same => n,VoiceMail(200@default,u)
exten => 0,1,Dial(PJSIP/100,30)
exten => t,1,VoiceMail(100@default,u)
exten => i,1,VoiceMail(100@default,u)

GotoIfTime() Syntax

GotoIfTime(times,days_of_week,days_of_month,months?context,extension,priority)
  • times: HH:MM-HH:MM (24-hour format)
  • days_of_week: mon-fri, sat, sun (or * for any)
  • days_of_month: 1-31 (or * for any)
  • months: jan-dec (or * for any)

Timezone Note: Asterisk uses the system timezone by default. Set the timezone per channel with Set(TZ=America/New_York) or configure it globally in /etc/asterisk/asterisk.conf.

7 Connecting to IPComms SIP Trunk

Your IVR needs a SIP trunk to receive and make calls over the PSTN. IPComms provides reliable SIP trunking with simple IP-based authentication, making it easy to connect your Asterisk system.

PJSIP Trunk Configuration

/etc/asterisk/pjsip.conf
; === IPComms SIP Trunk ===

[ipcomms]
type=endpoint
transport=transport-udp
context=from-ipcomms
disallow=all
allow=ulaw
allow=alaw
allow=g722
outbound_auth=ipcomms-auth
aors=ipcomms
direct_media=no
trust_id_inbound=yes
send_pai=yes
from_domain=s1.ipcomms.net

[ipcomms-auth]
type=auth
auth_type=userpass
username=YOUR_ACCOUNT
password=YOUR_PASSWORD

[ipcomms]
type=aor
contact=sip:s1.ipcomms.net:5060
qualify_frequency=30

[ipcomms-identify]
type=identify
endpoint=ipcomms
match=34.23.59.14

Inbound DID Routing to IVR

Route your IPComms DID number to the IVR context:

/etc/asterisk/extensions.conf
[from-ipcomms]
; Route your main DID to the IVR
exten => 14045551234,1,Goto(incoming,s,1)

; Route a second DID directly to a department
exten => 14045551235,1,Goto(sales,s,1)

; Catch-all for any other DIDs
exten => _1NXXNXXXXXX,1,Goto(incoming,s,1)

Outbound Calling from IVR Transfers

Allow IVR transfers to external numbers (e.g., forwarding to a cell phone):

[outbound-ipcomms]
; US/Canada dialing pattern
exten => _1NXXNXXXXXX,1,Set(CALLERID(num)=14045551234)
 same => n,Dial(PJSIP/${EXTEN}@ipcomms,60)
 same => n,Hangup()

; 10-digit dialing (prepend 1)
exten => _NXXNXXXXXX,1,Set(CALLERID(num)=14045551234)
 same => n,Dial(PJSIP/1${EXTEN}@ipcomms,60)
 same => n,Hangup()


[ivr-main]
; ... existing IVR options ...
; Option 4: Forward to external cell phone
exten => 4,1,Playback(custom/transferring)
 same => n,Dial(PJSIP/14045559999@ipcomms,60)
 same => n,VoiceMail(100@default,u)
 same => n,Hangup()

IPComms SIP Trunk Details

SIP Server: s1.ipcomms.net
IP Address: 34.23.59.14
SIP Port (UDP/TCP): 5060
SIP TLS Port: 5061
Codecs: G.711 (ulaw/alaw), G.722
Authentication: IP-based or credentials

8 Advanced IVR Features

DTMF Detection Settings

Configure DTMF detection for reliable key-press recognition:

; In pjsip.conf endpoint section
dtmf_mode=rfc4733      ; Recommended: out-of-band DTMF
; Alternatives: inband, info, auto

; In extensions.conf - set timeouts
exten => s,1,Set(TIMEOUT(response)=10)  ; Wait 10s for input
 same => n,Set(TIMEOUT(digit)=3)        ; Wait 3s between digits

Timeout Handling with Retry Limit

[ivr-main]
exten => s,1,Answer()
 same => n,Set(TRIES=0)
 same => n(menu),Background(custom/main-menu)
 same => n,WaitExten(5)

; Timeout: replay up to 3 times, then go to operator
exten => t,1,Set(TRIES=$[${TRIES}+1])
 same => n,GotoIf($[${TRIES}<3]?s,menu)
 same => n,Playback(custom/connecting-operator)
 same => n,Dial(PJSIP/100,30)
 same => n,VoiceMail(100@default,u)
 same => n,Hangup()

; Invalid: same retry logic
exten => i,1,Set(TRIES=$[${TRIES}+1])
 same => n,GotoIf($[${TRIES}<3]?invalid-msg)
 same => n,Playback(custom/connecting-operator)
 same => n,Dial(PJSIP/100,30)
 same => n,VoiceMail(100@default,u)
 same => n,Hangup()
 same => n(invalid-msg),Playback(invalid)
 same => n,Goto(s,menu)

Call Queue Integration

Route IVR options to call queues for even distribution among agents:

/etc/asterisk/queues.conf
[sales-queue]
musicclass=default
strategy=ringall
timeout=30
retry=5
maxlen=10
announce-frequency=60
announce-position=yes
member => PJSIP/200
member => PJSIP/201
member => PJSIP/202

[support-queue]
musicclass=default
strategy=leastrecent
timeout=45
retry=5
wrapuptime=15
member => PJSIP/300
member => PJSIP/301
Using queues in the IVR dialplan
; In extensions.conf
exten => 1,1,Playback(custom/sales-hold-msg)
 same => n,Queue(sales-queue,t,,,300)
 same => n,VoiceMail(200@default,u)
 same => n,Hangup()

Dial-by-Name Directory

; Directory(voicemail-context,dial-context,options)
; Options: f=search by first name, l=search by last name, b=both
exten => 3,1,Directory(default,internal-extensions,fb)

; Make sure voicemail.conf has full names:
; [default]
; 200 => 1234,John Smith,john@example.com
; 201 => 1234,Jane Doe,jane@example.com

Database-Driven IVR with AGI

For dynamic menus driven by a database, use AGI (Asterisk Gateway Interface):

/var/lib/asterisk/agi-bin/ivr-lookup.py
#!/usr/bin/env python3
"""AGI script for database-driven IVR routing"""
import asterisk.agi
import psycopg2

agi = asterisk.agi.AGI()

# Get the caller ID
callerid = agi.env['agi_callerid']

# Look up the caller in the database
conn = psycopg2.connect(
    host='34.31.22.45',
    dbname='customers',
    user='asterisk',
    password='secret'
)
cur = conn.cursor()
cur.execute(
    "SELECT priority, department FROM customers WHERE phone=%s",
    (callerid,)
)
result = cur.fetchone()

if result:
    priority, dept = result
    if priority == 'vip':
        agi.set_variable('IVR_DEST', 'vip-queue')
    else:
        agi.set_variable('IVR_DEST', f'{dept}-queue')
else:
    agi.set_variable('IVR_DEST', 'ivr-main')

conn.close()
Using AGI in dialplan
[from-ipcomms]
exten => _X.,1,Answer()
 same => n,AGI(ivr-lookup.py)
 same => n,GotoIf($["${IVR_DEST}"="vip-queue"]?vip)
 same => n,Goto(${IVR_DEST},s,1)
 same => n(vip),Playback(custom/vip-greeting)
 same => n,Queue(vip-queue,t)
 same => n,Hangup()

9 Testing Your IVR

Thorough testing is critical before putting your asterisk menu system into production. Test every path, every timeout, and every error condition.

Testing Checklist

1
Call from an external number

Do not test only from internal extensions. Call your DID from a cell phone or another line to verify the full path through the SIP trunk.

2
Test every menu option

Press each key at each menu level. Verify you reach the correct destination.

3
Test timeout behavior

Wait without pressing anything. Verify the menu replays and eventually routes to the operator or voicemail.

4
Test invalid input

Press keys that are not in the menu (e.g., 7 or *). The caller should hear "invalid" and be returned to the menu.

5
Test after-hours routing

Temporarily change the GotoIfTime() hours to force after-hours behavior during the day, then verify the correct message plays.

6
Verify voicemail delivery

Leave messages in all voicemail boxes and confirm they are accessible and sent via email if configured.

Debugging with the Asterisk CLI

# Connect to the Asterisk CLI
asterisk -rvvv

# Enable verbose dialplan tracing
core set verbose 5

# Watch DTMF events in real-time
core set debug 3

# Reload dialplan after changes (no restart needed)
dialplan reload

# Verify your context exists
dialplan show ivr-main

# Check active channels
core show channels

# Check PJSIP trunk status
pjsip show endpoints
pjsip show aor ipcomms

10 Common Mistakes to Avoid

Even experienced Asterisk administrators make these errors when building their first IVR. Avoid these pitfalls to ensure callers have a smooth experience.

No Timeout Handler

Without an exten => t extension, callers who do not press a key are left in silence. Always define the timeout extension to replay the menu or route to an operator.

Too Many Menu Levels

Three or more levels of sub-menus frustrate callers. If a caller has to press 4 keys to reach someone, they will call a competitor instead. Stick to a maximum of 2 levels.

Poor Audio Quality

Recording prompts with a laptop microphone in a noisy room results in unprofessional audio. Use a quality microphone, record in a quiet space, or hire a professional voice-over service.

Wrong Audio Format

Uploading 44.1kHz stereo MP3 files. Asterisk must transcode these in real-time, adding CPU load and potential quality issues. Always convert to 8kHz mono WAV or GSM.

No "Return to Main Menu" Option

In sub-menus, always provide a way back to the main menu. Otherwise, callers are trapped in the wrong department with no escape except hanging up.

No Way to Reach a Live Person

Some callers refuse to navigate menus. Always provide "Press 0 for the operator" on every level. This reduces frustration and abandoned calls.

Forgetting to Reload the Dialplan

After editing extensions.conf, you must run dialplan reload in the Asterisk CLI. Changes do not take effect until reloaded.

Not Testing DTMF Mode

If your SIP trunk uses a different DTMF method than your endpoint, key presses will not be detected. Ensure both sides use rfc4733 for reliable DTMF.

Need a Reliable SIP Trunk for Your IVR?

IPComms provides crystal-clear SIP trunking with instant activation, pay-per-minute billing, and free inbound calls. Perfect for Asterisk IVR systems.