Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.neuracall.com/llms.txt

Use this file to discover all available pages before exploring further.

Instalacion

pip install requests

Cliente Completo

import os
import json
import time
import requests
from typing import Optional, Dict, Any, List

class NeuracallClient:
    """Cliente para la API de Neuracall"""

    def __init__(
        self,
        client_id: Optional[str] = None,
        client_secret: Optional[str] = None,
        base_url: str = "https://api.neuracall.com"
    ):
        self.base_url = base_url
        self.client_id = client_id or os.environ["NEURACALL_CLIENT_ID"]
        self.client_secret = client_secret or os.environ["NEURACALL_CLIENT_SECRET"]
        self._token = None
        self._expires_at = 0

    def _get_token(self) -> str:
        """Obtiene o renueva el token de acceso"""
        if time.time() > self._expires_at - 300:
            response = requests.post(
                f"{self.base_url}/v1/auth",
                json={
                    "client_id": self.client_id,
                    "client_secret": self.client_secret
                }
            )
            response.raise_for_status()
            data = response.json()
            self._token = data["access_token"]
            self._expires_at = data["expires_at"]
        return self._token

    def _headers(self) -> Dict[str, str]:
        """Headers con autorizacion"""
        return {"Authorization": f"Bearer {self._get_token()}"}

    # ==================== CALL ANALYSIS ====================

    def create_analysis(
        self,
        audio_path: str,
        external_id: str,
        agent_id: str,
        agent_name: str,
        model_name: str,
        additional_parameters: Optional[Dict] = None
    ) -> Dict[str, Any]:
        """
        Crea un nuevo análisis de llamada.

        Args:
            audio_path: Ruta al archivo de audio
            external_id: ID externo de la llamada
            agent_id: ID del agente
            agent_name: Nombre del agente
            model_name: Nombre del modelo de análisis
            additional_parameters: Metadatos adicionales opciónales

        Returns:
            Datos del análisis creado
        """
        with open(audio_path, "rb") as audio_file:
            files = {"audio_file": audio_file}
            data = {
                "external_id": external_id,
                "agent_id": agent_id,
                "agent_name": agent_name,
                "model_name": model_name
            }
            if additional_parameters:
                data["additional_parameters"] = json.dumps(additional_parameters)

            response = requests.post(
                f"{self.base_url}/v1/call-analysis",
                headers=self._headers(),
                files=files,
                data=data
            )
            response.raise_for_status()
            return response.json()

    def get_analysis(self, analysis_id: str) -> Dict[str, Any]:
        """Obtiene un análisis por ID"""
        response = requests.get(
            f"{self.base_url}/v1/call-analysis/{analysis_id}",
            headers=self._headers()
        )
        response.raise_for_status()
        return response.json()

    def list_analyses(
        self,
        model_id: Optional[int] = None,
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
        page: int = 0,
        size: int = 20
    ) -> Dict[str, Any]:
        """Lista análisis con filtros y paginación"""
        params = {"page": page, "size": size}
        if model_id:
            params["model_id"] = model_id
        if start_date:
            params["start_date"] = start_date
        if end_date:
            params["end_date"] = end_date

        response = requests.get(
            f"{self.base_url}/v1/call-analysis",
            headers=self._headers(),
            params=params
        )
        response.raise_for_status()
        return response.json()

    def get_transcription(self, analysis_id: str) -> Dict[str, Any]:
        """Obtiene la transcripción de un análisis"""
        response = requests.get(
            f"{self.base_url}/v1/call-analysis/{analysis_id}/transcription",
            headers=self._headers()
        )
        response.raise_for_status()
        return response.json()

    def wait_for_completion(
        self,
        analysis_id: str,
        timeout: int = 600,
        poll_interval: int = 5
    ) -> Dict[str, Any]:
        """
        Espera a que un análisis se complete.

        Args:
            analysis_id: ID del análisis
            timeout: Tiempo máximo de espera en segúndos
            poll_interval: Intervalo entre consultas en segúndos

        Returns:
            Datos del análisis completado
        """
        start = time.time()
        while time.time() - start < timeout:
            result = self.get_analysis(analysis_id)
            status = result["status"]

            if status == "PROCESS_COMPLETED":
                return result
            elif status == "ERROR":
                raise Exception(f"Analysis failed: {result.get('error_message')}")

            time.sleep(poll_interval)

        raise TimeoutError(f"Analysis {analysis_id} did not complete within {timeout}s")

    # ==================== MODELS ====================

    def list_models(self, order_by_name: bool = False) -> List[Dict[str, Any]]:
        """Lista todos los modelos de análisis"""
        response = requests.get(
            f"{self.base_url}/v1/models",
            headers=self._headers(),
            params={"order_by_name": order_by_name}
        )
        response.raise_for_status()
        return response.json()

    def get_model(self, model_id: int) -> Dict[str, Any]:
        """Obtiene un modelo por ID"""
        response = requests.get(
            f"{self.base_url}/v1/models/{model_id}",
            headers=self._headers()
        )
        response.raise_for_status()
        return response.json()

    def create_model(
        self,
        name: str,
        description: str,
        prompt: str
    ) -> Dict[str, Any]:
        """Crea un nuevo modelo de análisis"""
        response = requests.post(
            f"{self.base_url}/v1/models",
            headers=self._headers(),
            json={
                "name": name,
                "description": description,
                "prompt": prompt
            }
        )
        response.raise_for_status()
        return response.json()

    # ==================== MODEL CATEGORIES ====================

    def list_categories(self, model_id: int) -> List[Dict[str, Any]]:
        """Lista las categorías de un modelo"""
        response = requests.get(
            f"{self.base_url}/v1/models/{model_id}/categories",
            headers=self._headers()
        )
        response.raise_for_status()
        return response.json()

    def create_category(
        self,
        model_id: int,
        name: str,
        description: str,
        order_number: int
    ) -> Dict[str, Any]:
        """Crea una categoría en un modelo"""
        response = requests.post(
            f"{self.base_url}/v1/models/{model_id}/categories",
            headers=self._headers(),
            json={
                "name": name,
                "description": description,
                "order_number": order_number
            }
        )
        response.raise_for_status()
        return response.json()

    # ==================== MODEL VARIABLES ====================

    def list_variables(self, model_id: int) -> List[Dict[str, Any]]:
        """Lista las variables de un modelo"""
        response = requests.get(
            f"{self.base_url}/v1/models/{model_id}/variables",
            headers=self._headers()
        )
        response.raise_for_status()
        return response.json()

    def create_variable(
        self,
        model_id: int,
        key: str,
        readable_name: str,
        description: str,
        var_type: str,
        weight: int,
        category_id: int,
        required: bool = True
    ) -> Dict[str, Any]:
        """Crea una variable en un modelo"""
        response = requests.post(
            f"{self.base_url}/v1/models/{model_id}/variables",
            headers=self._headers(),
            json={
                "key": key,
                "readable_name": readable_name,
                "description": description,
                "type": var_type,
                "weight": weight,
                "category_id": category_id,
                "required": required
            }
        )
        response.raise_for_status()
        return response.json()

    # ==================== MODEL METADATA ====================

    def list_metadata(self, model_id: int) -> List[Dict[str, Any]]:
        """Lista los campos de metadata de un modelo"""
        response = requests.get(
            f"{self.base_url}/v1/models/{model_id}/metadata",
            headers=self._headers()
        )
        response.raise_for_status()
        return response.json()

    def create_metadata(
        self,
        model_id: int,
        key: str,
        readable_name: str,
        meta_type: str,
        grouping_field: bool = False
    ) -> Dict[str, Any]:
        """Crea un campo de metadata en un modelo"""
        response = requests.post(
            f"{self.base_url}/v1/models/{model_id}/metadata",
            headers=self._headers(),
            json={
                "key": key,
                "readable_name": readable_name,
                "type": meta_type,
                "grouping_field": grouping_field
            }
        )
        response.raise_for_status()
        return response.json()

    # ==================== STATISTICS ====================

    def get_basic_stats(
        self,
        start_date: str,
        end_date: str,
        model_name: str
    ) -> Dict[str, Any]:
        """Obtiene estadísticas básicas"""
        response = requests.get(
            f"{self.base_url}/v1/stats/basic",
            headers=self._headers(),
            params={
                "start_date": start_date,
                "end_date": end_date,
                "model_name": model_name
            }
        )
        response.raise_for_status()
        return response.json()


