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:
- Outbound call initiation
- Natural language understanding mid-call
- 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
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"}'
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)
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}")
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"])
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
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}")
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."
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"}'
You'll have an access token in seconds and can place your first AI outbound call in minutes.
Building something with VoIPBin? Drop a comment — always curious what people are shipping.
Top comments (0)