Phone tree navigation
When a dialed number answers with a phone tree, Liveleg works through the menu toward the person you named, waits on hold, and connects your agent when a live person picks up.
How navigation works
Answering machine detection marks the line as a phone tree. With navigation on, the line is kept instead of dropped. The navigator listens to each menu prompt and answers it with a key press or a spoken option. It uses dial-by-name directories and known extensions.
The navigator only talks to automated systems. When a person answers at any point, detection marks the line as human and your agent is connected. The navigator never speaks to a person.
While a line is in a menu or on hold, the agent keeps dialing other lines in the session.
Targets per lead
Set ivr on the dial session as a default, and on individual leads to override it.
POST/v1/dial_sessions
| Parameters | Type | Description |
|---|---|---|
ivr.targetrequired | string | What to ask for. A department (sales, owner, billing, service), a person's name, or an extension such as ext:214. |
ivr.fallback | operator | any_human | none | What to try when the target is not in the menu. Default operator. |
ivr.max_hold_seconds | integer | Longest time to wait on hold before ending the line. Default 600. |
ivr.max_steps | integer | Most menu choices to make before giving up. Default 8. |
curl https://api.liveleg.dev/v1/dial_sessions \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agt_114",
"lead_list_id": "lst_roofing_tx",
"lines": "auto",
"ivr": { "target": "owner", "fallback": "operator", "max_hold_seconds": 600 }
}'const res = await fetch('https://api.liveleg.dev/v1/dial_sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIVELEG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
agent_id: 'agt_114',
lead_list_id: 'lst_roofing_tx',
lines: 'auto',
ivr: { target: 'owner', fallback: 'operator', max_hold_seconds: 600 },
}),
});
const session = await res.json();import os, requests
session = requests.post(
"https://api.liveleg.dev/v1/dial_sessions",
headers={"Authorization": f"Bearer {os.environ['LIVELEG_API_KEY']}"},
json={
"agent_id": "agt_114",
"lead_list_id": "lst_roofing_tx",
"lines": "auto",
"ivr": {"target": "owner", "fallback": "operator", "max_hold_seconds": 600},
},
).json() Per lead, for example a named contact at one company and an extension at another:
curl https://api.liveleg.dev/v1/lead_lists/lst_roofing_tx/leads \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{"leads": [
{"phone": "+15125550147", "state": "TX", "company": "Ridgeline Roofing",
"ivr": {"target": "Dana Ruiz", "fallback": "any_human"}},
{"phone": "+15125550163", "state": "TX", "company": "Bay City Solar",
"ivr": {"target": "ext:214"}}
]}' Call screeners
Some phones answer unknown callers with a screening assistant that asks who is calling and why. Some business lines ask the caller to state their name before transferring. With the screener option on, the navigator plays the agent's own recording of one short factual line and waits, for example: "This is Dana with Ridgeline Roofing LLC calling about your roof estimate. Our number is 512-555-0147."
The line plays only when the lead's consent record has recorded_voice: true, stored with the consent text, source and time. Otherwise the agent is connected straight to the screener and answers it live. The line never plays on calls to Washington numbers.
If the person picks up, your agent is connected as usual. If the screener sends the call to voicemail, the line ends with ivr.failed and reason screened_to_voicemail. The option is off by default.
| Parameters | Type | Description |
|---|---|---|
ivr.screener.enabled | boolean | Answer call screeners with your line. Default false. |
ivr.screener.line | string | One factual line: who is calling, the business, why, and a callback number. Placeholders: {agent_first_name}, {company}, {reason}, {callback_number}. Each agent records it in their own voice. |
Each agent records the line once in their own voice. Keep it to who is calling, the business, why and a callback number. The compliance guide explains why.
curl https://api.liveleg.dev/v1/dial_sessions \
-H "Authorization: Bearer $LIVELEG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agt_114",
"lead_list_id": "lst_roofing_tx",
"ivr": {
"target": "owner",
"screener": {
"enabled": true,
"line": "This is {agent_first_name} with {company} calling about {reason}. Our number is {callback_number}."
}
}
}' {
"type": "ivr.screener_answered",
"data": {
"call_id": "cal_4Kd72Ns",
"heard": "Hi, the person you are calling uses a screening service. Please say your name and why you are calling.",
"text": "This is Dana with Ridgeline Roofing LLC calling about your roof estimate. Our number is 512-555-0147.",
"voice": "agent_recording"
}
} Hold and limits
Hold music and recorded hold messages are ignored. Waiting ends when a person picks up, when max_hold_seconds is reached, or when the target's voicemail answers. Voicemail ends the line. No message is left.
Navigation also stops after max_steps menu choices, or when no option matches the target or the fallback. Each stop sends ivr.failed with the reason.
Connecting the agent
When a person picks up, the agent hears a short note first, for example "Owner, Ridgeline Roofing, after 2 minutes 14 seconds on hold", and is then connected. The call.bridged event carries the path taken and the time on hold.
{
"type": "call.bridged",
"data": {
"call_id": "cal_3Wq81Fd",
"agent_id": "agt_114",
"to": "+15125550147",
"speech_ended_to_bridge_ms": 980,
"hold_seconds": 134,
"ivr_path": [
{ "heard": "For sales, press 1. For the owner, press 3.", "sent": "3" },
{ "heard": "Please say the name of the person you are calling.", "sent": "Dana Ruiz" },
{ "hold": 134 },
{ "heard": "This is Dana.", "result": "human" }
]
}
} Events
| Event | When |
|---|---|
ivr.detected | An answered line is a phone tree and navigation starts. |
ivr.step | A key or spoken option is sent. Includes the prompt heard. |
ivr.screener_detected | A call screener or auto-attendant asks who is calling. |
ivr.screener_answered | The configured line is given. Includes the text and voice. |
ivr.hold_started, ivr.hold_ended | The call goes on and comes off hold. |
ivr.human_detected | A live person picks up. |
call.bridged | The agent is connected. Includes ivr_path and hold_seconds. |
ivr.failed | Navigation stops: no_match, timeout, voicemail, screened_to_voicemail or max_steps. |
A sample ivr_path
A navigation that reached the target's voicemail and ended:
{
"type": "ivr.failed",
"data": {
"call_id": "cal_7Hn20Pa",
"reason": "voicemail",
"ivr_path": [
{ "heard": "Enter the extension of the person you are calling.", "sent": "214" },
{ "heard": "Sam is not available. Please leave a message.", "result": "voicemail" }
]
}
} Pricing
| Phone tree navigationBilled in 6-second increments. Answering call screeners is part of navigation time. Once a person is connected to your agent, the per-answered-minute rate applies. | $0.08/ min navigating or on hold |
Time in the menu, with a call screener and on hold is billed at the navigation rate. Once a person is connected to your agent, the call is billed at $0.12 per answered minute.
Tips for business lists
- Send the contact's name as the target when you have it. Dial-by-name directories reach the right desk more often than department menus.
- Use extensions from earlier calls. The
ivr_pathof a connected call shows which keys worked for that number. - Set
fallbacktooperatorfor small businesses, where the front desk usually knows who handles sales. - Keep
max_hold_secondslower for lists with long hold times, so lines are free for other calls.