Automatizaciones

Reglas if/then

Motor de reglas que dispara acciones cuando suceden eventos: triggers, condiciones, acciones, recetas listas para copiar, debug y FAQ.

Cómo funcionan

Las automatizaciones son reglas cuando X sucede, si se cumple Y, entonces hacé Z. Sentik ejecuta esto por vos sin tocar código:

  • Trigger — el evento que dispara la regla (conversación cerrada, tag agregado, etc).
  • Condiciones — filtros opcionales que deciden si la regla aplica a este caso concreto. Se evalúan en AND (todas deben cumplirse).
  • Acciones — qué hacer cuando match. Se ejecutan en orden. Si una falla, las siguientes corren igual.
Si no agregás condiciones, la regla dispara siempre que ocurra el trigger. Útil para reglas globales como “cada conversación cerrada se taguea como ‘revisada’”.

Los 6 triggers

Cada trigger se dispara desde un punto específico del sistema. Si un código path no está wireado, el trigger nunca llega — abajo te aclaro de dónde sale cada uno.

TriggerCuándo disparaDisparado desde
conversation.createdLlega el primer mensaje de un visitante y se crea la conversación.Widget endpoint /api/widget/[key]/session
conversation.closedUn agente o el sistema cierra la conversación.Botón "Cerrar" del header (closeConversation server action)
conversation.at_risk_flippedEl sentiment cae al rojo o el cliente se enoja sostenidamente.Sentiment tracker (lib/ai/sentiment-tracker.ts)
tag.addedSe agrega un tag a la conversación (manual o por el bot).addConversationTag server action
handoff.firedEl bot escala con request_human_handoff o transfer_to_department.Tool de handoff del bot
time.elapsedUna conversación lleva N minutos sin actividad. Requiere trigger_params.minutes_idle.Cron interno /api/cron/automation-time-triggers (corre cada 5 min)

Configurar time.elapsed

A diferencia de los otros triggers que reaccionan a eventos instantáneos, time.elapsed se chequea por un cron cada 5 minutos. La regla declara cuánto tiempo de inactividad espera y, opcionalmente, qué status tiene que tener la conversación:

trigger_params para time.elapsedjson
{
  "minutes_idle": 1440,        // 24h sin actualización
  "only_status": "bot"          // opcional: bot | open | assigned
}

El engine garantiza idempotencia: una regla no vuelve a disparar para la misma conversación durante el mismo window. Si fijás minutes_idle=60 y la conversación queda idle 5 horas, la regla dispara una sola vez, no cinco.

Para que el cron funcione en producción tenés que setear la env var CRON_SECRET en Vercel. Vercel pasa el secret en Authorization: Bearer ... al endpoint del cron.

Contexto disponible

Cuando un trigger dispara, el engine arma un snapshot del estado actual de la conversación + lead y lo usa para evaluar las condiciones y dárselo a las acciones. El snapshot tiene estos campos:

CampoTipoDe dónde sale
department_idUUIDconversations.department_id
channelstringwhatsapp / web / other
sentiment_rollingnumber (-1 a 1)Rolling avg del sentiment de los últimos 5 mensajes del cliente
at_riskbooleantrue si la conversación está marcada en riesgo
is_testbooleanConversaciones del laboratorio
statusstringopen | assigned | closed | bot
message_countnumberTotal de mensajes intercambiados
lead_scorenumber (0-100)leads.qualification.score
lead_stagestringnew | qualified | nurturing | converting | won | lost
tagsstring[]Array de tags actuales de la conversación

Condiciones

Cada condición tiene la forma field + op + value. El builder visual solo muestra los operadores válidos para el tipo del campo.

Campos disponibles

CampoTipoEjemplo de uso
department_idselect= un dept específico
channelselect= "web" → solo conversaciones del widget
sentiment_rollingnumber< -0.3 → sentiment negativo
lead_scorenumber>= 70 → leads hot
lead_stageselect= "qualified"
has_tagstringcontains "reembolso" → tiene ese tag
is_testboolean= false → ignorar conversaciones del lab
message_countnumber> 10 → solo conversaciones largas

Operadores

OpSignificadoTipos aplicables
equalses igual astring · number · boolean · select
not_equalsno es igual astring · number · boolean · select
gtmayor quenumber
gtemayor o igual anumber
ltmenor quenumber
ltemenor o igual anumber
containscontiene (sub-string o item del array)string · array (has_tag)

Acciones

Cada acción tiene un type y un objeto params con los datos que necesita. Las acciones son idempotentes donde se puede (por ejemplo, agregar un tag que ya existe no falla, solo no hace nada).

