← Volver al catálogo

📞 Confirmación de pedidos COD por voz

Archivo: /Users/user/rifai-agents/agentes/voice/retell-confirm-cod.ts · Plist: com.rifai.voice-confirm-cod · Horario: cada 5 min (StartInterval 300s), pero solo llama dentro de la ventana 10:00–22:00 hora de Madrid y nunca en domingo.

Qué hace

Detecta pedidos contra reembolso (COD) nuevos en Shopify y lanza una llamada telefónica automática con Vapi para que un agente de IA confirme el pedido con el cliente antes de prepararlo y enviarlo. Es la primera pieza del flujo: descubre el pedido, normaliza el teléfono, dispara la llamada y deja el pedido marcado como LLAMANDO en Shopify. El resultado real (confirmado/rechazado/pendiente) lo procesa después el webhook (server.ts), no este agente. Evita llamadas duplicadas usando la base SQLite de llamadas. A pesar del nombre "retell", usa Vapi como proveedor de voz.

Cómo funciona

1. Carga .env con el snippet estándar (sin dotenv).

2. Comprueba isCallableNow(): si es domingo o fuera de 10–22h Madrid, sale sin hacer nada.

3. Pide a Shopify los pedidos status=open, fulfillment_status=unfulfilled creados en las últimas 6h (máx 20).

4. Filtra los que son COD (gateway contiene cod/cash o la nota contiene "contra") y descarta los que ya tengan tag confirmado/rechazado/llamando/problema.

5. Para cada candidato: salta si ya fue llamado en las últimas 24h (isOrderAlreadyCalled); extrae y normaliza el teléfono a E.164 español; convierte nombre, número, total e items a texto pronunciable en español con prepareVoiceVariables.

6. Lanza la llamada con createOutboundCall (Vapi POST /call/phone). Si responde con id, registra el inicio en SQLite (recordCallStart) y añade el tag LLAMANDO al pedido en Shopify.

7. Throttle de 1 llamada cada 2s. Al final, si lanzó alguna, notifica por Telegram (ops_pedido_pending).

Datos/APIs

  • Shopify Admin REST (2024-10): listar pedidos y hacer PUT del tag LLAMANDO. Vars: SHOPIFY_STORE, SHOPIFY_ACCESS_TOKEN.
  • Vapi (vía lib/vapi-client.ts): outbound call. Vars: VAPI_API_KEY, VAPI_ASSISTANT_ID_COD_CONFIRM, VAPI_PHONE_NUMBER_ID (o VAPI_FROM_NUMBER como fallback Twilio).
  • SQLite data/voice-calls.db (vía lib/voice-db.ts): dedupe y registro de llamadas.
  • Telegram vía tools/notify-router.ts (evento ops_pedido_pending → Oscar Ops).
  • Helpers internos: lib/phone-normalizer.ts, lib/spanish-converter.ts.

Cómo probarlo

cd /Users/user/rifai-agents && npx tsx agentes/voice/retell-confirm-cod.ts

Esperado: imprime la cabecera con timestamp, el nº de pedidos abiertos y de COD pendientes. Si está fuera de horario (domingo o no 10–22h Madrid) sale con . Si hay COD callables, imprime 📲 Llamando ... por cada uno y un resumen 📊 Lanzadas: N. OJO: no es dry-run puro — si hay pedidos COD válidos, lanza llamadas reales de pago. Para probar sin llamar, ejecútalo fuera de la ventana horaria o sobre una tienda sin pedidos COD recientes.

Si se rompe / recuperar

Recargar el plist:

launchctl unload ~/Library/LaunchAgents/com.rifai.voice-confirm-cod.plist
launchctl load   ~/Library/LaunchAgents/com.rifai.voice-confirm-cod.plist

Logs: /Users/user/rifai-agents/logs/voice-confirm-cod.out.log y .err.log. Si "no llama": revisar horario Madrid, que existan pedidos COD recientes, que VAPI_API_KEY/VAPI_PHONE_NUMBER_ID estén en .env, y que no estén ya marcados como llamados en data/voice-calls.db.

Cómo replicarlo

  • Snippet de carga de .env.
  • Cliente Vapi (createOutboundCallPOST https://api.vapi.ai/call/phone con assistantId, customer, assistantOverrides.variableValues/metadata).
  • Assistant Vapi de confirmación COD ya creado (VAPI_ASSISTANT_ID_COD_CONFIRM) + número saliente (VAPI_PHONE_NUMBER_ID).
  • Normalizador de teléfono ES→E.164 y ventana horaria (phone-normalizer.ts).
  • Conversor a español hablado (spanish-converter.ts).
  • SQLite para dedupe (voice-db.ts).
  • Acceso Shopify Admin con read_orders/write_orders (tags).
  • Router de notificaciones Telegram.
  • Plist launchd con StartInterval 300.