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.
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.
| Trigger | Cuándo dispara | Disparado desde |
|---|---|---|
conversation.created | Llega el primer mensaje de un visitante y se crea la conversación. | Widget endpoint /api/widget/[key]/session |
conversation.closed | Un agente o el sistema cierra la conversación. | Botón "Cerrar" del header (closeConversation server action) |
conversation.at_risk_flipped | El sentiment cae al rojo o el cliente se enoja sostenidamente. | Sentiment tracker (lib/ai/sentiment-tracker.ts) |
tag.added | Se agrega un tag a la conversación (manual o por el bot). | addConversationTag server action |
handoff.fired | El bot escala con request_human_handoff o transfer_to_department. | Tool de handoff del bot |
time.elapsed | Una 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:
{
"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.
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:
| Campo | Tipo | De dónde sale |
|---|---|---|
department_id | UUID | conversations.department_id |
channel | string | whatsapp / web / other |
sentiment_rolling | number (-1 a 1) | Rolling avg del sentiment de los últimos 5 mensajes del cliente |
at_risk | boolean | true si la conversación está marcada en riesgo |
is_test | boolean | Conversaciones del laboratorio |
status | string | open | assigned | closed | bot |
message_count | number | Total de mensajes intercambiados |
lead_score | number (0-100) | leads.qualification.score |
lead_stage | string | new | qualified | nurturing | converting | won | lost |
tags | string[] | 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
| Campo | Tipo | Ejemplo de uso |
|---|---|---|
department_id | select | = un dept específico |
channel | select | = "web" → solo conversaciones del widget |
sentiment_rolling | number | < -0.3 → sentiment negativo |
lead_score | number | >= 70 → leads hot |
lead_stage | select | = "qualified" |
has_tag | string | contains "reembolso" → tiene ese tag |
is_test | boolean | = false → ignorar conversaciones del lab |
message_count | number | > 10 → solo conversaciones largas |
Operadores
| Op | Significado | Tipos aplicables |
|---|---|---|
equals | es igual a | string · number · boolean · select |
not_equals | no es igual a | string · number · boolean · select |
gt | mayor que | number |
gte | mayor o igual a | number |
lt | menor que | number |
lte | menor o igual a | number |
contains | contiene (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ón | Params | Qué 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:
{
"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:
{
"text": "🚨 Lead en riesgo: score {{snapshot.lead_score}}, sentiment {{snapshot.sentiment_rolling}}",
"conversation_url": "https://sentik.io/agent?conv={{conversationId}}"
}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_rollinglt-0.3 - Entonces:
add_tag({tag: "cliente-perdido"})+set_qualification({stage: "lost"})
2. Auto-asignar VIPs al admin principal
- Trigger:
conversation.created - Si:
lead_scoregte70 - Entonces:
assign_to_user({user_id: "<admin_uuid>"})
3. Alertar al equipo cuando hay queja de reembolso
- Trigger:
tag.added - Si:
has_tagcontainsreembolso - 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_testequalstrue - Entonces:
add_tag({tag: "lab"})
5. Follow-up automático al cerrar bien
- Trigger:
conversation.closed - Si:
sentiment_rollinggte0.5 - Entonces:
send_automatic_message({event_type: "closing"})+set_qualification({stage: "won"})
6. Auto-cerrar leads idle de 7 días
- Trigger:
time.elapsedcon{ 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:
channelequalswhatsapp - Entonces:
send_webhookal 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:
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.
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.addedsolo 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
contextdeautomation_runs. - Si la fila tiene error rojo, una de las acciones falló — el mensaje está visible al hacer hover en el log.
[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_tagcon 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_departmentseguida deadd_internal_noteque 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.addeden 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ónadd_tages 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 3para ignorar el primer turno). - Log retention: los runs viven indefinidamente. Si crece mucho, podés purgar la tabla
automation_runsmensualmente 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.