Tutorial Factory

Powered by Telnyx

Dev Docs Low Latency Club GitHub Log in
AI Assistants Python Flask

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."""
    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."""
    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 asst_123 with an actual assistant ID):

curl -X POST http://localhost:5000/assistants/asst_123/clone \
  -H "Content-Type: application/json"

Expected response:

{
  "id": "asst_456",
  "name": "Customer Support Assistant (Copy)",
  "model": "meta-llama/Meta-Llama-3.1-70B-Instruct",
  "instructions": "You are a helpful customer support assistant...",
  "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."""
    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."""
    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 a 404 error stating "Assistant not found" when trying to clone. Confirm the assistant ID exists by first listing your assistants or checking the Telnyx Portal. Ensure you're using the correct assistant ID format (typically starts with asst_). The assistant must be in your account and not deleted.
Clone Operation Fails The cloning request returns a 500 error or unexpected API response. Verify the source assistant has valid configuration and is not corrupted. Some assistants with complex tool configurations may require additional permissions. Check that your API key has the necessary permissions for AI assistant operations in the Telnyx Portal.

Next Steps