Voice AI

Build an SMS Auto-Reply Bot With the Telnyx Messaging API

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:

  1. A customer sends an SMS to your Telnyx number. Telnyx receives the message and forwards it to your application as a message.received webhook event. The payload includes the sender's phone number, your Telnyx number, and the message text.
  1. 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.
  1. 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
  • ngrok or 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 application
  • README.md — quickstart and API reference
  • .env.example — environment variable template

Clone it, add your credentials, and you have a running SMS autoresponder.

Related Examples

Frequently Asked Questions

No FAQs yet.

Ready to build with low-latency voice AI?

Join developers building the future of real-time conversations