API de voz
Una sola API para todo lo que suene: síntesis, clonado de voz, conversaciones en el navegador y llamadas telefónicas. Tus sistemas hablan con esta API; las credenciales de Fish Audio y Twilio no salen de aquí.
Autenticación
Todas las rutas menos /health requieren un token en la cabecera. Cada sistema tiene el suyo, así se puede revocar uno sin afectar a los demás.
curl https://voz.bibliolabtech.com/api/v1/tts \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"text":"Hola, esto es una prueba."}' \
--output prueba.mp3Sin token responde 401. Con token inválido, también: no distingue entre ambos casos a propósito.
Síntesis
Convierte texto en audio. Devuelve un mp3 directamente, no un enlace: se puede reproducir o guardar tal cual.
POST/api/v1/tts
Sintetiza texto y devuelve audio mp3.
Cuerpo{
"text": "[confident] Buenas, habla Pablo. [break] ¿Tienes cinco minutos?",
"voiceId": "bed5a1875f2f4f39acad96f35af0e13f", // opcional
"speed": 1.15 // opcional, 0.5 a 2
}Respuestaaudio/mpeg · 192 kbps
- El texto admite etiquetas de dirección entre corchetes: [confident], [empathetic], [excited], [break], [long-break].
- También acepta descripciones libres, como [energetic Colombian salesman]. El modelo entiende miles de ellas, no solo la lista documentada.
- Escribe siempre con tildes: el modelo las usa para colocar el acento tónico. «genero» y «generó» no suenan igual.
- Escribe las cifras como se pronuncian («un millón doscientos mil») para evitar lecturas raras.
- Máximo 4000 caracteres por petición.
Voces
Catálogo y clonado. Un clon capta el timbre a partir de pocos segundos; la calidad depende casi por completo del material que le des.
GET/api/v1/voices?q=
Lista voces del catálogo. Sin `q` devuelve las de español.
Respuesta{ "voices": [{ "id": "...", "title": "...", "languages": ["es"] }] }POST/api/v1/voices
Clona una voz a partir de clips de audio (multipart/form-data).
Cuerpotitle=Pablo Financiero
clips=@nota1.ogg
clips=@nota2.ogg # hasta 20 archivos
enhance=0 # 1 activa el realce de Fish
Respuesta{ "voiceId": "bed5a187…", "clips": 6 }- Mínimo 10 segundos por clip; entre uno y dos minutos en total da mejor fidelidad.
- El realce viene apagado a propósito: su reducción de ruido quita brillo a las consonantes, y medimos caídas de 10,4 % a 2,6 % de energía en la banda que hace inteligible el habla.
- Elige los clips por brillo, no por duración. Una nota de WhatsApp larga suele ser peor que una corta bien grabada.
- El clonado copia el timbre, no el acento. La prosodia la pone el modelo: para acento regional hay que dirigirlo con etiquetas en el texto.
Conversación en el navegador
Sesiones de voz en tiempo real. El servidor acuña un token efímero; la clave de Fish nunca llega al cliente.
POST/api/v1/sessions
Crea una sesión y devuelve el token de la sala.
Cuerpo{
"persona": "marina", // o "closer": true
"variables": { // reemplazan los {{placeholders}} del prompt
"nombre": "Andrés",
"producto_precio": "un millón doscientos mil pesos"
}
}Respuesta{ "token": { "livekit_url": "wss://…", "token": "…", "expires_at": "…" } }- El navegador entrega ese token a `AgentSession.start({ sessionToken })` del SDK @fishaudio/agent-react.
- El token caduca en minutos y la llamada tiene un tope de 10 minutos.
- Hasta 50 variables por sesión. Una variable vacía deja el marcador «{{así}}» visible al modelo, que puede acabar leyéndolo en voz alta.
Llamadas telefónicas
Marca un número real y conecta la llamada con el agente a través del puente de audio.
POST/api/v1/calls
Lanza una llamada saliente.
Cuerpo{
"to": "+573001234567",
"variables": { "nombre": "Andrés", "producto_precio": "novecientos mil pesos" }
}Respuesta{ "callSid": "CA…", "to": "+57…" }- El destino va en formato E.164, empezando por «+».
- Cada país debe estar habilitado en los permisos geográficos de Twilio; si no, devuelve «not authorized to call».
- Las variables viajan como parámetros del stream y llegan al agente antes de que la persona conteste.
- Hasta 60 llamadas por hora y por token. Al pasarse responde 429 con la cabecera Retry-After.
GET/api/v1/calls?limit=30
Historial de llamadas con estado, duración y costo.
GET/api/v1/calls/:sid
Detalle de una llamada. Con un id «sess_…» devuelve la transcripción completa.
Servicio
Para monitorización externa.
GET/api/v1/health
Estado del servicio. No requiere token.
Respuesta{ "ok": true, "fish": "ok", "agente": "…", "puente": "voz.bibliolabtech.com" }Cómo está montado
tus sistemas ──> API de voz ──> Fish Audio (voz, agente)
└──> Twilio (telefonía)
llamada telefónica:
Twilio ──(mu-law 8 kHz por WebSocket)──> puente ──> sala del agente
<────────────────────────────── <──El audio de las conversaciones nunca pasa por esta API: el navegador se conecta directo a la sala del agente, y en las llamadas es el puente quien habla con Twilio. Esta API es el plano de control — decide quién puede hacer qué, con qué voz y con qué guion— y por eso no añade latencia a la conversación.
Límites y costos
Síntesis4000 caracteres por petición
Clonado20 clips por voz, 10 s mínimo cada uno
Sesión de voz10 minutos por conversación
Llamadas por token60 por hora
Demo pública3 llamadas por hora e IP, solo a números verificados
Llamada a móvil colombiano≈ $0.034 por minuto (Twilio)
Síntesis s2.1-pro$15 por millón de bytes (≈12 h de audio)