<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nandan Singh</title>
    <description>The latest articles on DEV Community by Nandan Singh (@sarasvi).</description>
    <link>https://dev.to/sarasvi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4235972%2F5d13826e-f3d8-481c-8985-2ab3aa7274b3.jpg</url>
      <title>DEV Community: Nandan Singh</title>
      <link>https://dev.to/sarasvi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarasvi"/>
    <language>en</language>
    <item>
      <title>Building an AI Receptionist: Automating Clinic Bookings with Voice Agents and Odoo</title>
      <dc:creator>Nandan Singh</dc:creator>
      <pubDate>Mon, 09 Nov 2026 22:29:04 +0000</pubDate>
      <link>https://dev.to/sarasvi/building-an-ai-receptionist-automating-clinic-bookings-with-voice-agents-and-odoo-1jg7</link>
      <guid>https://dev.to/sarasvi/building-an-ai-receptionist-automating-clinic-bookings-with-voice-agents-and-odoo-1jg7</guid>
      <description>&lt;p&gt;The front desk of a busy clinic is a high-pressure environment. Between managing walk-in patients and handling administrative tasks, incoming calls often go to voicemail—or worse, remain unanswered. For a clinic, a missed call isn't just a missed notification; it's a patient who might look elsewhere.&lt;/p&gt;

&lt;p&gt;At NMT Solutions, we’ve been implementing AI voice agents that act as a 24/7 digital front desk. These aren't the frustrating "Press 1 for Sales" IVR systems of the past. These are LLM-powered agents that can understand natural speech, answer complex questions about services, and directly book appointments into a CRM like Odoo or Zoho.&lt;/p&gt;

&lt;p&gt;In this guide, I’ll walk you through the architecture and implementation of an AI receptionist tailored for a medical or wellness clinic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: How Voice Meets Intelligence
&lt;/h2&gt;

&lt;p&gt;Building a voice agent involves bridging the gap between telephony and Large Language Models (LLMs). Here is the high-level flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Telephony Layer:&lt;/strong&gt; A service like Twilio or Vapi handles the incoming phone call.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Voice Processing:&lt;/strong&gt; Speech-to-Text (STT) converts the patient's voice to text in real-time.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The "Brain":&lt;/strong&gt; An LLM (like GPT-4o or Claude 3.5 Sonnet) processes the text, references the clinic's availability, and decides on the next action.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Action Layer:&lt;/strong&gt; If the patient wants to book, the agent calls an API to check the Odoo calendar.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Voice Output:&lt;/strong&gt; Text-to-Speech (TTS) converts the LLM's response back into a natural-sounding voice.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Designing the Call Flow
&lt;/h2&gt;

&lt;p&gt;A successful AI receptionist needs a clear "Happy Path" and robust handling for edge cases. For a clinic, the flow usually looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Greeting:&lt;/strong&gt; "Hi, thank you for calling [Clinic Name]. How can I help you today?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intent Discovery:&lt;/strong&gt; The patient might want to book an appointment, check pricing, or ask about specific treatments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qualification:&lt;/strong&gt; "Have you visited us before?" / "What symptoms or treatment are you calling about?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability Check:&lt;/strong&gt; The agent queries the Odoo &lt;code&gt;calendar.event&lt;/code&gt; or &lt;code&gt;appointment.type&lt;/code&gt; records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirmation:&lt;/strong&gt; "I've booked you for Tuesday at 4 PM. You'll receive a confirmation WhatsApp shortly."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: The LLM System Prompt
&lt;/h2&gt;

&lt;p&gt;The LLM needs a "personality" and a set of rules. Here is a simplified version of a system prompt we use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are the AI Receptionist for 'Wellness Hub Pune'. 
Your goal is to be professional, empathetic, and efficient.

CONSTRAINTS:
1. Always verify if the patient is new or existing.
2. Only offer slots that are returned by the 'check_availability' tool.
3. If a patient mentions an emergency, immediately use the 'transfer_to_human' tool.
4. Keep responses concise (under 20 words) to maintain a natural conversation flow.

TOOLS AVAILABLE:
- check_availability(date)
- book_appointment(name, phone, time, service)
- transfer_to_human()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Integrating with Odoo via XML-RPC
&lt;/h2&gt;

&lt;p&gt;To make the agent functional, it must talk to your database. Odoo provides a powerful XML-RPC interface. Below is a Python snippet that the AI agent's backend would use to search for available slots in the Odoo Appointment module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xmlrpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;

&lt;span class="c1"&gt;# Odoo Connection Details&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://your-odoo-instance.com"&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your_db"&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"admin"&lt;/span&gt;
&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your_password"&lt;/span&gt;

&lt;span class="c1"&gt;# Authenticate&lt;/span&gt;
&lt;span class="n"&gt;common&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmlrpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServerProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'{url}/xmlrpc/2/common'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;common&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmlrpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServerProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'{url}/xmlrpc/2/object'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_odoo_availability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Search for existing appointments on that date&lt;/span&gt;
    &lt;span class="n"&gt;appointments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_kw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'calendar.event'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'search_read'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'start'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'{target_date} 00:00:00'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'start'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'{target_date} 23:59:59'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'fields'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'start'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stop'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Logic to find gaps in the schedule would go here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;appointments&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the LLM decides to book, it triggers a &lt;code&gt;create&lt;/code&gt; call to Odoo to insert a new &lt;code&gt;calendar.event&lt;/code&gt; or a &lt;code&gt;crm.lead&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Human-in-the-Loop Handover
&lt;/h2&gt;

&lt;p&gt;No AI is perfect, and some situations require a human touch. We implement a "Human Handover" trigger. If the LLM detects high frustration, a request for a manager, or a complex medical question it isn't trained for, it uses a SIP transfer to ring the clinic's physical front desk.&lt;/p&gt;

&lt;p&gt;The handover should be seamless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; "I'm going to connect you with our lead coordinator who can assist you better with that specific medical question. Please hold for a moment."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Executes a bridge command to the human operator while passing the call transcript to the operator's screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Latency:&lt;/strong&gt; If there is a 3-second delay between the patient speaking and the AI responding, the conversation feels "broken." Use specialized voice-native providers (like Vapi or Retell) to keep latency under 800ms.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Hallucinations:&lt;/strong&gt; Never let the AI "guess" a price. If it's not in the knowledge base, it must say, "I'll have a team member call you back with exact pricing."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Interrupt Handling:&lt;/strong&gt; Patients often speak over the AI. Ensure your STT implementation supports "barge-in," so the AI stops talking as soon as the patient interrupts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementation Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Define the Persona:&lt;/strong&gt; Voice (Male/Female/Accent) and Tone.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Clean Data:&lt;/strong&gt; Ensure your Odoo/Zoho calendar is the "Source of Truth."&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Testing:&lt;/strong&gt; Run at least 50 test calls covering various accents and background noise.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Monitoring:&lt;/strong&gt; Log every call transcript to identify where the AI gets confused.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Automating the front desk doesn't mean losing the personal touch; it means ensuring that every patient is heard, even at 2 AM on a Sunday.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm Nandan Singh — I build AI and automation systems (Odoo, Zoho, chatbots, voice agents) at NMT Solutions. Questions? Drop a comment or WhatsApp +91 92260 02903.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>odoo</category>
      <category>python</category>
    </item>
  </channel>
</rss>
