Send automated appointment reminders with the Telnyx Messaging API. Includes scheduling strategies, opt-out handling, and best practices.
Reduce no-shows by sending automated SMS appointment reminders with the Telnyx Messaging API. This guide covers scheduling strategies, message templates, opt-out handling, and timing best practices.
Choose a scheduling approach based on your application’s requirements:
Telnyx Scheduled Messages
Cron / Job Scheduler
Event-Driven Queue
The simplest approach — use the Telnyx API’s built-in scheduled messaging feature. No external scheduler needed.
Python
Report incorrect code
Copy
Ask AI
from datetime import datetime, timedelta, timezone# Schedule reminder 24 hours before appointmentreminder_time = appointment_time - timedelta(hours=24)response = client.messages.send( from_=os.environ.get("TELNYX_FROM_NUMBER"), to="+15559876543", text="Reminder: You have an appointment tomorrow at 2:30 PM.", send_at=reminder_time.astimezone(timezone.utc).isoformat(),)
Pros: No infrastructure needed, simple API call
Cons: Limited to single scheduled time per API call, max 7 days in advance
Run a periodic job (e.g., every hour) that queries your database for upcoming appointments and sends reminders.
Python
Report incorrect code
Copy
Ask AI
# Example cron job (runs hourly)from datetime import datetime, timedeltadef send_pending_reminders(): """Find appointments in the next 24-25 hours and send reminders.""" now = datetime.now() window_start = now + timedelta(hours=23) window_end = now + timedelta(hours=25) # Query your database appointments = db.query( "SELECT * FROM appointments " "WHERE start_time BETWEEN %s AND %s " "AND reminder_sent = FALSE", (window_start, window_end) ) for appt in appointments: send_reminder( to=appt.phone, patient_name=appt.name, appointment_time=appt.start_time, location=appt.location, ) db.execute( "UPDATE appointments SET reminder_sent = TRUE WHERE id = %s", (appt.id,) )
Set up a webhook to receive replies and update appointment status:
Report incorrect code
Copy
Ask AI
from flask import Flask, request, jsonifyapp = Flask(__name__)@app.route("/webhooks/messaging", methods=["POST"])def handle_webhook(): data = request.json["data"] if data["event_type"] != "message.received": return jsonify({"status": "ignored"}), 200 payload = data["payload"] from_number = payload["from"]["phone_number"] text = payload["text"].strip().upper() if text == "CONFIRM": # Update appointment status in your database db.execute( "UPDATE appointments SET status = 'confirmed' WHERE phone = %s " "AND start_time > NOW()", (from_number,) ) # Send confirmation client.messages.send( from_=os.environ.get("TELNYX_FROM_NUMBER"), to=from_number, text="Your appointment has been confirmed. See you then!", ) elif text == "CANCEL": db.execute( "UPDATE appointments SET status = 'cancelled' WHERE phone = %s " "AND start_time > NOW()", (from_number,) ) client.messages.send( from_=os.environ.get("TELNYX_FROM_NUMBER"), to=from_number, text="Your appointment has been cancelled. " "Please call us to reschedule.", ) return jsonify({"status": "ok"}), 200
You must honor opt-out requests. Telnyx automatically handles STOP/UNSTOP keywords for 10DLC and Toll-Free numbers, but you should also track opt-outs in your application.
Automatic opt-out (Telnyx managed)
Telnyx automatically handles standard opt-out keywords (STOP, UNSUBSCRIBE, CANCEL, END, QUIT) for US long codes and toll-free numbers. When a user texts STOP:
Telnyx sends an automatic reply confirming the opt-out
Future messages to that number are blocked at the carrier level
You receive a message.received webhook with the STOP keyword
In addition to Telnyx’s automatic handling, track opt-outs in your database to prevent scheduling reminders for opted-out users:
Report incorrect code
Copy
Ask AI
def handle_opt_out(phone_number: str): """Mark a phone number as opted out.""" db.execute( "UPDATE patients SET sms_opted_out = TRUE WHERE phone = %s", (phone_number,) ) # Cancel any pending reminders db.execute( "DELETE FROM scheduled_reminders WHERE phone = %s AND sent = FALSE", (phone_number,) )def can_send_reminder(phone_number: str) -> bool: """Check if we can send a reminder to this number.""" result = db.query( "SELECT sms_opted_out FROM patients WHERE phone = %s", (phone_number,) ) return result and not result.sms_opted_out
24 hours before: Primary reminder — enough time to cancel/reschedule
2-3 hours before: Final reminder for same-day appointments
Avoid late night/early morning: Only send between 9 AM and 8 PM in the recipient’s local time zone
2
Use multiple reminder windows
For high-value appointments (medical, legal), send two reminders:
48 or 24 hours before — gives time to reschedule
2-3 hours before — final confirmation
For routine appointments (salon, auto service), a single reminder 24 hours before is usually sufficient.
3
Respect time zones
Always calculate reminder times in the recipient’s local time zone. Sending a reminder at 3 AM is worse than not sending one at all.
Report incorrect code
Copy
Ask AI
from zoneinfo import ZoneInfo# Store patient timezone in your databasepatient_tz = ZoneInfo(patient.timezone) # e.g., "America/New_York"local_time = reminder_time.astimezone(patient_tz)# Only send between 9 AM and 8 PM local timeif 9 <= local_time.hour < 20: send_reminder(...)else: # Reschedule to 9 AM local time next_9am = local_time.replace(hour=9, minute=0) if next_9am < local_time: next_9am += timedelta(days=1) schedule_reminder_at(next_9am, ...)
4
Keep messages concise
SMS has character limits. Keep reminders under 160 characters (1 segment) when possible to minimize costs. Include only essential info: