🎙️ Servidor de webhooks Vapi (Voice Server)
Archivo: /Users/user/rifai-agents/agentes/voice/server.ts · Plist: com.rifai.voice-server · Horario: daemon permanente (KeepAlive=true, RunAtLoad=true). Escucha en el puerto 7411 (VOICE_SERVER_PORT), siempre activo. Vapi accede a él desde fuera vía el túnel cloudflared (com.rifai.voice-tunnel).
Qué hace
Es el cerebro receptor del departamento de voz. Expone un servidor Express que recibe los eventos de Vapi durante y al final de cada llamada, y un conjunto de endpoints de *function calling* que el assistant de voz consulta en vivo (estado de un pedido, info de un producto, stock, envíos, top ventas). Cuando una llamada termina, decide el resultado (CONFIRMADO/RECHAZADO/PENDIENTE/PROBLEMA), lo persiste, actualiza el tag del pedido en Shopify, encola reintentos si quedó pendiente y avisa por Telegram. Sin este servidor, las llamadas se lanzan pero nadie recoge el resultado.
Cómo funciona
1. Levanta Express en 0.0.0.0:7411 con GET /health.
2. POST /vapi/webhook (eventos generales): en end-of-call-report/call.ended extrae análisis, transcript, sentimiento, endedReason, duración y coste. Determina el result desde structuredData o, en su defecto, por heurística (no-answer/busy → PENDIENTE; <15s → PENDIENTE; regex sobre transcript → CONFIRMADO/RECHAZADO). Persiste con updateCallEnd. Lee shopify_order_id de la metadata de la llamada y: actualiza el tag del pedido (updateShopifyTag, limpia tags de estado previos y pone el nuevo), si quedó PENDIENTE encola reintento a 2h (queueForRetry), y notifica Telegram (cs_complaint si PROBLEMA/RECHAZADO, si no ops_tracking).
3. Endpoints de herramientas que el assistant llama en directo, cada uno devuelve texto natural en español listo para leer:
POST /vapi/tools/lookup-order— busca pedido por número (#1234) o id y devuelve items, total, ciudad y estado.POST /vapi/tools/lookup-product— busca producto por título/keyword, devuelve precio, stock y descripción.POST /vapi/tools/check-stock— stock total de un producto.POST /vapi/tools/shipping-info— tiempos/costes de envío por zona (península, Canarias, Baleares, Ceuta/Melilla) + garantía.POST /vapi/tools/top-products— top 3 más vendidos de los últimos 30 días.
4. extractToolArgs/toolResponse normalizan el formato de tool-call de Vapi.
Datos/APIs
- Vapi (entrante): recibe webhooks y tool-calls. La URL pública la registra
tunnel-keeper.shen el assistant (serverUrl). - Shopify Admin REST (
2024-10): consulta pedidos/productos y PUT de tags. Vars:SHOPIFY_STORE,SHOPIFY_ACCESS_TOKEN. - SQLite
data/voice-calls.db(lib/voice-db.ts):updateCallEnd,queueForRetry. - Telegram vía
tools/notify-router.ts(cs_complaint→ Cristina;ops_tracking→ Oscar). - Vars:
VOICE_SERVER_PORT(default 7411),SHOPIFY_STORE,SHOPIFY_ACCESS_TOKEN.
Cómo probarlo
cd /Users/user/rifai-agents && npx tsx agentes/voice/server.ts
Esperado: imprime 🎙️ Voice Webhook Server on http://0.0.0.0:7411. Verificar salud en otra terminal:
curl -s http://localhost:7411/health
Debe devolver {"ok":true,"ts":...}. Para probar un tool, hacer un curl POST a /vapi/tools/lookup-order con {"message":{"toolCallList":[{"id":"t1","function":{"name":"lookup_order","arguments":"{\"order_number\":\"1234\"}"}}]}} y ver la respuesta en español. (Levantar manualmente colisiona con el daemon en 7411; parar el daemon antes o usar otro puerto con VOICE_SERVER_PORT.)
Si se rompe / recuperar
launchctl unload ~/Library/LaunchAgents/com.rifai.voice-server.plist
launchctl load ~/Library/LaunchAgents/com.rifai.voice-server.plist
Logs: /Users/user/rifai-agents/logs/voice-server.out.log y .err.log. Si Vapi no recibe respuesta: comprobar que el daemon escucha en 7411 (curl /health), que el túnel com.rifai.voice-tunnel esté arriba y que la serverUrl actual del assistant apunte al .trycloudflare.com de data/tunnel-url.txt. La URL del túnel cambia en cada reinicio: tunnel-keeper.sh la re-registra automáticamente vía PATCH al assistant Vapi.
Cómo replicarlo
- Servidor Express con
/health, webhook genérico y endpoints de tools. - Lógica de clasificación de resultado (structuredData + heurística por endedReason/duración/transcript).
- Cliente Shopify Admin (lectura pedidos/productos, PUT tags).
- SQLite (
updateCallEnd,queueForRetry). - Router de notificaciones Telegram.
- Túnel público (cloudflared) que registre la
serverUrlen el assistant Vapi — vertunnel-keeper.sh+ plistcom.rifai.voice-tunnel. - Plist daemon con
KeepAlive=trueyRunAtLoad=true.