AcciónParamsQué hace
add_tag{ tag, color? }Agrega un tag a la conversación. Skip si ya existía.
remove_tag{ tag }Quita un tag por nombre.
assign_to_user{ user_id }Asigna la conversación a un agente y la pasa a status=assigned.
set_department{ department_id }Routea la conversación a un depto específico.
set_status{ status }Cambia el status: open / assigned / closed / bot.
set_qualification{ score?, stage?, budget?, timeline?, authority? }Patch parcial sobre leads.qualification. Solo los campos enviados se actualizan.
add_internal_note{ content }Inserta una nota interna en la conversación (con source=system para distinguirla).
send_automatic_message{ event_type }Manda al cliente el mensaje configurado para ese evento (welcome_new_lead, post_handoff, etc).
send_webhook{ url, method?, headers?, body? }POST a una URL externa con el contexto del trigger. Timeout 5s. Cualquier 2xx cuenta como éxito.

Webhooks salientes (send_webhook)

Sirve para integrar Sentik con cualquier servicio externo (Slack via Incoming Webhooks, Discord, Notion API, tu propio CRM, Zapier, n8n, Make). Por default el cuerpo es JSON con el contexto completo del trigger:

Body por defaultjson
{
  "trigger": "conversation.closed",
  "rule_id": "...",
  "rule_name": "Notificar cierres de Pagos",
  "tenant_id": "...",
  "conversation_id": "...",
  "lead_id": "...",
  "snapshot": {
    "department_id": "...",
    "channel": "whatsapp",
    "sentiment_rolling": -0.45,
    "lead_score": 65,
    "lead_stage": "nurturing",
    "tags": ["reembolso"],
    ...
  },
  "fired_at": "2025-..."
}

Podés mandar tu propio body usando placeholders {{...}}. Los paths soportan el snapshot completo:

Body custom con placeholderstext
{
  "text": "🚨 Lead en riesgo: score {{snapshot.lead_score}}, sentiment {{snapshot.sentiment_rolling}}",
  "conversation_url": "https://sentik.io/agent?conv={{conversationId}}"
}
Para Slack: creá un Incoming Webhook en tu workspace, pegalo como url, y mandá el body con la forma { "text": "..." }. Tu canal de Slack recibe la notificación al instante.

Recetas listas para copiar

Las podés armar desde el editor visual en /admin/automations o decírselas al copilot en lenguaje natural.

1. Marcar leads perdidos cuando cierran molestos

  • Trigger: conversation.closed
  • Si: sentiment_rolling lt -0.3
  • Entonces: add_tag({tag: "cliente-perdido"}) + set_qualification({stage: "lost"})

2. Auto-asignar VIPs al admin principal

  • Trigger: conversation.created
  • Si: lead_score gte 70
  • Entonces: assign_to_user({user_id: "<admin_uuid>"})

3. Alertar al equipo cuando hay queja de reembolso

  • Trigger: tag.added
  • Si: has_tag contains reembolso
  • Entonces: set_department({department_id: "<pagos_uuid>"}) + add_internal_note({content: "Caso de reembolso — prioridad alta"})

4. Routear conversaciones del lab a sí mismas (skip métricas)

  • Trigger: conversation.created
  • Si: is_test equals true
  • Entonces: add_tag({tag: "lab"})

5. Follow-up automático al cerrar bien

  • Trigger: conversation.closed
  • Si: sentiment_rolling gte 0.5
  • Entonces: send_automatic_message({event_type: "closing"}) + set_qualification({stage: "won"})

6. Auto-cerrar leads idle de 7 días

  • Trigger: time.elapsed con { minutes_idle: 10080, only_status: "bot" }
  • Si: (sin condiciones)
  • Entonces: set_status({status: "closed"}) + add_tag({tag: "auto-cerrada"})

7. Avisar a Slack cuando hay queja

  • Trigger: conversation.at_risk_flipped
  • Si: channel equals whatsapp
  • Entonces: send_webhook al Incoming Webhook de Slack con body { "text": "⚠️ {{snapshot.lead_stage}} con sentiment {{snapshot.sentiment_rolling}}" }

Crear reglas desde el copilot

El copilot puede armar las reglas por vos. Solo describí qué querés:

Ejemplo de prompt al copilottext
Crea una regla:
"Cuando una conversación se cierra y el sentiment es menor a -0.3,
taguéala como 'cliente-perdido' y agregale nota interna 'revisar feedback'".

El copilot va a llamar la tool create_automation_rule con el JSON estructurado y te devuelve confirmación con el id de la regla creada.

También tiene list_automation_rules para auditar las reglas existentes y toggle_automation_rule para activar/desactivar. Útil si querés “pausá todas las reglas que mandan mensajes automáticos” durante mantenimiento.

