
Share:
Liz Acosta is a Developer Advocate at Vonage. While her career path from film student to marketer to engineer to Developer Advocate might seem unconventional, it’s pretty typical for Developer Relations! Liz loves pizza, plants, pugs, and Python.
Best Practices for Call Flows
Time to read: 11 minutes
A call flow is a structured path a customer is guided along when they contact your business. It maps out every interaction from the initial greeting to final resolution, and includes menu options, wait times, and transfers between systems or agents. A call flow determines whether a caller reaches the right department quickly and leaves satisfied. Poor design can leave them lost in a maze of irrelevant options and frustrating dead ends. You’ve probably experienced both!
The following are two examples of call flows:
Scenario 1
You call the customer support number for a business you ordered a product from, and haven’t yet received it in the mail.
You are greeted with a menu of options:
Press 1 if you want to check the status of an order, but only if you know the order number
Press 2 to check the status of an order if you don’t know the order number
Press 3 to check the delivery status of an order, but only if you know the tracking number
Press 4 to check the delivery status of an order, but only if you know the order number
Press 5 to check the delivery status of an order if you don’t know the tracking number
Press 0 to speak with a representative
You press 0 because you have neither the order number nor the tracking number, and you’re placed on hold while repetitive music plays.
While on hold, you are also informed that you can check the status of your order online – if you have the tracking number.
You are finally connected with a representative who, using your name and address, is able to look up your order number, but unable to tell you the status of your order, so they transfer you to another agent.
While you are being transferred, you are put on hold again, and the same repetitive music plays
You are connected with another agent who has no context for your call, so you must repeat your name and address along with your order number.
They are able to tell you that the order has been shipped, and they can give you the tracking number as well. They cannot, however, look up the delivery status and instead advise you to check the website for the delivery provider.
You hang up and vow to never work with this business again.
Does calling customer service sometimes feel like this?Scenario 2
You call the customer support number for a business you ordered a product from, and haven’t yet received in the mail.
You are greeted with a menu of options:
Press 1 if you want to check the status of an order
Press 2 if you would like to make another order
Press 0 to speak with a representative
You press 1 and are presented with a menu of options:
If you know the order number, you can say or use the keypad to enter it
If you don’t know the order number, you can say, “I don’t know the order number” and remain on the line for a representative
You say that you don’t know the order number and are placed on a brief hold.
When you are connected with a representative, they have already used your phone number – which is associated with the order – to look up your purchase in their database.
They inform you that the order is in transit, provide the tracking number, and let you know that it is scheduled for delivery tomorrow.
You hang up and look forward to receiving your order.
These two scenarios highlight the difference between poor and effective call flow design. Which call flow delivers a better customer experience?
Good Call Flows and Customer Loyalty
Implementing best practices for call flows isn’t just a courtesy, it directly impacts your bottom line. Poor call flows leave customers frustrated and dissatisfied, which increases abandonment rates and tarnishes a brand’s reputation. Misdirected calls are also a drain on business resources. Conversely, a well-designed call flow can reduce call times, improve first-contact resolution, and build trust and customer loyalty.
In this post, we will explore the key principles and practical strategies that can transform call flows into a competitive advantage. We’ll also take a look at using the Vonage Voice API to improve customer experience and operational efficiency.
Best Practices for Efficient, Effective Call Flows
Now that you know the importance of good flows in creating customer experiences that nurture customer loyalty and brand goodwill, let’s examine best practices to keep in mind when developing a call flow.
Design Effective Interactive Voice Response Systems
Interactive Voice Response (IVR) is an automated telephony technology that enables a human caller to interact with a computer using voice and Dual-Tone Multi Frequency (DTMF) tones input on a keypad. IVR is usually implemented with a menu that callers select an option from using either voice or DTMF.
Reduce Agent Workload With Intentional Self-Service Where Appropriate
It is likely that by the time a customer decides to pick up the phone, they’ve already exhausted all other self-service options. While IVR is usually the first step in a call flow, it’s important to wield this kind of automation carefully and provide customers with an “escape route” to a human agent. However, you also do not want to overburden agents with calls that could be handled through self-service. Balancing automation, self-service, and human agents requires consideration and intention.
When creating a menu, don’t overwhelm customers with too many options. Limit choices from three to five maximum, and use clear, concise language. Implement timeouts with appropriate fallback handling and provide the option to speak directly to an agent should none of the menu options apply to a customer’s needs – you don’t want someone hanging up out of frustration right away!
Invest In Voice Quality and Natural Language Processing
Few things can feel more cold and alienating than interacting with a robotic-sounding interface. One way to combat this is to employ human voice actors to record more evergreen messages and to invest in high-quality text-to-speech options for more dynamic messages.
Additionally, using natural language processing instead of rigid, menu-based IVR systems allows more flexibility for customer responses instead of rigid predefined scripts that may confuse or frustrate callers – especially if there is a language barrier. This flexibility enables your IVR to serve a wider range of customers and makes it more likely that your call flow will route their call appropriately. With AI becoming more accessible, natural language processing can easily be integrated into your call flow.
Integrate Customer Relationship Management For Personalization
Customer Relationship Management (CRM) refers to the concept of recording, organizing, and analyzing interactions with potential and current customers in order to nurture relationships and grow your business. In practice, CRM refers to either a system by which customer interactions are captured and organized, or a strategy by which a business manages their customer relationships. Essentially, what a CRM system does is help compile a profile of a customer – including data beyond contact and orders – into a record of all interactions that can help inform future communications.
When it comes to call flows, integrating a CRM system helps automate customer data lookup and facilitate building a more customized experience. Using Automatic Number Identification (ANI)/Caller Line Identification (CLI) to identify a caller, CRM within a call flow can help tailor self-service options or provide context for the call when a caller reaches an agent or even route the caller to an agent they have worked with previously with a positive outcome. Personalization like this helps increase customer satisfaction and retention.
Moreover, integration with a CRM system means calls can be automatically logged and documented to help maintain a record of customer history, ensure compliance and quality, and provide data for improving call flows.
Match Callers With Skills-Based Agents
Not all customer inquiries are created equal, and neither are your agents. Routing every caller to the next available representative might be efficient in theory, but it often leads to longer resolution times and unnecessary transfers. Instead, implement skills-based routing, where calls are directed to agents best equipped to handle the specific issue.
For example, a customer calling about billing should be routed to an agent trained in account and payment systems; a technical support inquiry should go to someone with product expertise. This reduces friction, shortens call duration, and increases the likelihood of first-contact resolution.
By aligning customer needs with agent expertise from the outset, you create a smoother, more competent experience that builds trust and satisfaction.
Handle Errors Gracefully
Even the best-designed call flows encounter errors such as misunderstood inputs, system timeouts, or unexpected user behavior. What separates a frustrating experience from a positive one is how these moments are handled.
Instead of forcing callers through rigid loops or dropping them from the system, design your flow with graceful error handling in mind. When something goes wrong, the system should guide the caller forward, not trap them in confusion. Additionally, logging and analyzing error points can reveal patterns that help you refine and improve your call flow over time.
Handled thoughtfully, errors become opportunities to demonstrate reliability and care that can turn potential frustration into reassurance that your system is designed with the customer in mind.
A thoughtfully designed call flow can inspire customer trust and loyalty.
Sample Application: Call Flow In Action
This sample application written in Python uses the Voice API to demonstrate best practices for call flows. It uses a SQLite database to simulate a CRM used for call personalization and customer interaction logging. While you probably wouldn’t want to use this code in production, it serves as a foundation for constructing call center automation that helps retain rather than repel customers.
You can find the full code and README to run the app locally in the Vonage Community on GitHub.
IVR Best Practices
In the sample application, the caller is greeted with a menu of options: 1 for billing, 2 for technical support, and 3 for account management. If none of these options meet their needs, they can dial 0 to speak to an operator. Note that the menu does not exceed 5 options, and each item is concisely described.
def build_welcome_menu_ncco(self) -> List[Dict]:
"""
Build the welcome menu NCCO with IVR options
Demonstrates: Effective IVR with self-service + escape routes
"""
webhook = f"{settings.ngrok_url}/ivr/menu-selection"
greeting = "Welcome to our customer service. Press 1 for billing, 2 for technical support, or 3 for account management. Press 0 to speak with an operator."
ncco = [
{"action": "talk", "text": greeting, "style": 11, "bargeIn": True},
{
"action": "input",
"type": ["dtmf"],
"dtmf": {"maxDigits": 1, "timeOut": 3},
"eventUrl": [webhook],
"eventMethod": "POST",
},
]
return nccoThe application uses the NCCO talk input actions to list the menu options and record the caller’s selection. Dialing 0 for an operator provides an escape route for more complex needs.
To learn more about Vonage Call Control Objects (also known as “NCCO”) refer to the documentation.
Skills-Based Agent Routing
For demonstration purposes, the application uses a pre-populated SQLite database as a stand-in for a CRM system to retrieve the caller’s customer information. In a production setting, you’d want to write these records in as a seeder that runs after your migrations in a deploy process. A NCCO talk action simulates a skills-based agent by reciting the specific requested customer information.
def build_customer_greeting_ncco(
self,
uuid: str,
call_info: dict = None,
department_info: str = None,
) -> List[Dict]:
"""
Build personalized greeting for customer
Demonstrates: CRM integration for personalization
"""
greeting = "Unfortunately, we don't have any customer records associated with this number."
# If the customer has an entry in the database
if department_info:
customer_name = call_info.get("customer")["name"]
greeting = f"Hello {customer_name}, welcome back. The information you requested is as follows: {department_info}."
greeting_ncco = [{"action": "talk", "text": greeting, "style": 11}]
recording_ncco = self.build_recording_ncco(uuid)
return greeting_ncco + recording_ncco An Escape Route With Customer Callback
If the caller dials 0 for an operator, the application simulates a full call queue, apologizing for the inconvenience, but then follow-up with a call once an operator becomes available.
def build_operator_greeting_ncco(
self,
uuid: str,
call_info: dict = None,
) -> List[Dict]:
"""
Build the operator greeting for customer
Demonstrates: Escape route + customer follow up
"""
greeting = "I'm sorry, all operators are currently busy. We will call you back in a moment when an operator becomes available."
ncco = [
{"action": "talk", "text": greeting, "style": 11},
]
# Schedule callback in configured seconds
from_number = call_info.get("from")
asyncio.create_task(self.schedule_callback(from_number, uuid, call_info))
return nccoSince this is a demonstration application, it uses a 10-second countdown to make a callback rather than an available operator.
async def schedule_callback(
self, phone_number: str, uuid: str, call_info: dict = None
):
"""
Simulate calling a customer back instead of making them wait on hold
Demonstrates: Graceful error handling with follow-up contact
"""
delay_seconds = 10
try:
print(f"Scheduling callback to {phone_number} in {delay_seconds} seconds")
customer_name = call_info.get("customer")["name"]
greeting = "Hello. We are calling you back."
if customer_name:
greeting = f"Hello {customer_name}. We are calling you back."
# Wait for the specified delay
for i in range(delay_seconds, 0, -1):
print(f"Calling {phone_number} in {i} seconds ... ")
await asyncio.sleep(1)
ncco = [{"action": "talk", "text": greeting, "style": 11}]
print(f"Now returning call to: ==> {phone_number}")
call = CreateCallRequest(
ncco=ncco,
to=[ToPhone(number=phone_number)],
from_=Phone(number=settings.vonage_virtual_number),
)
response = self.client.voice.create_call(call)
return response.status
except Exception as e:
print(f"Error in callback scheduling: {str(e)}") Logging Interactions for Better Customer Support
Using the NCCO record action, the application asks the caller to leave a message about call quality. This recording is then transcribed and logged in the CRM system as a customer interaction. In a production deployed call flow, these logged interactions can help improve call quality and personalize the dialogue between customer and agent, creating a warmer experience.
def build_recording_ncco(self, uuid: str) -> list[Dict]:
"""
Build the recording NCCO
Demonstrates: Logging customer interactions in CRM
"""
webhook = f"{settings.ngrok_url}/webhooks/recording?uuid={uuid}"
greeting = "Thank you for calling. Please leave a message about the quality of this call. Press the pound key when you are done."
ncco = [
{"action": "talk", "style": 11, "text": greeting},
{
"action": "record",
"endOnKey": "#",
"beepStart": True,
"endOnSilence": 3,
"transcription": {
"eventUrl": [webhook],
"eventMethod": "POST",
"language": "en-US",
},
},
{
"action": "talk",
"text": "Thank you for your message. Goodbye.",
"style": 11,
},
]
return ncco Failing With Grace
Just because a call might take an unexpected turn, it doesn’t mean your flow needs to be interrupted. This is how errors are handled in the application:
def build_error_ncco(self) -> List[Dict]:
"""
Build NCCO for error handling
Demonstrates: Graceful error handling
"""
greeting = "Sorry, I didn't understand that input. Please try again."
greeting_ncco = [{"action": "talk", "text": greeting, "style": 11}]
return greeting_ncco Go With The Flow
A well-designed call flow is more than just a technical necessity. It’s a critical touchpoint that shapes how customers perceive your business. By keeping menus simple, balancing automation with human support, investing in natural and responsive interactions, leveraging CRM data for personalization, and ensuring callers are routed efficiently while errors are handled with care, you create an experience that feels intuitive rather than frustrating. When every step of the journey is intentional, customers spend less time navigating systems and more time getting the help they need, which leads to stronger satisfaction, improved loyalty, and a more efficient operation overall. Start building your call flow with the Vonage Voice API today by exploring the resources below.
Further Reading and Resources
Build an Interactive Voice Response Menu Using Node.js and Express: In this tutorial, we will build an interactive voice response menu using the Vonage Voice API and Node.js to receive inbound calls and capture user input via the keypad.
How to Build an Advanced IVR / Voice Bot: Build an advanced IVR voice bot with the Vonage Voice API and OpenAI. This guide shows you how to handle inbound calls, use ASR to capture caller intent, and generate intelligent responses from an AI voice agent.
Vonage Integration Platform API Overview: Get guides and code snippets, browse reference documentation, and more to develop connected applications with Vonage APIs and SDKs.
Have a question or want to share what you're building?
Subscribe to the Developer Newsletter
Follow us on X (formerly Twitter) for updates
Watch tutorials on our YouTube channel
Connect with us on the Vonage Developer page on LinkedIn
Stay connected and keep up with the latest developer news, tips, and events.