Phone menu navigation

Let an AI agent answer automated phone menus and wait on hold on outbound calls - how to switch it on, what the Calls page shows, how these calls end, when they are retried, and what they cost.

An outbound call to a company often reaches an automated menu ("press 2 for English") and then a hold queue, not a person. With phone menu navigation on, the AI agent listens to each menu, presses the key that fits the call's task (or answers a menu that asks you to speak), waits quietly on hold, and starts talking when a person picks up. It is off by default and you opt in per agent. It works on outbound calls only: support for keypad menus on inbound calls comes later.

A call can last at most 29 minutes, including time in menus and on hold. If nobody answers by then, the call ends as “Hold limit reached” (ended_reason ivr_timeout, reason_detail deadline). No setting raises this limit.

This page is the whole picture in one place. The reference for each part is in Answer detection (the setting, the menu hint and the languages), Call variables (numbers the agent may type) and Post-call results (the ending values). Campaign retries are described in Campaigns.

Which agents can use it

Phone menu navigation works for agents on the Cascade engine, including agents that run a conversation workflow, and for agents on Gemini Live. It is not available for agents on GPT-Live at this release: in the workspace the setting is shown switched off, with the reason beside it. Through the API, turning navigation.enabled on for a GPT-Live agent, or switching an agent that has it on to GPT-Live, is refused with 400 navigation_engine_not_supported; the response names the engine and lists the supported ones (supported_engines). Each engine in the agent catalog (GET /v1/agent-catalog) says whether it can navigate in supports_navigation. Switch the agent to Cascade or Gemini Live first, or leave navigation off.

Turn it on

In the workspace, open the AI agent, then Tools, and tick Navigate phone menus and wait on hold. Two limits appear under it, with notes on the menu languages and on which voices may say a value (see Languages and Typed and spoken values):

SettingAPI fieldDefaultRange
Max hold timenavigation.max_hold_seconds600 seconds (10 minutes)10 seconds up to 29 minutes: a whole call lasts at most 29 minutes, menus and hold included
Max time in menusnavigation.max_navigation_seconds180 seconds (3 minutes)10 seconds up to 15 minutes

Time in menus and time on hold have separate limits: a long hold never uses up the time allowed for menus, and the other way round. The API schema allows up to 3600 seconds for hold and 900 seconds for menus, but a hold or a menu cannot outlast the call, so the 29-minute call maximum caps both.

Through the API the same setting is the navigation section of PATCH /v1/agents/{agent_id}/voice-stack:

curl -X PATCH https://api.skysay.ai/v1/agents/agt_123/voice-stack \
  -H "Authorization: Bearer $SKYSAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"navigation": {"enabled": true, "max_hold_seconds": 900}}'

A hold or a menu cannot outlast the call that contains it, so neither limit can be longer than the agent's maximum call length (turn_taking.max_call_seconds, 1740 seconds = 29 minutes unless you shorten it). A longer limit is refused with 400, and so is shortening the maximum call length below a limit you set.

The limits belong to the agent and are not changed per call. Time in menus and time on hold are counted separately across the whole call, and the call also ends at its overall maximum length. The setting applies when a call starts; saving never changes a call in progress. Navigation only applies to calls the agent places, not to calls it receives.

What the agent does

It has four jobs, and nothing else changes while it is in a menu or a queue:

  • Answering menus. It listens to each menu and answers it the way the menu asks. On a keypad menu it presses the key that fits the call's task and listens again. On a menu that asks you to speak ("say billing") it says the answer. While it is in a menu or a queue, silence, hold music or a recorded "goodbye" never hangs the call up.
  • Waiting on hold. It stays silent through the whole queue: hold music, announcements, and "press 1 to stay in the queue", which it answers by pressing the key. Only the two limits or the call's maximum length end the wait early.
  • Declining callback offers. When a menu offers to call you back instead of holding, the agent declines it automatically, by key or by voice. Silence never accepts a callback either. If the offer has an option to stay in the queue, it takes that one. On Gemini Live, a callback offer that can only be answered by speech gets no spoken decline: the callback is never taken, and the call stays in the queue.
  • Giving up on a menu. When no option fits the call's task, the menu asks for something the call does not carry, or the key presses change nothing, it ends the call instead of guessing. The call ends as Menu had no matching option and is not redialled.