Debug — por qué no disparó

Cada vez que un trigger se evalúa contra una regla, el engine inserta un row en automation_runs con el resultado. La columna derecha de /admin/automations muestra los últimos 30 con icono verde (matched) o gris (no match), el nombre de la regla, la cantidad de acciones corridas, y errores si hubo.

Si una regla NO está disparando cuando esperabas:

  • Confirmá que está activada. El toggle a la izquierda de la fila tiene que estar verde.
  • Confirmá que el trigger es el correcto. Por ejemplo, tag.added solo dispara cuando el tag se agrega desde la UI o el bot — no dispara para tags creados en el setup.
  • Revisá el log de Actividad reciente. Si ves la fila con icono gris (no match), una de las condiciones falló. El snapshot del contexto está guardado en la columna context de automation_runs.
  • Si la fila tiene error rojo, una de las acciones falló — el mensaje está visible al hacer hover en el log.
El engine es fire-and-forget — si por algún motivo el código que dispara el trigger crashea (por ejemplo, error en el sentiment tracker), el trigger nunca llega al engine y no vas a ver ni siquiera un log de no-match. En esos casos buscá los warnings en los logs del servidor (busca por [automation]).

Orden y prioridad

  • Las reglas se cargan ordenadas por priority ASC. Default 100. Menor número = se evalúa primero.
  • Todas las reglas que matchean corren — no hay early-exit. Si dos reglas dicen add_tag con tags distintos al mismo trigger, ambos tags se agregan.
  • Las acciones dentro de una regla corren en el orden declarado. Si la regla tiene set_department seguida de add_internal_note que menciona el dept, el nuevo dept ya está aplicado cuando la nota corre.
  • Si una acción falla, las siguientes corren igual. El error queda registrado en automation_runs.actions_run.

Performance y límites

  • Fire-and-forget: ningún trigger bloquea la acción que lo disparó. El cliente que cierra una conversación no espera a las reglas.
  • Cero loops infinitos por diseño: los triggers de cambio (tag.added en particular) podrían crear un ciclo si una regla agrega un tag que dispara otra regla que agrega otro tag, y así. El engine corta esto porque la acción add_tag es idempotente — re-agregar el mismo tag no re-dispara el trigger.
  • Sin rate limit por regla. Si tenés una conversación ruidosa que dispara 100 veces el mismo trigger, las reglas también corren 100 veces. Para evitar spam, agregá condiciones (ej: message_count gt 3 para ignorar el primer turno).
  • Log retention: los runs viven indefinidamente. Si crece mucho, podés purgar la tabla automation_runs mensualmente desde Supabase.

FAQ

¿Puedo programar acciones a futuro? (ej. follow-up en 3 días)

Sí. Usá el trigger time.elapsed con { minutes_idle: 4320 } (3 días). Un cron interno chequea cada 5 min las conversaciones idle y dispara la regla. La idempotencia está garantizada — solo va a disparar una vez por conversación dentro del window.

¿Las reglas se aplican a conversaciones existentes o solo nuevas?

Solo a eventos desde el momento en que activás la regla. Si querés taguear retroactivamente conversaciones viejas, hacelo con copilot: “Lista las conversaciones cerradas en los últimos 7 días con sentiment menor a -0.3 y agregales el tag ‘cliente-perdido’”.

¿Cuántas reglas puedo tener?

Sin límite duro. En la práctica, > 50 reglas activas por tenant empieza a ser difícil de mantener mentalmente. Si llegás ahí, conviene dividir en categorías por trigger o usar el campo priority para ordenarlas.

¿Y si quiero condiciones más complejas (OR, anidadas)?

Por ahora todas las condiciones se evalúan en AND. Para un OR podés crear dos reglas separadas con las mismas acciones y triggers pero distintas condiciones — equivalente lógico. Si necesitás algo más complejo, avisame y vemos.

¿Las reglas funcionan en el lab?

Sí, pero por default no querés que apliquen a conversaciones de prueba. Agregá una condición is_test equals false en cada regla que quieras filtrar.

¿Puedo notificar por Slack / Discord / mi CRM?

Sí — usá la acción send_webhook. Para Slack creá un Incoming Webhook en tu workspace, pegá la URL en la acción y mandá body { "text": "..." }. Para tu CRM apuntá a su endpoint de creación de leads con los placeholders que necesites. Email todavía no es nativo — si querés, usá Zapier o Make como puente.

¿Cómo exporto los logs?

En /admin/automations, en el panel de Actividad reciente, está el botón CSV. Te baja un archivo con las últimas 1000 ejecuciones: fecha, regla, trigger, match, acciones ok / total, error, conversation_id.