Every business gets text messages outside of office hours. Customers ask about pricing, request support, or just want to know when you're open. Without an autoresponder in place, those messages sit unanswered until someone checks the queue the next morning — and by then, the customer has moved on.
An SMS auto-reply bot solves this by responding instantly to every inbound message. You can route replies by keyword, adjust responses based on business hours, and handle opt-in/opt-out compliance — all without a human in the loop. With the Telnyx Messaging API and webhooks, you can build one in a single endpoint.
What Is an SMS Autoresponder?
An SMS autoresponder is a service that listens for incoming text messages and sends back a pre-defined reply based on the content of the message. The Telnyx Messaging API makes this straightforward: when someone texts your Telnyx number, Telnyx delivers the message to your application as a webhook event. Your application inspects the message, picks the right response, and sends a reply back through the API.
The key components are keyword routing and business-hours logic. Keyword routing maps words like "help," "hours," or "pricing" to specific responses. Business-hours logic lets you tailor replies depending on whether your team is currently available — directing callers to a phone number during the day, or setting expectations about response times at night.
How It Works
The autoresponder flow has three steps:
- A customer sends an SMS to your Telnyx number. Telnyx receives the message and forwards it to your application as a
message.receivedwebhook event. The payload includes the sender's phone number, your Telnyx number, and the message text.
- Your application generates a response. Your webhook handler parses the incoming message, checks it against keyword rules and business-hours logic, and selects the appropriate reply.
- Your application sends the reply via the Telnyx API. Your app calls the Messaging API with the sender's number as the destination, your Telnyx number as the sender, and the generated response as the message body. The customer receives the reply as a standard SMS.
No polling. No message queues. One webhook in, one API call out.
The Architecture
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Customer │ ──SMS──►│ Telnyx Messaging │ ──POST─►│ Your App │
│ (phone) │ │ API │ │ (Flask) │
└──────────────┘ └──────────────────┘ └──────────────┘
▲ │
│ │ SDK call
└──────────────────────────┘
Your Python Flask application receives inbound SMS as webhook events and sends replies through the Telnyx Python SDK. Every webhook is signed with Ed25519 for cryptographic verification.
Prerequisites
- Python 3.8 or higher (Python 3.11+ recommended)
- A Telnyx account — sign up at portal.telnyx.com for free trial credit
- A Telnyx phone number with messaging capability
- A Messaging Profile configured in the Telnyx Portal with a webhook URL
ngrokor another tunnel for local webhook testing
Step 1: Clone and Configure
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/sms-auto-reply-bot-python
pip install -r requirements.txt
cp .env.example .env
Edit .env with your credentials:
TELNYX_API_KEY=KEY_your_api_key_here
TELNYX_PUBLIC_KEY=your_public_key_here
TELNYX_PHONE_NUMBER=+15551234567
Step 2: Initialize the Telnyx Client
The Telnyx Python SDK uses a single constructor with your API key and public key for webhook verification:
import telnyx
from dotenv import load_dotenv
load_dotenv()
client = telnyx.Telnyx(
api_key=os.getenv("TELNYX_API_KEY"),
public_key=os.getenv("TELNYX_PUBLIC_KEY"),
)
One client, one API key, one bill. Telnyx is an AI Communications Infrastructure platform — voice, messaging, verification, AI inference, and IoT all live behind the same authentication surface.
Step 3: Build the Keyword Router
The response generator maps incoming message content to contextual replies:
def generate_response(incoming_message: str, sender_number: str) -> str:
message_lower = incoming_message.lower().strip()
current_hour = datetime.now().hour
is_business_hours = 9 <= current_hour <= 17
if any(word in message_lower for word in ['help', 'support', 'assistance']):
if is_business_hours:
return "Hi! Our support team is available now. Please call (555) 123-4567."
else:
return "Thanks for reaching out! Our support hours are 9 AM - 5 PM."
elif any(word in message_lower for word in ['hours', 'open', 'closed']):
return "We're open Monday-Friday 9 AM to 5 PM EST."
elif any(word in message_lower for word in ['price', 'cost', 'pricing', 'quote']):
return "Thanks for your interest! Visit our website or call (555) 123-4567."
elif message_lower in ['stop', 'unsubscribe', 'opt out']:
return "You've been unsubscribed. Reply START to opt back in."
elif message_lower in ['start', 'subscribe', 'opt in']:
return "Welcome! You're now subscribed. Reply STOP anytime to unsubscribe."
else:
if is_business_hours:
return "Thanks for your message! A team member will respond shortly."
else:
return "Thanks for contacting us! We'll respond during business hours."
The business-hours check uses datetime.now().hour. For production, you'd use timezone-aware timestamps and pull your schedule from a configuration store.
Step 4: Send the Reply
The reply function uses the Telnyx SDK to send the response:
def send_auto_reply(to_number: str, message: str) -> dict:
from_number = os.getenv("TELNYX_PHONE_NUMBER")
response = client.messages.create(
from_=from_number,
to=to_number,
text=message,
)
return {
"message_id": response.data.id,
"status": response.data.to[0].status if response.data.to else "unknown",
"from": from_number,
"to": to_number,
"text": message,
}
The SDK response object is not JSON-serializable by default, so the function extracts the fields you need into a plain dict.
Step 5: Handle the Webhook
The webhook handler ties everything together. It verifies the Ed25519 signature, extracts the message, generates a response, and sends the reply:
@app.route("/webhooks/sms", methods=["POST"])
def handle_sms_webhook():
# Verify the Telnyx Ed25519 signature
try:
client.webhooks.unwrap(
request.get_data(as_text=True),
headers=dict(request.headers),
)
except Exception:
return jsonify({"error": "invalid signature"}), 401
webhook_data = request.get_json()
event_type = webhook_data.get("data", {}).get("event_type")
if event_type != "message.received":
return jsonify({"message": "Event ignored"}), 200
payload = webhook_data.get("data", {}).get("payload", {})
from_number = payload.get("from", {}).get("phone_number")
message_text = payload.get("text", "")
response_text = generate_response(message_text, from_number)
reply_result = send_auto_reply(from_number, response_text)
return jsonify({
"status": "success",
"original_message": message_text,
"response_sent": response_text,
"reply_id": reply_result["message_id"],
}), 200
The signature verification happens before any processing. This ensures that only legitimate Telnyx events trigger your auto-reply logic — no spoofed requests, no replay attacks.
Step 6: Handle Errors
The Telnyx SDK throws typed errors that map cleanly to HTTP status codes:
except telnyx.AuthenticationError:
return jsonify({"error": "Invalid API key"}), 401
except telnyx.RateLimitError:
return jsonify({"error": "Rate limit exceeded"}), 429
except telnyx.APIStatusError as e:
return jsonify({"error": "API request failed"}), e.status_code
except telnyx.APIConnectionError:
return jsonify({"error": "Network error connecting to Telnyx"}), 503
These typed errors mean your monitoring dashboard can differentiate between authentication failures, rate limits, API errors, and network issues — each with a meaningful HTTP status code.
Step 7: Run It
python app.py
In another terminal, expose your local server:
ngrok http 5000
Set the ngrok URL as your Messaging Profile's webhook URL in the Telnyx Portal. Now text your Telnyx number:
curl -X POST http://localhost:5000/webhooks/sms \
-H "Content-Type: application/json" \
-d '{
"data": {
"event_type": "message.received",
"payload": {
"from": {"phone_number": "+12125551234"},
"to": [{"phone_number": "+13105559876"}],
"text": "What are your hours?"
}
}
}'
The bot replies with your operating schedule. Try "help" during and after business hours to see the context-aware routing in action.
Why This Pattern Works for Production
1. Signature verification at the boundary. The Ed25519 verification happens before any business logic runs. This is the correct place for authentication — at the HTTP boundary, not deep in the handler.
2. Keyword routing is data, not code. The generate_response function maps keywords to responses. In production, you'd pull these mappings from a database or configuration service so business teams can update responses without deploying code.
3. Opt-in/opt-out is handled. The bot respects STOP and START keywords for TCPA compliance. In production, you'd persist opt-out state in a database and check it before sending any reply.
Extending the Example
- AI-powered responses — Replace keyword routing with Telnyx AI Inference to generate contextual responses from a knowledge base.
- Multi-language support — Detect the message language and route to localized responses.
- CRM integration — Look up the sender's number in your CRM and personalize the response with their name and account details.
- Escalation — If the keyword router doesn't match, forward the message to a human agent via Slack or email.
The Bigger Picture: Programmable Messaging Infrastructure
An SMS autoresponder is a starting point. The same webhook pattern — receive an event, process it, send a response — powers every messaging application. Chatbots, order notifications, appointment reminders, two-factor authentication, and customer surveys all follow the same loop.
Telnyx is AI Communications Infrastructure. The same API key that sends SMS also handles voice calls, WhatsApp, SIP trunking, and AI inference. One platform, one bill, one support team that owns the delivery path from your application to the customer's phone.
Get the Code
The full example is in the telnyx-code-examples repository under sms-auto-reply-bot-python. It includes:
app.py— the complete Flask applicationREADME.md— quickstart and API reference.env.example— environment variable template
Clone it, add your credentials, and you have a running SMS autoresponder.