When something that may be a person answers, the agent first confirms it with a short check question, such as whether it is speaking with a person. Only then does it start the conversation.

Tell it what to expect. If you know the menu, pass navigation_hint in the call's call_context or as a campaign column (for example Press 2 for English, then 1 for billing.). It is plain text of at most 500 characters, reaches the agent as the built-in variable call.navigation_hint, and a longer or non-text hint refuses the call with call_variables_invalid. It is a hint, not a script: the agent still answers the menu it actually hears.

Numbers the menu asks for

A menu often asks for a number: a card number, a PIN, a government ID, a date of birth, an account or booking reference. These come only from the call's own data, as a keypad variable: you declare the variable with keypad and a class (card, pin, government_id, date_of_birth, reference or other) and send its value with the call. The agent never makes one up, and a menu that asks for a number the call does not carry gets nothing.

  • The model never sees the value. It is not in the prompt, the conversation, a tool request or a webhook. The agent can ask for the field to be entered; the platform fetches the value at that moment and sends it.
  • Transcripts show the field, not the digits. A typed value reads as Entered {field} (for example Entered {account_number}) in transcripts, the timeline and logs.
  • The value is deleted at the end of the call. It is not kept in the call record afterwards, so a retry needs the value sent again with the new call.

Typed and spoken values

Most menus take a number on the keypad. Typed values work on every supported engine, everywhere keys are accepted, except on agents whose languages include Polish (see Languages). Some menus accept a number only by voice ("say your account number"). The platform then speaks the digits itself, in the agent's own voice, without the model.

  • Spoken values (a stored value said aloud to a prompt that asks for it to be spoken) are supported on Cascade agents only, workflow agents included, and only with these voices: Azure AI Speech in English, German, French, Spanish, Italian, Dutch, Portuguese and Russian; Gemini TTS in English, German, French, Spanish, Dutch and Russian; ElevenLabs Flash v2.5 in English. With any other voice or language, the value is entered on the keypad only, and a question that can be answered by speech alone ends the call.
  • On Gemini Live, a prompt that takes speech only ends the call as data not authorised (ended_reason ivr_timeout, reason_detail data_not_authorised) before the value is read. Prompts that take keys, take either, or don't say how to answer still get the value as keypad tones.

Languages

Phone menu navigation works in English, German, French, Spanish, Italian, Dutch, Portuguese, Russian and Polish. Estonian and Finnish are not supported. An agent whose languages are all unsupported cannot turn navigation on (the API answers 400 navigation_languages_unsupported with reason unreleased). An agent with at least one supported language can, and its menus in other languages are not navigated.

Agents whose languages include Polish navigate Polish menus, but cannot enter keypad values yet (value_language_unsupported). That applies to the whole agent: a typed value is not entered on any call of an agent that has Polish among its languages, and the menus are still navigated. When such a call reaches a question that asks for a value it carries, such as a PIN or a card number, the call ends as data not authorised (ended_reason ivr_timeout, reason_detail data_not_authorised), because Polish cannot enter keypad values yet.

To listen to the far end the agent uses at most two speech-to-text connections, chosen from the transcribers Skysay supports for the agent's selected languages. If the agent's languages cannot be covered that way, switching navigation on is refused with 400 navigation_languages_unsupported; the response lists the language codes concerned in languages, and reason is uncovered (no supported transcriber hears that language), too_many_sockets (every language is covered, but not by two connections) or unreleased (see above). Remove a language, add a supported one, or turn navigation off. Details are in Answer detection.

Lines that carry keys

Pressing keys needs a carrier line where key delivery has been proven. Skysay records this per line and sends keys only on a line that is proven: DIDWW is proven today, and every other line is refused until it is. A customer-owned trunk and, today, the DIDLogic line do not send keys. The agent does not pretend: the timeline says Keys not supported on this line, and a menu that needs keys ends the call as Menu had no matching option. Menus you can answer by speaking, and holds, work on every line.

A self-hosted (open-source) worker does not send keys either. The table that says which carrier lines can carry key presses is not part of the open-source export, so a self-hosted worker refuses key presses and reports Keys not supported on this line. See Deployment modes.

