diff --git a/mvp/b2c/.env.example b/mvp/b2c/.env.example index e59faa9..82d1778 100644 --- a/mvp/b2c/.env.example +++ b/mvp/b2c/.env.example @@ -7,6 +7,9 @@ DATABASE_URL="postgresql://postgres:reformix@localhost:5432/reformix" RETELL_API_KEY="" RETELL_AGENT_ID="" RETELL_FROM_NUMBER="" # número de origen en E.164, p. ej. +34910000000 +# Allowlist de pruebas: si tiene valor (CSV de E.164), SOLO se llama a esos números; el resto se +# omite (la llamada no se lanza). Vaciar para volver a llamar a todos. Ej: "+34651194617" +RETELL_ALLOWED_NUMBERS="" # EP de ingesta del lead (/api/leads/:id/ingesta). Clave compartida que valida al llamante # externo (Authorization: Bearer ...). Sin ella, el EP responde 401. diff --git a/mvp/b2c/src/lib/env.ts b/mvp/b2c/src/lib/env.ts index 9fd3606..6b8a037 100644 --- a/mvp/b2c/src/lib/env.ts +++ b/mvp/b2c/src/lib/env.ts @@ -12,6 +12,8 @@ const schema = z.object({ RETELL_API_KEY: opcional, RETELL_AGENT_ID: opcional, RETELL_FROM_NUMBER: opcional, + // Allowlist de pruebas: si tiene valor (CSV de números), SOLO se llaman esos; vacío = todos. + RETELL_ALLOWED_NUMBERS: opcional, // EP de ingesta del lead: clave compartida que valida al llamante externo. FUNNEL_API_KEY: opcional, // SMTP para enviar el presupuesto y el enlace al formulario. @@ -32,6 +34,7 @@ export const env = schema.parse({ RETELL_API_KEY: process.env.RETELL_API_KEY, RETELL_AGENT_ID: process.env.RETELL_AGENT_ID, RETELL_FROM_NUMBER: process.env.RETELL_FROM_NUMBER, + RETELL_ALLOWED_NUMBERS: process.env.RETELL_ALLOWED_NUMBERS, FUNNEL_API_KEY: process.env.FUNNEL_API_KEY, SMTP_HOST: process.env.SMTP_HOST, SMTP_PORT: process.env.SMTP_PORT, diff --git a/mvp/b2c/src/lib/voice/retell.ts b/mvp/b2c/src/lib/voice/retell.ts index b622e5c..f02ee52 100644 --- a/mvp/b2c/src/lib/voice/retell.ts +++ b/mvp/b2c/src/lib/voice/retell.ts @@ -50,6 +50,18 @@ export async function iniciarLlamadaSaliente(opts: { const toNumber = normalizarTelefonoEs(opts.telefono); if (!toNumber) return null; + // Bypass de seguridad (fase de pruebas): si RETELL_ALLOWED_NUMBERS tiene valor, SOLO se llama a + // esos números; cualquier otro se omite (devuelve null, el funnel sigue en simulado). Vaciar la + // variable para volver a llamar a todos. + const allow = (env.RETELL_ALLOWED_NUMBERS ?? '') + .split(',') + .map((s) => normalizarTelefonoEs(s.trim())) + .filter((n): n is string => Boolean(n)); + if (allow.length > 0 && !allow.includes(toNumber)) { + console.warn(`Retell: ${toNumber} no está en RETELL_ALLOWED_NUMBERS; llamada omitida (bypass).`); + return null; + } + const body: Record = { from_number: env.RETELL_FROM_NUMBER, to_number: toNumber,