# ==================== EJEMPLO DE USO ====================

if __name__ == "__main__":
    # Inicializar cliente (usa variables de entorno)
    client = NeuracallClient()

    # Listar modelos disponibles
    print("Modelos disponibles:")
    models = client.list_models()
    for model in models:
        print(f"  - {model['name']} (ID: {model['id']})")

    # Crear análisis
    print("\nCreando análisis...")
    analysis = client.create_analysis(
        audio_path="llamada.mp3",
        external_id="CALL-2024-001",
        agent_id="AGT-001",
        agent_name="Juan Perez",
        model_name=models[0]["name"]
    )
    print(f"Análisis creado: {analysis['id']}")
    print(f"Estado: {analysis['status']}")

    # Esperar a que complete
    print("\nEsperando resultado...")
    result = client.wait_for_completion(analysis["id"])

    # Mostrar resultados
    print(f"\n=== RESULTADO ===")
    print(f"NeuraScore: {result['score_percentage']}%")
    print(f"Resumen: {result['summary']}")

    if result.get("insights"):
        print("\nInsights:")
        for i, insight in enumerate(result["insights"], 1):
            print(f"  {i}. {insight}")

    if result.get("keywords"):
        print(f"\nKeywords: {', '.join(result['keywords'])}")

Uso Rápido