What makes it reliable

Navigation is not left to prompt instructions. It rests on these parts.

  • A detector, not prompt instructions. A detector tells a menu, a queue, a recording and a person apart, rather than relying on instructions in the agent's prompt.
  • A person check before the agent talks. When something answers, the agent confirms a real person with a short check question, then continues the conversation.
  • Carrier-proven key delivery. Keys are sent only on carriers proven to deliver them.
  • Sensitive values isolated from the model. Card, PIN, government ID and date of birth come only from the call's own data, and the model never sees them.
  • Automatic callback decline. Callback offers are declined automatically, and silence never accepts one.
  • Separate time limits for menus and holds. A long queue cannot use up the time allowed for menus, and the other way round, and the whole call is still capped at 29 minutes.
  • Tested before every release. Every release is tested against about 40 call scenarios, on each supported engine where the scenario applies.

What you see in the workspace

Calls list. The list shows short chips. A call that went through a menu gets one quiet chip in the Outcome column, for example Menu → hold 6:40. A call that stopped there also gets a short ending chip:

ChipMeaning
Hold limitnobody picked up within the max hold time, or the call reached its maximum length while it waited
No menu matchthe menu had no option that fits the call's task, or asked for a number the call did not carry
Menu time limitthe agent used the time allowed for menus without reaching a queue or a person
Hung up on holdthe other side hung up while the call was on hold
Answered by a machinethe number answered with a recorded message only: no menu, no person

The Failure reason filter has the first three, under Telephony, with their full names (Hold limit reached, Menu had no matching option, Time limit in phone menus reached).

Call details. The header splits the call's duration into Menu, Hold and Conversation, and says in one sentence how the call ended. The timeline shows, in order: each menu the agent detected, each key it sent, a typed value as Entered {field}, each interval on hold, Keys not supported on this line where a line cannot carry keys, and the moment a person answered, with the short check that confirmed them, before the conversation itself.

How these calls end

These endings are reported on the call resource as ended_reason with a reason_detail. A campaign gives each one a disposition and retries it under the same per-outcome rules as other calls (see Campaigns):

ended_reasonreason_detailCampaign dispositionRetried
ivr_timeouthold_limitno_answeryes, when no person was reached earlier
ivr_timeoutdeadlineno_answeryes, when no person was reached earlier
remote_party_disconnectedduring_holdno_answeryes, when no person was reached earlier
ivr_timeoutmenu_no_pathconnected_completedno, terminal
ivr_timeoutnavigation_limitconnected_completedno, terminal
ivr_timeoutdata_not_authorisedconnected_completedno, terminal
no_human_after_answerrecorded_message_onlyno_answeryes, like any other machine answer
media_session_lostnavigation_detector_lostconnected_completedno, terminal

The retried endings are the ones another attempt can change: the hold queue was too long, the call ran out of time, or the other side hung up before a person came on. The terminal ones are dead ends: the same menu will be there next time. A line that only plays a recorded message shows as Answered by a machine.

media_session_lost with navigation_detector_lost is not a limit or a menu outcome: it means the call could no longer follow the line, because none of the sources that listen to the far end delivered anything for 30 seconds, so the platform ended the call. The Calls list files it under the media failures as Media session lost. It has no campaign rule of its own: the call connected, so it takes connected_completed unless the agent or its workflow declared another outcome, and a campaign does not retry it.

person_reached on the call resource says whether a person was reached earlier in the call; it is null when the call did not report it, and a missing value never causes a retry. These calls connected, so the carrier reports them as completed, and the ending says why nothing came of the call.

Cost

Time in menus and on hold is billed at your normal call rate, as part of one call. Navigation adds no surcharge and there is no separate hold price; the split in the call details is for reading, not for billing.

Limits worth knowing

  • A menu that changes its mind, forwards the line elsewhere or asks for something unexpected is answered as heard; the agent ends the call at a menu with no matching option rather than guess.
  • How well the agent understands a menu is measured language by language before it is claimed (see Answer detection); a language that works on keys can still have a menu the agent misreads.
  • Once a person answers, everything they say is an ordinary conversation, even if they read a number back.