DEV Community

voipbin
voipbin

Posted on

Build a Voice Appointment Reminder Bot: AI Calls That Confirm, Reschedule, or Cancel

No-shows are expensive. Whether you're running a clinic, a salon, a law office, or a SaaS demo pipeline — every missed appointment is lost revenue. The traditional fix is a human calling each person the day before. That doesn't scale.

The modern fix is an AI voice bot that calls, holds a real conversation, and updates your system based on what the customer says.

Let's build one.

The Problem with Most Reminder Systems

Most automated reminder systems are one-way: "Your appointment is tomorrow at 2pm. Reply STOP to opt out." That's not a conversation — it's spam with a disclaimer.

What customers actually want is to be able to say: "Can we do 3pm instead?" or "Actually, I need to cancel." And what you want is to capture that intent and act on it immediately — without a human in the loop.

This requires:

  1. Outbound call initiation
  2. Natural language understanding mid-call
  3. A webhook to update your backend

All three are straightforward with VoIPBin + your AI of choice.

Architecture Overview

Your Backend
    │
    ├── POST /calls (initiate outbound call)
    │       │
    │       ▼
    │   VoIPBin ──── calls patient's phone
    │       │
    │       ├── STT: patient speech → text
    │       ├── sends text to your AI
    │       ├── TTS: AI response → audio
    │       │
    │       └── POST /webhook (call events)
    │               │
    └───────────────┘
                    ▼
              Update your DB
Enter fullscreen mode Exit fullscreen mode

Your server never touches audio. VoIPBin handles the RTP stream, STT, and TTS. You write business logic — not telephony code.

Step 1: Sign Up and Get Your API Key

curl -s -X POST https://api.voipbin.net/v1.0/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"username": "yourname", "password": "yourpass"}'
Enter fullscreen mode Exit fullscreen mode

Response includes accesskey.token — use that as your Bearer token for all subsequent requests.

Step 2: Define the AI Conversation

Here's a minimal Python handler for the AI side. This runs on your server and receives text from VoIPBin:

from flask import Flask, request, jsonify
import openai

app = Flask(__name__)
client = openai.OpenAI()

sessions = {}  # conversation_id -> history

@app.route("/ai", methods=["POST"])
def handle_voice_turn():
    data = request.json
    call_id = data.get("call_id")
    user_text = data.get("text", "")

    if call_id not in sessions:
        sessions[call_id] = [
            {
                "role": "system",
                "content": (
                    "You are a scheduling assistant for Maple Dental Clinic. "
                    "You're calling to confirm tomorrow's appointment. "
                    "Be concise — this is a phone call. "
                    "Ask if they can confirm, reschedule, or if they need to cancel. "
                    "When they decide, say: 'Great, I've noted that. Have a good day!' "
                    "and end the call."
                )
            }
        ]

    sessions[call_id].append({"role": "user", "content": user_text})

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=sessions[call_id]
    )

    reply = response.choices[0].message.content
    sessions[call_id].append({"role": "assistant", "content": reply})

    # Signal call end when AI wraps up
    should_end = "have a good day" in reply.lower()

    return jsonify({
        "text": reply,
        "end_call": should_end
    })

if __name__ == "__main__":
    app.run(port=8080)
Enter fullscreen mode Exit fullscreen mode

Simple, stateful, and easy to extend.

Step 3: Handle VoIPBin Webhooks

VoIPBin posts call events to your webhook endpoint. Here's how to capture the outcome:

@app.route("/webhook", methods=["POST"])
def call_webhook():
    event = request.json
    call_id = event.get("call_id")
    event_type = event.get("type")

    if event_type == "call.ended":
        # Extract last user intent from session history
        history = sessions.get(call_id, [])
        last_user_msg = next(
            (m["content"] for m in reversed(history) if m["role"] == "user"),
            ""
        ).lower()

        if "cancel" in last_user_msg:
            update_appointment(call_id, status="cancelled")
        elif "reschedule" in last_user_msg or "change" in last_user_msg:
            update_appointment(call_id, status="reschedule_requested")
        else:
            update_appointment(call_id, status="confirmed")

        # Clean up session
        sessions.pop(call_id, None)

    return jsonify({"ok": True})

def update_appointment(call_id, status):
    # Your DB update logic here
    print(f"[{call_id}] Appointment status: {status}")
Enter fullscreen mode Exit fullscreen mode

Step 4: Initiate the Outbound Call

Now the part that kicks everything off — your backend triggers the call:

import httpx

VOIPBIN_TOKEN = "your-access-token"

def call_patient(patient_phone: str, call_id: str):
    payload = {
        "source": "+15551234567",   # your VoIPBin number
        "destination": patient_phone,
        "ai_url": "https://yourserver.com/ai",
        "webhook_url": "https://yourserver.com/webhook",
        "metadata": {"call_id": call_id}
    }

    res = httpx.post(
        "https://api.voipbin.net/v1.0/calls",
        json=payload,
        headers={
            "Authorization": f"Bearer {VOIPBIN_TOKEN}",
            "Content-Type": "application/json"
        }
    )
    return res.json()

# Example: trigger reminder for tomorrow's appointments
for appt in get_tomorrows_appointments():
    call_patient(appt["phone"], appt["id"])
Enter fullscreen mode Exit fullscreen mode

VoIPBin places the call, pipes audio to/from your AI endpoint, and fires the webhook when it ends.

Step 5: Schedule It

For a daily reminder run, a simple cron job does the trick:

# crontab -e
0 10 * * * /usr/bin/python3 /app/trigger_reminders.py >> /var/log/reminders.log 2>&1
Enter fullscreen mode Exit fullscreen mode

Or use your scheduler of choice (Celery, APScheduler, AWS EventBridge, etc.).

What You Get

With this setup:

  • Confirmed → appointment stays, no staff effort needed
  • Reschedule requested → flag in your system, staff follows up or routes to another AI flow
  • Cancelled → slot freed immediately, you can offer it to someone on a waitlist

The whole loop — call placed, conversation completed, DB updated — takes under 2 minutes per patient, fully automated.

Extending the Flow

A few things you can layer in:

Voicemail fallback: If no one answers, leave a message and send an SMS link.

if event_type == "call.no_answer":
    send_sms(patient_phone, f"Missed your call! Confirm your appt here: {confirm_link}")
Enter fullscreen mode Exit fullscreen mode

Reschedule automation: Instead of flagging for human follow-up, connect to your calendar API and offer alternative slots during the call:

# In your system prompt:
"Available slots tomorrow: 9am, 11am, 3pm. Offer these if they want to reschedule."
Enter fullscreen mode Exit fullscreen mode

Multilingual support: Swap the system prompt language based on the patient's stored preference. Same VoIPBin setup, different AI instruction.

Why Not Just Use a DTMF Menu?

You could. "Press 1 to confirm, press 2 to cancel" has worked for 30 years.

But patients hang up on DTMF menus. They talk. An AI that can handle "I need to push it back a bit, maybe Thursday?" has a dramatically higher completion rate — and that means fewer no-shows.

Try It

VoIPBin signup is instant — no OTP, no lengthy onboarding:

curl -X POST https://api.voipbin.net/v1.0/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"username": "you", "password": "yourpass"}'
Enter fullscreen mode Exit fullscreen mode

You'll have an access token in seconds and can place your first AI outbound call in minutes.

voipbin.net


Building something with VoIPBin? Drop a comment — always curious what people are shipping.

Top comments (0)