Skip to content

getDigits: the timeouts and retries that decide your abandon rate

· Updated · ivrloom team

#five9 #ivr-design #testing

getDigits is the node that collects a caller’s input — an account number, a ZIP code, a PIN. It is also where a surprising share of abandoned calls happen, because it is the first point where the caller has to do something and can get it wrong.

What the node is actually deciding

Three things, and they interact:

  • How long to wait before deciding the caller is not going to press anything.
  • How many digits constitute a complete answer.
  • What happens when neither of those resolves cleanly.

The third is where flows differ most, and where the default behaviour is least likely to be what you want.

The fields, as they appear in the export

Opening a getDigits module in a .five9ivr file, the collection settings are a handful of elements inside its data block:

FieldWhat it holdsValues we have seen in production exports
numberOfDigitshow many digits complete the entry1, 4, 5
maxTimeoverall time to enter, in seconds5, 17, 20
maxSilencehow long to wait for the next digit, in seconds2, 4, 5
terminateDigitthe key that ends a variable-length entry#, or x for none
clearDigitBufferdiscard anything typed ahead before this collectiontrue in every one
targetVariableNamethe variable the digits are written toscript-specific

Two of those deserve a second look. maxTime and maxSilence are different clocks: the first bounds the whole entry, the second bounds the gap between keypresses. And clearDigitBuffer being true everywhere is not a coincidence — it stops a digit the caller pressed during the previous prompt from being counted as the first digit of this one.

Mis-entry is where getDigits differs from a menu. A menu retries on its own and leaves through its No Match branch; getDigits does neither. When the caller runs out of time, Five9 moves on to the next module — or repeats this one — with whatever was entered, possibly nothing. So the handling belongs in the module after it, and in every real script we examined it is there: each getDigits is followed by an ifElse or a case that tests the value. The exceptionalDescendant some of them carry is for a prompt that fails to play, not for a caller who didn’t answer.

Corrected 2026-09-22: an earlier version of this post said the mis-entry behaviour lives in events blocks on the module and exits through exceptionalDescendant. That describes a menu, not getDigits — none of the real getDigits modules we have examined carry events.

Inter-digit timeout is not the same as initial timeout

Waiting for the first digit and waiting between digits are different problems. A caller who has not started yet may be finding their account number. A caller mid-way through a ten-digit entry is reading it off a statement and pausing.

A single short timeout that works for a menu choice will cut off someone entering a long number. A single long timeout that works for a long number makes every menu feel sluggish. That is why the export has two fields. A maxSilence of 2 seconds with a maxTime of 5 is a menu-shaped setting; the same 2-second maxSilence with a maxTime of 20 is an account-number-shaped one. If you have both kinds of collection in one flow, they should not share values.

Fixed length lets you finish early

If you know the answer is exactly four digits, numberOfDigits of 4 means the caller does not have to press # and does not have to wait out a timeout. That is a real reduction in call time on a high-volume path.

If the length varies, you need a terminateDigit, and you need to say so in the prompt. “Enter your account number, then press pound” is not padding — without it, callers wait, then repeat themselves, then press zero.

Retries are a budget, not a loop

Two attempts is a common landing point. What matters more than the number is that each attempt is different:

  • First failure: the caller may not have heard. Repeat.
  • Second failure: repeating again is unlikely to help. Route to a human, or offer a different path.

An unbounded retry loop is not caller-friendly; it is a trap with no exit. Every retry path should terminate somewhere a person can help — which in the file means the check after the getDigits should count the tries and, once they run out, send the caller to a person or another path rather than round again or to a bare hangup.

The blank that flows downstream

There is a subtler failure than the caller giving up. When nothing is collected, the target variable is still written — to an empty string. If the next node is a case that compares that variable against “1”, “2”, “3”, empty matches nothing and the call takes whatever the unmatched path is. If there is no unmatched path, the flow has no defined next step.

Our simulator used to get this wrong in the other direction: a silent caller left the previous collection’s digits in the buffer, so a downstream case matched as though they had answered. The fix was to reset the buffer on no-input, which is what a real Five9 script does. An early version of that fix also stopped the call at the getDigits when no retry handler was configured. Since Five9’s getDigits has no such handler and simply moves on, that made every silent caller look like a dead end; it was corrected on 2026-09-22.

The edges worth testing

Digit collection has a small, well-defined set of failure modes, and all of them are testable without placing a call:

  • No input at all
  • Input shorter than expected, then silence
  • Input longer than expected
  • A key you do not handle
  • The terminator pressed immediately, with nothing before it

That last one catches more bugs than you would expect — an empty entry that passes validation and flows downstream as a blank variable.

In ivrloom each of these is a saved scenario. Queue the digits as one entry (a four-digit PIN is one collection, not four keypresses), run, and the trace shows which exit the node took and what the variable held afterwards. For the silent-caller case, step to the node and choose “No input — caller stayed silent”: the call moves on to the next module with nothing entered, which is exactly the case your check has to handle. If that check sends the caller straight back to the same getDigits with no counter, the run reaches its step limit — which is what the loop does to a real silent caller. If the getDigits has no next module at all, the run stops with a diagnostic naming the node. (What a saved scenario sends to our servers, and when, is answered in the FAQ.)

What the Issues panel checks for you

Before you run anything, the Issues panel reports a getDigits with no next module as a dead end, and a branch whose target no longer exists as a broken connection. It also follows a caller who presses nothing through the flow at a few fixed times of the week and warns when that call goes round a loop and never reaches an exit — the retry loop with no counter, where every path out exists but needs the caller to cooperate. In one real script we examined, an “incorrect account number” loop sent a silent caller round forever. Loops that run through a Menu are not reported yet, so the silent-caller scenario above is still worth saving.

Updated 2026-09-25: an earlier version of this section said the Issues panel could not see a retry loop with no counter. It has warned about them since 22 September.