Asterisk IVR Multiple Digits: Fix Dropped Extension Dialing
Asterisk IVR multiple digits fail when the entry matches no extension in the IVR context: Direct Dial only knows local extensions, so a remote CUCM number like 291 hits the invalid handler. Add a pattern such as _2XX that dials the trunk.
- FreePBX Direct Dial only matches extensions that exist in FreePBX, so numbers on CUCM or 3CX are invalid and fall to the i handler.
- A pattern like _2XX added to the ivr-N context, dialing PJSIP/${EXTEN}@trunk, makes remote extensions reachable with no placeholders.
- Single-digit options sharing a first digit with extensions (option 2 and 2XX) cause delays or intercepted entries, so remove or renumber them.
- Set the Timeout and Invalid destinations to an operator or the IVR itself, never Hangup, so a mistyped entry does not drop the caller.
- Confirm with dialplan show ivr-1, pjsip set logger on and CUCM route patterns to find which side rejects the call.
Why does the IVR drop the call when callers dial more than one digit?
An Asterisk IVR is a loop of Background() (play the prompt while listening for DTMF) followed by WaitExten(). Each digit is appended to the buffer and Asterisk searches the IVR context for a matching extension. If the buffer matches exactly and nothing longer could match, it executes immediately. That is why a single digit like 1 works instantly.
For 291 to work, the context needs an extension that matches 291. FreePBX Direct Dial (Enable Direct Dial in the IVR) just includes the context from-did-direct-ivr, which holds entries only for extensions that exist in FreePBX. Extensions living on CUCM or 3CX are not there, so 291 matches nothing.
When nothing matches, Asterisk jumps to the i (invalid) extension. If the IVR's invalid retries are exhausted or the invalid destination is Hangup, the caller is disconnected. If digits stop arriving, the t (timeout) extension fires the same way. To the caller it feels like the call dropped right after typing the extension.
How does Asterisk match digits: patterns, X, Z, N and the dot
Dialplan patterns start with an underscore. The character classes are fixed, and knowing them lets you cover whole ranges in one line.
The difference between . and ! matters in an IVR. The dot needs at least one more character and keeps matching as long as digits arrive, so _2. waits for the digit timeout. The ! wildcard matches zero or more characters and counts as a complete match as soon as it is unambiguous, which is useful for variable-length numbers but can fire early. For fixed-length internal extensions, prefer explicit patterns.
| Pattern | Matches | Typical use |
|---|---|---|
| X | One digit 0-9 | _2XX = 200 to 299 |
| Z | One digit 1-9 | _ZXX = 3-digit, no leading 0 |
| N | One digit 2-9 | _NXXX = 4-digit, first digit 2-9 |
| [135-7] | Any listed digit or range | _[23]XX = 200-399 |
| . (dot) | One or more of any character | _9. = trunk prefix, any length |
| ! (bang) | Zero or more, matches early | Variable-length, use with care |
Which fix should I use for extensions on another PBX?
There are three workable approaches. All of them end with the IVR context having something to match; they differ in how much you maintain.
For a CUCM or 3CX range that is contiguous (say 2000 to 2999 or 200 to 299), the pattern approach is almost always the best choice. Placeholders only make sense for a handful of numbers.
| Fix | How it works | Best when | Drawback |
|---|---|---|---|
| 1. Placeholder or custom extensions | Create a local extension or Custom Extension per remote number, forwarding or dialing out to the real number | Few remote numbers | One object per number; must stay in sync with CUCM |
| 2. Pattern in extensions_custom.conf | _2XX added to the ivr-N context dials the CUCM trunk | Contiguous ranges, many numbers | Hand-written dialplan; no GUI validation |
| 3. Include a custom context into ivr-N | ivr-N includes your own context holding patterns | Reusing one range across several IVRs | Context names and hooks vary by version |
Copy-paste dialplan for Asterisk IVR multiple digits (pattern-based extension dialing)
Asterisk merges multiple definitions of the same context name, so you can add lines to [ivr-1] from extensions_custom.conf, which FreePBX never overwrites. Replace 1 with your IVR's ID and cucm-trunk with the actual trunk name (the PJSIP endpoint name shown by pjsip show endpoints).
Dialing the trunk directly skips outbound routes and their dial patterns. Dialing Local/${EXTEN}@from-internal instead sends the call through outbound routes, which is useful when you want route permissions, prefixes or failover to apply.
; /etc/asterisk/extensions_custom.conf
[ivr-cucm-ext]
exten => _2XX,1,NoOp(IVR remote ext ${EXTEN} from ${CALLERID(num)})
same => n,Set(CALLERID(dnid)=${EXTEN})
same => n,Dial(PJSIP/${EXTEN}@cucm-trunk,45,Tt)
same => n,Goto(app-operator,s,1) ; fallback if unanswered/failed (use your own destination)
; Alternative: go through outbound routes
;exten => _2XX,1,Dial(Local/${EXTEN}@from-internal,45)
; Hook into IVR 1 (merged with the generated context)
[ivr-1]
include => ivr-cucm-extWhy do timeouts and single-digit options break multi-digit entry?
Two timers matter. TIMEOUT(response) is how long Asterisk waits for the first digit after the prompt, and the IVR's Timeout field controls this. TIMEOUT(digit) is the gap allowed between digits and is typically only a few seconds by default. A long digit timeout makes callers wait in silence after typing a complete 3-digit number if a longer match is still possible. A short one cuts off slow typists mid-entry, which then falls to i.
The prefix conflict is the most common hidden cause. If the menu has an option 2 and your extensions are 2XX, then 2 is a complete match but also the start of a longer pattern. Depending on version and settings, the caller either waits for the digit timeout before option 2 fires, or the option fires before the rest is dialed. Either way 291 becomes unreliable.
Fix it by renumbering options (use 1 for sales, 9 for operator, and keep extensions in another range) or by moving the extension range away from the option digits. Callers pressing # to finish an entry also fail, because # is not part of _2XX. Either tell callers not to press it or add an explicit pattern such as _2XX# if you want to support it.
How do I stop callers from being dropped?
In the IVR settings, set the Timeout destination and the Invalid destination to the operator, a ring group or the same IVR rather than Terminate Call/Hangup. Set Invalid Retries to 2 or 3 and enable Invalid Retry Recording so callers hear a message such as 'that extension was not recognized' before the retry. Use the same pattern for Timeout Retries.
Also give the Dial() a fallback line after it, as in the dialplan above. If CUCM returns 404 or busy, Dial() ends and the call would otherwise hang up at the end of the context.
Debugging checklist with the Asterisk CLI
Work from the caller inward: did the digits arrive, did they match, did the INVITE go out, and what did CUCM answer. A failure at each stage looks different.
| Symptom in the CLI | Likely cause | Fix |
|---|---|---|
| Sent to 'i' handler / Invalid extension | No pattern matched the entered digits | Add the _2XX pattern or placeholder; check prefix conflicts |
| Only the first digit ever appears | DTMF not received after the first or mismatch (RFC2833 vs inband) | Set dtmf_mode=rfc4733 on the inbound trunk/endpoint, match the carrier |
| Executing 't' after typing | Digit timeout too short or typing too slow | Raise TIMEOUT(digit) slightly; do not exceed about 5 seconds |
| INVITE sent, 404 Not Found | CUCM has no route pattern for the number | Add the route pattern or translation on CUCM |
| INVITE sent, 403 or 488 | Calling search space blocks it or codec mismatch | Fix CSS/partition; align codecs (ulaw/alaw) on both sides |
| No INVITE at all | Wrong trunk name or outbound route not reached | Check pjsip show endpoints and the Dial() string |
- Run asterisk -rvvvvv (or core set verbose 5 inside the CLI) and place a test call.
- Watch for lines like 'Goto (ivr-1,i,1)', which confirms the digits matched nothing.
- Run dialplan show ivr-1 and confirm your _2XX pattern is listed.
- Run pjsip set logger on and repeat the call to see the outbound INVITE and the response from CUCM; sngrep shows the same flow more readably.
- Run rtp set debug on if the call connects but audio is missing, and check direct_media, external_media_address and local_net on the trunk.
asterisk -rvvvvv
dialplan show ivr-1
dialplan show from-did-direct-ivr
pjsip show endpoints
pjsip set logger on
rtp set debug on
# separate shell:
sngrep -d any port 5060 What must be configured on the CUCM side?
Asterisk can only deliver the digits it sends, so CUCM must accept them. The SIP trunk's calling search space must contain a partition holding a route pattern or directory number that matches what you send. If the IVR passes 291 but CUCM expects 5 digits, add a translation pattern or send the full number from the dialplan with a prefix.
On the Asterisk side, confirm the trunk allows the pattern. If you use Local/${EXTEN}@from-internal, an outbound route must match the digits, otherwise you get 'No such extension' in from-internal rather than an INVITE.
Common questions
Why does FreePBX IVR Direct Dial not work for extensions on another server?
Direct Dial includes only extensions that exist in FreePBX itself. Remote numbers are unknown to it, so you must add a pattern route, custom extensions or placeholders for them.
Can I use 3-digit extension dialing from an IVR to an external PBX?
Yes. Add a pattern such as _2XX to the ivr-N context that dials the trunk with Dial(PJSIP/${EXTEN}@trunkname). Make sure the remote PBX has a matching route pattern or directory number.
What do the i and t extensions do in an Asterisk IVR?
The i extension runs when the entered digits match no extension, and t runs when the caller enters nothing before the timeout. In FreePBX these are mapped to the invalid and timeout settings and their retry counts.
How long should the digit timeout be for extension entry?
Around 3 to 5 seconds between digits suits most callers. Shorter values cut off slow typing and longer ones leave callers in silence after a complete entry.
Will my changes in extensions_custom.conf survive a FreePBX reload?
Yes. FreePBX regenerates its own files but leaves extensions_custom.conf untouched. Only the generated contexts are rewritten, so keep your additions in your own context and include it.