
Conecta números virtuales netelip con cualquier motor de voz IA usando LiveKit como capa de telefonía.
Esta guía cubre la configuración completa: desde el SIP Trunk en netelip hasta el despliegue en producción, incluyendo ejemplos de código funcionales y casos de uso reales.
LiveKit es una plataforma open-source de comunicación en tiempo real. Su módulo de telefonía actúa como puente entre el mundo SIP (llamadas telefónicas) y WebRTC (comunicación web en tiempo real). En la práctica, permite que un número virtual de netelip reciba o haga llamadas procesadas por un agente de inteligencia artificial.
LiveKit no es un motor de voz. Es la infraestructura que conecta la telefonía con cualquier motor de voz IA que elijas.
Ventajas de usar LiveKit con netelip:
- Números virtuales geográficos, 900 e internacionales de netelip conectados directamente a agentes IA
- Compatible con más de 15 proveedores de IA simultáneamente
- Grabación de llamadas nativa
- Transferencia de llamadas (fría y caliente)
- Soporte DTMF para menús IVR
- Audio HD, cancelación de ruido, region pinning
- Open-source: sin vendor lock-in
| Cuenta / Servicio | Dónde conseguirlo |
|---|---|
| netelip SIP Trunk | panel.netelip.com — contratar SIP Trunk |
| Número virtual netelip | panel.netelip.com — geográfico, 900, etc. |
| Cuenta LiveKit Cloud | cloud.livekit.io — plan gratuito disponible |
| API Key + Secret de LiveKit | Dashboard de LiveKit Cloud |
| Cuenta en proveedor de IA | Ver sección 4 de esta guía |
| Servidor para el agente | VPS, Modal.com, Railway, Fly.io, etc. |
| Python 3.9+ o Node.js 18+ | Para el LiveKit Agents SDK |
- Servidor SIP:
siptrunk.netelip.com(o el que te indique netelip) - Puerto:
5060(UDP/TCP) o5061(TLS) - Usuario SIP: proporcionado por netelip
- Contraseña SIP: proporcionada por netelip
- Número virtual: el DID asignado (ej.
+34910123456)
Codecs. netelip soporta G.711 (PCMA/PCMU) y G.729. LiveKit trabaja con Opus y G.711. La negociación de codec es automática.
Si ya usas la Centralita Virtual de netelip. Puedes configurar una extensión específica para redirigir llamadas al SIP Trunk de LiveKit, manteniendo el resto de extensiones funcionando con normalidad.
El Inbound Trunk le dice a LiveKit desde dónde esperar llamadas SIP entrantes. Puedes crearlo de tres formas:
- Ve a
cloud.livekit.io> tu proyecto > Telephony > SIP Trunks - Crea un nuevo Inbound Trunk
- Name:
netelip-inbound - Allowed addresses: la IP o rango de IPs de netelip (consulta con soporte netelip)
- Numbers:
+34910123456(tu DID de netelip, formato E.164) - Auth: username + password proporcionados
lk sip inbound create inbound-trunk.json
Contenido de inbound-trunk.json:
{
"trunk": {
"name": "netelip-inbound",
"numbers": ["+34910123456"],
"auth_username": "tu_usuario_netelip",
"auth_password": "tu_contrasena_netelip",
"allowed_addresses": ["siptrunk.netelip.com"]
}
}
from livekit.api import LiveKitAPI, CreateSIPInboundTrunkRequest, SIPInboundTrunkInfo
async def create_inbound():
api = LiveKitAPI()
trunk = await api.sip.create_sip_inbound_trunk(
CreateSIPInboundTrunkRequest(
trunk=SIPInboundTrunkInfo(
name="netelip-inbound",
numbers=["+34910123456"],
auth_username="tu_usuario_netelip",
auth_password="tu_contrasena_netelip",
allowed_addresses=["siptrunk.netelip.com"],
)
)
)
print(f"Trunk ID: {trunk.sip_trunk_id}")
Después de crear el Inbound Trunk. LiveKit te proporcionará una dirección SIP de destino (algo como sip:xxxxx@sip.livekit.cloud). Configura esta dirección como destino del DID en netelip.
El Outbound Trunk permite que tu agente IA haga llamadas salientes a través de netelip.
lk sip outbound create outbound-trunk.json
Contenido de outbound-trunk.json:
{
"trunk": {
"name": "netelip-outbound",
"address": "siptrunk.netelip.com",
"numbers": ["+34910123456"],
"auth_username": "tu_usuario_netelip",
"auth_password": "tu_contrasena_netelip",
"transport": "udp"
}
}
Para hacer una llamada saliente desde el agente:
from livekit.api import LiveKitAPI, CreateSIPParticipantRequest
async def call_outbound(room_name, to_number):
api = LiveKitAPI()
participant = await api.sip.create_sip_participant(
CreateSIPParticipantRequest(
sip_trunk_id="TK_xxxxxxxxxxxx", # ID del outbound trunk
sip_call_to=to_number, # ej. "+34666123456"
room_name=room_name,
participant_name="llamada-saliente",
)
)
return participant
Las Dispatch Rules determinan qué sucede cuando llega una llamada. La más común: enviar la llamada a un agente IA específico.
lk sip dispatch create dispatch-rule.json
Opción A: Sala individual por llamada (recomendado)
{
"rule": {
"trunk_ids": ["TK_xxxxxxxxxxxx"],
"rule": {
"dispatchRuleIndividual": {
"room_prefix": "call-"
}
}
}
}
Opción B: Sala fija para todas las llamadas
{
"rule": {
"trunk_ids": ["TK_xxxxxxxxxxxx"],
"rule": {
"dispatchRuleDirect": {
"room_name": "agente-recepcion"
}
}
}
}
LiveKit Agents SDK tiene un sistema de plugins que soporta los principales proveedores de IA. Puedes combinarlos libremente.
Estos modelos reciben audio y devuelven audio directamente, sin pasos intermedios. Menor latencia, conversación más natural.
OpenAI Realtime API
| Parámetro | Valor |
|---|---|
| Plugin | livekit-plugins-openai (clase RealtimeModel) |
| Modelo | gpt-4o-realtime-preview |
| Latencia | ~300ms |
| Voces | alloy, ash, ballad, coral, echo, sage, shimmer, verse |
| Precio | $5.00/1M tokens entrada audio · $20.00/1M tokens salida audio |
pip install "livekit-agents[openai]"
from livekit.agents import AgentSession
from livekit.plugins.openai import realtime
session = AgentSession()
model = realtime.RealtimeModel(
model="gpt-4o-realtime-preview",
voice="coral",
instructions="Eres un asistente de atención al cliente para una empresa española.",
)
await session.start(agent=model, room=room)
- Ventajas: latencia muy baja, entiende tono y emoción, soporta function calling nativo.
- Limitación: coste elevado en llamadas largas, solo modelos OpenAI.
Google Gemini Live
| Parámetro | Valor |
|---|---|
| Plugin | livekit-plugins-google (clase GeminiMultimodalLiveModel) |
| Modelo | gemini-3.1-flash-live-preview |
| Latencia | ~250–400ms |
| Voces | Aoede, Charon, Fenrir, Kore, Leda, Orus, Puck, Zephyr |
| Precio | Gratis hasta cierto volumen (API Key) · pago vía Google Cloud |
pip install "livekit-agents[google]"
from livekit.plugins.google import beta as google_beta
model = google_beta.GeminiMultimodalLiveModel(
model="gemini-3.1-flash-live-preview",
voice="Kore",
instructions="Eres Sonia, asistente de una clínica dental en Madrid.",
)
- Ventajas: búsqueda en Google integrada, gratis para prototipos, multimodal.
- Limitación: sesiones limitadas a 30 minutos (hay que reconectar), disponibilidad regional variable.
Límite de sesión Gemini Live. Las sesiones están limitadas a 30 minutos. Si necesitas llamadas más largas, implementa reconexión automática al detectar la desconexión.
Convierten la voz del usuario en texto. Necesarios en arquitectura STT + LLM + TTS.
| Proveedor | Plugin | Idiomas | Latencia | Precio aprox. |
|---|---|---|---|---|
| Deepgram | livekit-plugins-deepgram |
36+ (español incluido) | ~150–300ms | desde $0.0043/min |
| OpenAI Whisper | livekit-plugins-openai |
57+ | media-alta | $0.006/min |
| AssemblyAI | livekit-plugins-assemblyai |
12+ (español) | media | desde $0.12/hora |
| Gladia | livekit-plugins-gladia |
100+ | media | consultar |
| ElevenLabs Scribe | livekit-plugins-elevenlabs |
— | media | consultar |
Uso de Deepgram (recomendado para español):
pip install "livekit-agents[deepgram]"
from livekit.plugins import deepgram stt = deepgram.STT(model="nova-2", language="es")
Convierten la respuesta en texto del LLM en voz. Determinan cómo suena tu agente.
| Proveedor | Plugin | Latencia | Precio aprox. | Destacado |
|---|---|---|---|---|
| Cartesia | livekit-plugins-cartesia |
~100–200ms | $0.040/1000 chars | Control de emoción y velocidad. Opción más popular en la comunidad LiveKit. |
| ElevenLabs | livekit-plugins-elevenlabs |
~200–400ms | $0.18/1000 chars | 1000+ voces, clonación profesional, máxima calidad. |
| OpenAI TTS | livekit-plugins-openai |
~200–400ms | $15.00/1M chars | Buena calidad general, integración simple si ya usas OpenAI. |
Uso de Cartesia:
pip install "livekit-agents[cartesia]"
from livekit.plugins import cartesia
tts = cartesia.TTS(
model="sonic",
voice="nombre-de-la-voz",
language="es",
speed="normal",
emotion=["positivity:high"],
)
El LLM procesa el texto transcrito y genera la respuesta. También gestiona el contexto, herramientas (function calling) y la lógica del agente.
| Proveedor | Plugin | Instalación |
|---|---|---|
| OpenAI | livekit-plugins-openai |
pip install "livekit-agents[openai]" |
| Azure OpenAI | livekit-plugins-azure |
pip install "livekit-agents[azure]" |
| Google Gemini | livekit-plugins-google |
pip install "livekit-agents[google]" |
| Anthropic Claude | livekit-plugins-anthropic |
pip install "livekit-agents[anthropic]" |
| DeepSeek | livekit-plugins-deepseek |
pip install livekit-plugins-deepseek |
| Groq | livekit-plugins-groq |
pip install livekit-plugins-groq |
| Cerebras | livekit-plugins-cerebras |
pip install livekit-plugins-cerebras |
| Together AI | livekit-plugins-together |
pip install livekit-plugins-together |
| Fireworks AI | livekit-plugins-fireworks |
pip install livekit-plugins-fireworks |
| Perplexity | livekit-plugins-perplexity |
pip install livekit-plugins-perplexity |
| Ollama (local) | livekit-plugins-ollama |
pip install livekit-plugins-ollama |
| xAI (Grok) | livekit-plugins-xai |
pip install livekit-plugins-xai |
Ollama (local). Requiere Ollama corriendo en tu servidor. Sin coste de API, pero necesitas GPU.
LiveKit Egress permite grabar llamadas automáticamente. Cuatro modalidades:
| Modalidad | Descripción | Ideal para |
|---|---|---|
| Room Composite Egress | Graba toda la sala mezclada en un solo archivo (audio + video si hay). | Grabación simple de llamadas completas. |
| Track Composite Egress | Graba tracks específicos combinados. | Grabar solo el audio, sin video. |
| Track Egress | Graba un track individual (voz del usuario o del agente por separado). | Análisis separado, quality assurance, entrenamiento de modelos. |
| Auto Egress | Grabación automática de todas las salas que cumplan un patrón. | Grabar TODAS las llamadas sin código adicional. |
Ejemplo: Room Composite Egress
from livekit.api import LiveKitAPI, RoomCompositeEgressRequest, EncodedFileOutput
async def start_recording(room_name):
api = LiveKitAPI()
egress = await api.egress.start_room_composite_egress(
RoomCompositeEgressRequest(
room_name=room_name,
file=EncodedFileOutput(
filepath="recordings/{room_name}-{time}.mp4",
),
)
)
Auto Egress (todas las llamadas sin código adicional):
lk egress auto-create --room-prefix "call-" --output "s3://tu-bucket/recordings/"
Formatos soportados: MP4, OGG, WebM, WAV, MP3. Destinos: S3, GCS, Azure Blob, cualquier almacenamiento compatible con S3.
Aviso legal — RGPD + LOPD-GDD. En España, informar al interlocutor de que la llamada se graba es obligatorio. Configura tu agente para que lo anuncie al inicio de la conversación.
El agente transfiere la llamada a otro número y se desconecta. El usuario queda hablando con el destino.
from livekit.agents import sip
async def transfer_cold(participant, destination):
await sip.transfer_participant(
participant=participant,
transfer_to=f"sip:{destination}@siptrunk.netelip.com",
)
Ejemplo. El agente detecta que el cliente quiere hablar con un comercial y transfiere a la extensión 200 de la centralita netelip.
El agente pone al usuario en espera, llama al destino, presenta el caso, y luego conecta a ambos.
LiveKit soporta DTMF (Dual-Tone Multi-Frequency), los tonos que se generan al pulsar las teclas del teléfono (0–9, *, #).
Caso de uso típico. El usuario llama, un menú IVR le pide que pulse 1 para ventas, 2 para soporte. Según la tecla, se enruta a un agente IA diferente o a una extensión humana de netelip.
Recibir DTMF (enrutar según tecla pulsada):
from livekit import rtc
@room.on("sip_dtmf_received")
def on_dtmf(event):
digit = event.digit
if digit == "1":
# Enrutar a agente de ventas
pass
elif digit == "2":
# Enrutar a agente de soporte
pass
elif digit == "0":
# Transferir a operador humano vía netelip
pass
Enviar DTMF desde el agente (para navegar IVRs de terceros en llamadas salientes):
await sip.send_dtmf(participant=participant, digits="1234#")
| Número | Geográfico de Madrid (+34 91X XXX XXX) |
| Arquitectura | Deepgram STT + GPT-4o + Cartesia TTS |
| Función | Atiende llamadas, agenda citas vía Cal.com, responde FAQ |
| Transferencia | A recepción humana si el paciente insiste (extensión Centralita Virtual netelip) |
| Grabación | Activada, con aviso RGPD al inicio |
| Número | 900 (gratuito para el llamante) |
| Arquitectura | OpenAI Realtime API (Speech-to-Speech) |
| Función | Resuelve dudas técnicas, consulta documentación vía RAG, crea tickets en Zendesk |
| Transferencia | Caliente: si el caso es crítico, conecta con ingeniero de guardia |
| DTMF | Menú inicial: 1=incidencia, 2=consulta, 3=facturación |
| Número | Geográfico local del destino |
| Arquitectura | Deepgram + Claude + ElevenLabs (voz clonada del comercial) |
| Función | Llama a leads de HubSpot, cualifica con preguntas BANT, agenda reunión en Cal.com |
| Integración | Webhook N8N que dispara la llamada cuando llega un lead nuevo |
| Número | Internacional (+44, +33, +49 vía netelip) |
| Arquitectura | Gladia STT (detección automática de idioma) + GPT-4o + ElevenLabs (auto_mode) |
| Función | Detecta el idioma del llamante y responde en ese idioma automáticamente |
| Transferencia | A equipos locales según idioma detectado |
| Número | Geográfico o móvil virtual |
| Arquitectura | Deepgram + GPT-4o-mini (bajo coste) + OpenAI TTS |
| Función | Llama al cliente 24h después del servicio, hace 3–5 preguntas, guarda respuestas en Supabase |
| DTMF | "pulse 1 si está satisfecho, 2 si no" |
| Número | Geográfico de la empresa |
| Arquitectura | Deepgram + GPT-4o + Cartesia (tono profesional pero firme) |
| Función | Llama a clientes con facturas pendientes, negocia plan de pago, registra compromiso |
| Compliance | Graba llamada, anuncia grabación, respeta horarios legales |
| Número | Geográfico local |
| Arquitectura | Gemini Live (Speech-to-Speech, bajo coste) |
| Función | Gestiona reservas, informa del menú, horarios, eventos especiales |
| Integración | Google Calendar para disponibilidad en tiempo real |
Agente pipeline clásico STT + LLM + TTS. Archivo: agent.py
from livekit.agents import (
AgentSession, AutoSubscribe, WorkerOptions, cli, function_tool,
)
from livekit.plugins import deepgram, openai, cartesia
# Configurar componentes
stt = deepgram.STT(model="nova-2", language="es")
llm = openai.LLM(
model="gpt-4o",
temperature=0.7,
)
tts = cartesia.TTS(
model="sonic",
voice="Spanish Female - Warm",
language="es",
)
# Herramientas del agente
@function_tool
async def check_availability(date: str, time: str) -> str:
"""Consulta disponibilidad para una cita."""
return f"Hay disponibilidad el {date} a las {time}."
@function_tool
async def book_appointment(
name: str, phone: str, date: str, time: str, reason: str
) -> str:
"""Reserva una cita para el cliente."""
return f"Cita reservada para {name} el {date} a las {time}."
# Punto de entrada
async def entrypoint(ctx):
session = AgentSession(stt=stt, llm=llm, tts=tts)
session.llm.system_prompt = """
Eres Sonia, recepcionista de Clínica Dental Sonrisa en Madrid.
Tu trabajo es atender llamadas, responder preguntas frecuentes
y agendar citas. Eres amable pero eficiente. Hablas en español.
Si el paciente necesita hablar con un doctor, transfiere la llamada.
Siempre confirma nombre y teléfono antes de agendar.
"""
await session.start(room=ctx.room, agent=ctx.agent)
await session.say(
"Clínica Dental Sonrisa, le atiende Sonia. ¿En qué puedo ayudarle?"
)
if __name__ == "__main__":
cli.run_app(
WorkerOptions(
entrypoint_fnc=entrypoint,
auto_subscribe=AutoSubscribe.AUDIO_ONLY,
)
)
Variables de entorno y ejecución:
export LIVEKIT_URL=wss://tu-proyecto.livekit.cloud export LIVEKIT_API_KEY=APIxxxxxxxx export LIVEKIT_API_SECRET=xxxxxxxxxxxxxxxx export DEEPGRAM_API_KEY=xxxxxxxx export OPENAI_API_KEY=sk-xxxxxxxx export CARTESIA_API_KEY=xxxxxxxx
python agent.py start
Agente Speech-to-Speech. Archivo: agent_realtime.py
from livekit.agents import AgentSession, AutoSubscribe, WorkerOptions, cli
from livekit.plugins.openai import realtime
model = realtime.RealtimeModel(
model="gpt-4o-realtime-preview",
voice="coral",
temperature=0.7,
instructions="""
Eres Carlos, asistente de soporte técnico de una empresa de software.
Hablas en español. Resuelves dudas técnicas de forma clara y concisa.
Si no puedes resolver el problema, ofreces crear un ticket de soporte
y transferir a un técnico humano.
""",
)
async def entrypoint(ctx):
session = AgentSession()
await session.start(room=ctx.room, agent=model)
if __name__ == "__main__":
cli.run_app(
WorkerOptions(
entrypoint_fnc=entrypoint,
auto_subscribe=AutoSubscribe.AUDIO_ONLY,
)
)
Variables de entorno y ejecución:
export LIVEKIT_URL=wss://tu-proyecto.livekit.cloud export LIVEKIT_API_KEY=APIxxxxxxxx export LIVEKIT_API_SECRET=xxxxxxxxxxxxxxxx export OPENAI_API_KEY=sk-xxxxxxxx
python agent_realtime.py start
Tu agente LiveKit necesita estar corriendo 24/7 para atender llamadas.
Serverless, escala automáticamente. Configura min_containers=1 para que siempre haya un worker listo. Coste orientativo: ~$30–50/mes con tráfico bajo.
import modal
app = modal.App("voice-agent")
image = modal.Image.debian_slim().pip_install(
"livekit-agents[deepgram,openai,cartesia]"
)
@app.function(
image=image,
secrets=[modal.Secret.from_name("livekit-secrets")],
min_containers=1,
timeout=3600,
)
def run_agent():
import subprocess
subprocess.run(["python", "agent.py", "start"])
Contenedores siempre activos. Similar a un VPS pero gestionado. Coste: ~$5–20/mes según recursos.
Cualquier VPS con Python 3.9+. Usa systemd o supervisor para mantener el proceso. Coste: desde $5/mes (Hetzner, OVH, etc.)
Importante para producción. Configura health checks y reconexión automática. Ten al menos 2 workers para redundancia. Monitoriza latencia y errores. Configura region pinning en LiveKit para minimizar latencia (Europa para números españoles).
| Problema | Causa probable | Solución |
|---|---|---|
| La llamada entra pero no conecta con el agente | Dispatch Rule mal configurada o agente no está corriendo | Verifica que el trunk_id coincide y que el agente está activo |
| Audio entrecortado | Codec incompatible o latencia de red alta | Usa G.711 (PCMU/PCMA), verifica region pinning en LiveKit |
| SIP 403 Forbidden | Credenciales incorrectas o IP no autorizada | Revisa usuario/contraseña del SIP Trunk en netelip |
| SIP 404 Not Found | Número DID no configurado | Verifica que el DID está asignado al SIP Trunk en netelip |
| El agente no habla | TTS mal configurado o API key inválida | Revisa API keys del proveedor de TTS y los logs del agente |
| El agente no entiende | STT mal configurado o idioma incorrecto | Verifica idioma del STT y API key de Deepgram/OpenAI |
| Llamada se corta a los 30 segundos | Timeout del agente o keep-alive SIP | Configura keep-alive, revisa timeout en LiveKit y netelip |
| Llamada se corta a los 30 minutos (Gemini) | Límite de sesión de Gemini Live | Implementa reconexión automática al detectar desconexión |
| No se graba la llamada | Egress no configurado | Configura Auto Egress o inicia Room Composite Egress manualmente |
| Eco en la llamada | Cancelación de eco no activa | LiveKit incluye AEC; verifica que no hay doble reproducción |
| Transferencia falla | SIP REFER no soportado por el trunk | Verifica con netelip que el trunk soporta REFER; usa warm transfer |
| Alta latencia (>2s) | Servidor del agente lejos del servidor de LiveKit | Usa region pinning en LiveKit (Europa) y servidor en EU |
| Llamadas salientes fallan | Outbound Trunk mal configurado o número no autorizado | Verifica address, credenciales y que netelip permite salientes |
| Recurso | URL |
|---|---|
| Documentación general | docs.livekit.io |
| Telefonía SIP | docs.livekit.io/telephony |
| Agents SDK | docs.livekit.io/agents |
| Integraciones | docs.livekit.io/agents/integrations/overview |
| Transferencias | docs.livekit.io/telephony/features/transfers |
| Egress (grabación) | docs.livekit.io/home/egress/overview |
| LiveKit Cloud | cloud.livekit.io |
| GitHub | github.com/livekit |
| Recurso | URL |
|---|---|
| Panel de cliente | panel.netelip.com |
| Centro de Ayuda | ayuda.netelip.com |
| SIP Trunk | netelip.com/sip-trunk |
| Números virtuales | netelip.com/numero-virtual |
| Proveedor | URL |
|---|---|
| OpenAI | platform.openai.com |
| Google AI Studio | aistudio.google.com |
| Deepgram | deepgram.com |
| Cartesia | cartesia.ai |
| ElevenLabs | elevenlabs.io |
| AssemblyAI | assemblyai.com |
| Anthropic | console.anthropic.com |
Para preguntas sobre el SIP Trunk o los números virtuales, abre un ticket desde tu panel privado de cliente.
Para la configuración de LiveKit, consulta la documentación oficial de LiveKit.
Volver