from neuracall_client import NeuracallClient

client = NeuracallClient()

# Analizar una llamada
result = client.create_analysis(
    audio_path="llamada.mp3",
    external_id="CALL-001",
    agent_id="AGT-001",
    agent_name="Juan Perez",
    model_name="Evaluación Servicio"
)

# Esperar y obtener resultados
completed = client.wait_for_completion(result["id"])
print(f"Score: {completed['score_percentage']}%")

Manejo de Errores

import requests

try:
    result = client.create_analysis(...)
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 401:
        print("Error de autenticación")
    elif e.response.status_code == 400:
        print(f"Datos invalidos: {e.response.json()}")
    else:
        print(f"Error HTTP: {e}")
except Exception as e:
    print(f"Error: {e}")

Gestión de Modelos

Crear un Modelo Completo

from neuracall_client import NeuracallClient

client = NeuracallClient()

# 1. Crear el modelo base
model = client.create_model(
    name="Evaluación Ventas Q1",
    description="Modelo para evaluar llamadas de ventas",
    prompt="Evalúa la llamada considerando: saludo, identificación de necesidades, presentación y cierre."
)
model_id = model["id"]
print(f"Modelo creado: {model['name']} (ID: {model_id})")

# 2. Crear categorías
categories_data = [
    {"name": "Apertura", "description": "Saludo e identificación", "order_number": 1},
    {"name": "Descubrimiento", "description": "Identificación de necesidades", "order_number": 2},
    {"name": "Presentación", "description": "Exposición de la solución", "order_number": 3},
    {"name": "Cierre", "description": "Confirmación y siguientes pasos", "order_number": 4}
]

categories = {}
for cat_data in categories_data:
    cat = client.create_category(
        model_id=model_id,
        name=cat_data["name"],
        description=cat_data["description"],
        order_number=cat_data["order_number"]
    )
    categories[cat["name"]] = cat["id"]
    print(f"  Categoría creada: {cat['name']} (ID: {cat['id']})")

