Clone AI Assistant with Python and Flask
Overview
Build a Flask endpoint that clones existing AI assistants using the Telnyx AI Assistants API. This tutorial demonstrates how to duplicate assistants with their configurations, handle API responses properly, and implement production-ready error handling for AI assistant management workflows.
Prerequisites
- Python 3.8 or higher.
- A Telnyx account with an active API key from the Telnyx Portal.
- At least one existing AI assistant in your Telnyx account to clone.
- pip (Python package manager).
Step 1: Setup
Install the required dependencies:
pip install telnyx flask python-dotenv
Create a project directory and navigate into it:
mkdir telnyx-ai-clone
cd telnyx-ai-clone
Step 2: Configuration
Create a .env file in your project root to store credentials securely:
TELNYX_API_KEY=YOUR_API_KEY_HERE
Replace YOUR_API_KEY_HERE with your actual API key from the Telnyx Portal.
Step 3: Implementation
Create app.py and initialize the Telnyx client. Define a helper function to handle assistant cloning with proper validation:
import os
import telnyx
from dotenv import load_dotenv
load_dotenv()
# Initialize client with the new SDK pattern
client = telnyx.Telnyx(api_key=os.getenv("TELNYX_API_KEY"))
def clone_assistant(assistant_id: str) -> dict:
"""Clone an AI assistant and return JSON-serializable response data."""
if not assistant_id:
raise ValueError("Assistant ID is required")
# Use client.ai_assistants.clone() — NOT client.ai_assistants.clone()
response = client.ai_assistants.clone(assistant_id)
# Extract serializable data — SDK objects are NOT JSON-serializable
return {
"id": response.data.id,
"name": response.data.name,
"model": response.data.model,
"instructions": response.data.instructions,
"tools": response.data.tools,
"enabled_features": response.data.enabled_features,
"created_at": response.data.created_at,
}
def get_assistant_details(assistant_id: str) -> dict:
"""Retrieve assistant details for verification purposes."""
response = client.ai_assistants.retrieve(assistant_id)
return {
"id": response.data.id,
"name": response.data.name,
"model": response.data.model,
"instructions": response.data.instructions,
"enabled_features": response.data.enabled_features,
"created_at": response.data.created_at,
}
Step 4: Testing
Add Flask routes with comprehensive error handling for production resilience:
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route("/assistants/<assistant_id>/clone", methods=["POST"])
def clone_assistant_endpoint(assistant_id: str):
"""HTTP endpoint to clone an AI assistant."""
if not assistant_id:
return jsonify({"error": "Assistant ID is required"}), 400
try:
result = clone_assistant(assistant_id)
return jsonify(result), 201
except telnyx.AuthenticationError:
return jsonify({"error": "Invalid API key"}), 401
except telnyx.RateLimitError:
return jsonify({"error": "Rate limit exceeded. Please slow down."}), 429
except telnyx.APIStatusError as e:
if e.status_code == 404:
return jsonify({"error": "Assistant not found"}), 404
return jsonify({"error": str(e), "status_code": e.status_code}), e.status_code
except telnyx.APIConnectionError:
return jsonify({"error": "Network error connecting to Telnyx"}), 503
except ValueError as e:
return jsonify({"error": str(e)}), 400
@app.route("/assistants/<assistant_id>", methods=["GET"])
def get_assistant_endpoint(assistant_id: str):
"""HTTP endpoint to retrieve assistant details."""
if not assistant_id:
return jsonify({"error": "Assistant ID is required"}), 400
try:
result = get_assistant_details(assistant_id)
return jsonify(result), 200
except telnyx.AuthenticationError:
return jsonify({"error": "Invalid API key"}), 401
except telnyx.RateLimitError:
return jsonify({"error": "Rate limit exceeded. Please slow down."}), 429
except telnyx.APIStatusError as e:
if e.status_code == 404:
return jsonify({"error": "Assistant not found"}), 404
return jsonify({"error": str(e), "status_code": e.status_code}), e.status_code
except telnyx.APIConnectionError:
return jsonify({"error": "Network error connecting to Telnyx"}), 503
if __name__ == "__main__":
app.run(debug=True, port=5000)
Start the server:
python app.py
Test the cloning endpoint using curl (replace assistant_123 with an actual assistant ID):
curl -X POST http://localhost:5000/assistants/assistant_123/clone \
-H "Content-Type: application/json"
Expected response:
{
"id": "assistant_456",
"name": "Customer Support Assistant (Copy)",
"model": "meta-llama/Meta-Llama-3.1-70B-Instruct",
"instructions": "You are a helpful customer support agent...",
"tools": [],
"enabled_features": ["telephony", "messaging"],
"created_at": "2026-04-21T10:30:00Z"
}
Complete Code
#!/usr/bin/env python3
"""Production-ready Flask endpoint for cloning AI assistants via Telnyx."""
import os
import telnyx
from dotenv import load_dotenv
from flask import Flask, jsonify, request
load_dotenv()
app = Flask(__name__)
# Initialize client with the new SDK pattern
client = telnyx.Telnyx(api_key=os.getenv("TELNYX_API_KEY"))
def clone_assistant(assistant_id: str) -> dict:
"""Clone an AI assistant and return JSON-serializable response data."""
if not assistant_id:
raise ValueError("Assistant ID is required")
# Use client.ai_assistants.clone() — NOT client.ai_assistants.clone()
response = client.ai_assistants.clone(assistant_id)
# Extract serializable data — SDK objects are NOT JSON-serializable
return {
"id": response.data.id,
"name": response.data.name,
"model": response.data.model,
"instructions": response.data.instructions,
"tools": response.data.tools,
"enabled_features": response.data.enabled_features,
"created_at": response.data.created_at,
}
def get_assistant_details(assistant_id: str) -> dict:
"""Retrieve assistant details for verification purposes."""
response = client.ai_assistants.retrieve(assistant_id)
return {
"id": response.data.id,
"name": response.data.name,
"model": response.data.model,
"instructions": response.data.instructions,
"enabled_features": response.data.enabled_features,
"created_at": response.data.created_at,
}
@app.route("/assistants/<assistant_id>/clone", methods=["POST"])
def clone_assistant_endpoint(assistant_id: str):
"""HTTP endpoint to clone an AI assistant."""
if not assistant_id:
return jsonify({"error": "Assistant ID is required"}), 400
try:
result = clone_assistant(assistant_id)
return jsonify(result), 201
except telnyx.AuthenticationError:
return jsonify({"error": "Invalid API key"}), 401
except telnyx.RateLimitError:
return jsonify({"error": "Rate limit exceeded. Please slow down."}), 429
except telnyx.APIStatusError as e:
if e.status_code == 404:
return jsonify({"error": "Assistant not found"}), 404
return jsonify({"error": str(e), "status_code": e.status_code}), e.status_code
except telnyx.APIConnectionError:
return jsonify({"error": "Network error connecting to Telnyx"}), 503
except ValueError as e:
return jsonify({"error": str(e)}), 400
@app.route("/assistants/<assistant_id>", methods=["GET"])
def get_assistant_endpoint(assistant_id: str):
"""HTTP endpoint to retrieve assistant details."""
if not assistant_id:
return jsonify({"error": "Assistant ID is required"}), 400
try:
result = get_assistant_details(assistant_id)
return jsonify(result), 200
except telnyx.AuthenticationError:
return jsonify({"error": "Invalid API key"}), 401
except telnyx.RateLimitError:
return jsonify({"error": "Rate limit exceeded. Please slow down."}), 429
except telnyx.APIStatusError as e:
if e.status_code == 404:
return jsonify({"error": "Assistant not found"}), 404
return jsonify({"error": str(e), "status_code": e.status_code}), e.status_code
except telnyx.APIConnectionError:
return jsonify({"error": "Network error connecting to Telnyx"}), 503
if __name__ == "__main__":
app.run(debug=True, port=5000)
Troubleshooting
| Issue | Problem | Solution |
|---|---|---|
| Authentication Error (401) | The endpoint returns {"error": "Invalid API key"} with HTTP 401. |
Verify your TELNYX_API_KEY in the .env file matches the key shown in the Telnyx Portal. Ensure there are no trailing spaces or quotes. If the key was regenerated recently, update your environment file and restart the Flask server. |
| Assistant Not Found (404) | You receive {"error": "Assistant not found"} when trying to clone. |
Confirm the assistant ID exists by first listing your assistants or checking the Telnyx Portal. The assistant ID should be in the format assistant_123abc. Ensure you're using the correct account and the assistant hasn't been deleted. |
| Clone Operation Fails | The cloning request returns a 500 error or times out unexpectedly. | Check that the original assistant is in a valid state and not currently being modified. Some assistants with complex configurations may take longer to clone. Verify your account has sufficient quota for creating new assistants. Try cloning a simpler assistant first to test the endpoint. |