# 3. Crear variables
variables_data = [
    # Apertura (20 puntos)
    {"key": "greeting", "readable_name": "Saludo", "description": "Saludo cordial y profesional",
     "type": "INTEGER", "weight": 10, "category": "Apertura"},
    {"key": "identification", "readable_name": "Identificación", "description": "Se identificó correctamente",
     "type": "INTEGER", "weight": 10, "category": "Apertura"},

    # Descubrimiento (25 puntos)
    {"key": "open_questions", "readable_name": "Preguntas Abiertas", "description": "Usó preguntas abiertas",
     "type": "INTEGER", "weight": 15, "category": "Descubrimiento"},
    {"key": "active_listening", "readable_name": "Escucha Activa", "description": "Demostró escucha activa",
     "type": "INTEGER", "weight": 10, "category": "Descubrimiento"},

    # Presentación (30 puntos)
    {"key": "benefits", "readable_name": "Presentación de Beneficios", "description": "Presentó beneficios claramente",
     "type": "INTEGER", "weight": 15, "category": "Presentación"},
    {"key": "objections", "readable_name": "Manejo de Objeciones", "description": "Manejó objeciones efectivamente",
     "type": "INTEGER", "weight": 15, "category": "Presentación"},

    # Cierre (25 puntos)
    {"key": "proposal", "readable_name": "Propuesta Clara", "description": "Hizo una propuesta clara",
     "type": "INTEGER", "weight": 10, "category": "Cierre"},
    {"key": "next_steps", "readable_name": "Próximos Pasos", "description": "Confirmó los próximos pasos",
     "type": "INTEGER", "weight": 15, "category": "Cierre"},

    # Variable de extracción (sin peso)
    {"key": "product_interest", "readable_name": "Producto de Interés", "description": "Producto mencionado",
     "type": "STRING", "weight": 0, "category": "Descubrimiento", "required": False}
]

for var_data in variables_data:
    var = client.create_variable(
        model_id=model_id,
        key=var_data["key"],
        readable_name=var_data["readable_name"],
        description=var_data["description"],
        var_type=var_data["type"],
        weight=var_data["weight"],
        category_id=categories[var_data["category"]],
        required=var_data.get("required", True)
    )
    print(f"  Variable creada: {var['readable_name']} (peso: {var['weight']})")

# 4. Crear campos de metadata
metadata_fields = [
    {"key": "campaign", "readable_name": "Campaña", "type": "STRING", "grouping_field": True},
    {"key": "product", "readable_name": "Producto", "type": "STRING", "grouping_field": True},
    {"key": "region", "readable_name": "Región", "type": "STRING", "grouping_field": True}
]

for meta_data in metadata_fields:
    meta = client.create_metadata(
        model_id=model_id,
        key=meta_data["key"],
        readable_name=meta_data["readable_name"],
        meta_type=meta_data["type"],
        grouping_field=meta_data["grouping_field"]
    )
    print(f"  Metadata creada: {meta['readable_name']} (agrupación: {meta['grouping_field']})")

print(f"\nModelo '{model['name']}' configurado completamente!")

Listar Configuración de un Modelo

# Obtener toda la configuración de un modelo
model_id = 15

model = client.get_model(model_id)
print(f"Modelo: {model['name']}")

categories = client.list_categories(model_id)
print(f"\nCategorías ({len(categories)}):")
for cat in categories:
    print(f"  - {cat['name']} (orden: {cat['order_number']})")

variables = client.list_variables(model_id)
print(f"\nVariables ({len(variables)}):")
total_weight = 0
for var in variables:
    print(f"  - {var['readable_name']}: {var['type']} (peso: {var['weight']})")
    total_weight += var['weight']
print(f"  Total de pesos: {total_weight}")

metadata = client.list_metadata(model_id)
print(f"\nMetadata ({len(metadata)}):")
for meta in metadata:
    grouping = "✓" if meta['grouping_field'] else "✗"
    print(f"  - {meta['readable_name']}: {meta['type']} (agrupación: {grouping})")

Manejo de Errores en Modelos

import requests

try:
    # Intentar modificar un modelo con análisis existentes
    client.create_variable(
        model_id=15,
        key="new_variable",
        readable_name="Nueva Variable",
        description="...",
        var_type="INTEGER",
        weight=10,
        category_id=42
    )
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 409:
        print("Error: No se puede modificar un modelo que ya tiene análisis asociados.")
        print("Solución: Crea una nueva versión del modelo.")
    elif e.response.status_code == 404:
        print("Error: El modelo o categoría no existe.")
    else:
        print(f"Error HTTP: {e.response.status_code} - {e.response.json()}")