Gestão de Followup
Ative e gerencie o followup automático de conversas (versão inicial com trigger idle)
Visão geral
A área de Followup permite que o gestor ative um serviço de reengajamento automático. Nesta primeira versão, o sistema considera apenas o cenário de inatividade (idle) para retomar o contato com o usuário.
Quando ativo, o sistema acompanha cada conversa e programa um horário (notify_at
) para recontato. Ao atingir esse horário, uma mensagem é enviada (via WhatsApp por padrão ou via webhook externo se configurado), respeitando regras de deduplicação, segurança e limite de tentativas.
Como funciona
- Ativação por host: se
public.host_config.followup = true
, o serviço entra em operação para o host. - Criação de acompanhamento: ao criar uma conversa, é criada uma linha em
public.conversations_followup
ligada a ela. - Reagendamento: sempre que
public.conversations.last_message_at
muda, recalculamosnotify_at
. - Envio: quando
notify_at <= now()
e regras ok, o sistema publica um job na filaSMART_FEEDBACK
para envio. - Contagem: a cada contato realizado, incrementa
hit
. Ao chegar emhit = 3
, a conversa é encerrada (fechada/desativada).
Cálculo do notify_at
(idle)
- Base: última atividade do lead (
public.conversations.last_message_at
da conversa mais recente do lead). - Atraso: definido em
public.host_config.followup_config.idle_minutes
(ou variantes legadas:iddle_minutes
,delay_minutes
,idle_minutes[]
,schedule[]
,delays[]
,per_hit
). - Fallback: se ausente/inválido, usa-se 30 minutos.
Regras de banco de dados (por host)
- Trigger cria/reativa
public.conversations_followup
ao criar conversa (com o mesmohost_id
). - Se existir registro inativo da conversa, cria uma nova linha ativa.
- Ao fechar a conversa (
public.conversations.closed = true
), desativa o followup correspondente. - Trigger atualiza
notify_at
ao alterarpublic.conversations.last_message_at
. - Limpeza diária: remove registros de
public.conversations_followup
comcreated_at
> 30 dias.
Pooling e publicação
- Processo de pooling a cada 1 minuto busca
public.conversations_followup
comactive = true
,hit < 3
enotify_at <= now()
. - Para cada item elegível, publica payload na fila
SMART_FEEDBACK
comretryCount = 0
,rebounce = true
erebounceInstructions
vindas degeneral_instructions
.
Deduplicação por lead e ativação
- Apenas a conversa mais recente do lead (maior
last_message_at
) é elegível. - Conversas antigas do mesmo lead têm
active = false
no followup. - Debounce por lead evita múltiplos enfileiramentos no mesmo ciclo.
Unicidade por telefone (idle)
- Telefone único por
host_id
ewpp_id
no contexto de idle. - Normalização com E.164 sem
+
(ex.:5511999998888
). - Matching com variantes (com/sem dígito 9).
- Atualiza
conversations_followup.wpp_id
da conversa e desativa duplicatas do mesmo telefone mantendo a mais recente (porlast_message_at
, fallbackcreated_at
). - Se mais de um
lead_id
tiver o mesmo telefone, prevalece a conversa com movimentação mais recente.
Agregação de hits por telefone (24h)
- O
hit
efetivo é no mínimo o total de conversas criadas nas últimas 24h para o mesmo telefone/host (considerando variantes com/sem 9), respeitandomax_notifications
do host.
Propagação de general_instructions
(24h)
- Se a conversa atual (mais recente para o telefone) estiver sem
general_instructions
e existir outra com valor preenchido nas últimas 24h, o valor é propagado.
Smart Feedback e SmartDecision
O consumer de SMART_FEEDBACK
executa um health check, chama a IA de SmartDecision e aplica guardrails de segurança:
- Decisão da IA:
decision
emsend | block | delay
, comconfidence
,reason_code
,risk_flags
,manager_rule
,channel
esuppression_until
quando aplicável. - Compatibilidade com legado é suportada e normalizada pelo servidor.
- Guardrails (fail-closed):
user_opt_out
/legal_restriction
→ forçablock
.manager_rule = hard_block
→block
.quiet_hours
ourecent_duplicate
sempriority_send
→ convertesend
paradelay
.- Campos obrigatórios:
channel
parasend
(padrão “whatsapp”),suppression_until
paradelay
. - Qualquer erro de template/parsing →
block
comreason_code = system_error
.
Reagendamento por delay
- Se
decision = delay
, atualizanotify_at = suppression_until
e mantémactive = true
. - Se
suppression_until
ausente equiet_hours = true
, calcula próxima janela útil às 09:05 (horário de São Paulo). Fallback: +60 minutos.
Combinação de instruções por hit
general_instructions
combina instruções do hit comsmartDecisionInstructions
e é persistido emconversations_followup.general_instructions
.
Logging
Registrar no globalData.tolkyRequestLog
:
- Contexto (host, rebounce, runtimeGlobalData).
- Resultado bruto da IA e o resultado pós-guardrails.
- Ação tomada: envio, bloqueio (com motivo) ou reagendamento (
notify_at
).
RabbitMQ
- Fila principal:
SMART_FEEDBACK
. - Dead-letter manual:
SMART_FEEDBACK_DLQ
. - Tentativas: até 2 processamentos no total (1ª + 1 retry). Em indisponibilidade, usa
nack
para adiar.
Envio via webhook externo (webhook_config
)
Se public.conversations_followup.webhook_config
estiver preenchido (JSONB), o envio é feito por HTTP ao invés de WhatsApp.
url
obrigatório.type
padrãoPOST
.headers
: objeto ou array de strings ("Header: Valor"
).queryParams
: envia body como query string quandotrue
.isAsyncCall
: dispara sem aguardar resposta.multipleCalls
: quando algum campo do body é array, faz múltiplas chamadas.cacheResults
,insertDialogue
,errorMessage
,instructions
,model
,isFixed
,includeDefaults
.schema
opcional em JSON Schema simplificado ou mapa direto.- Defaults no body:
message
,conversation_id
,host_id
,lead_id
,wpp_id
(quandoincludeDefaults = true
). - Em erro na chamada REST, trata como falha de envio.
Configuração
Você pode gerenciar a feature por dois meios principais:
- Ativar/desativar na
host_config
(followup
efollowup_config
). - Endpoints de configuração dedicados:
/api/config/followup
(GET/PUT).
Consulte a seção Desenvolvedor → API para esquemas e exemplos de requisição.
API de Configuração de Host e Followup
Esta documentação descreve os endpoints disponíveis para configurar hosts e o sistema de followup automático.
Autenticação
Todos os endpoints requerem autenticação via Bearer Token no header:
Authorization: Bearer <seu-token>
O token deve ser válido e associado ao host que você deseja configurar.
Endpoints de Configuração do Host
GET /api/config/hostConfig
Recupera todas as configurações do host autenticado.
Resposta de Sucesso (200):
{
"id": "uuid",
"host_id": "uuid",
"general_level": 1,
"model": "gpt-5",
"max_tokens": 1500,
"max_chunks": 5,
"full_context": true,
"persistent_conversation": true,
"followup": false,
"followup_config": null,
"conversation_analysis": true,
"leads": true,
"online_search_allowed": false,
"img_transc_allowed": true,
"site_transc_allowed": true,
"video_transc_allowed": null,
"max_anonymous_messages": 15,
"notification_email_l1": null,
"heat_score_description": "...",
"sentiment_score_criteria": "...",
"voice": "echo",
"created_at": "2024-01-01T00:00:00.000Z",
"updated_at": "2024-01-01T00:00:00.000Z"
}
POST /api/config/hostConfig
Cria uma nova configuração para o host. Falha se já existir uma configuração ativa.
Body (opcional):
{
"general_level": 1,
"model": "gpt-5",
"max_tokens": 1500,
"max_chunks": 5,
"full_context": true,
"persistent_conversation": true,
"followup": false,
"conversation_analysis": true,
"leads": true,
"online_search_allowed": false,
"img_transc_allowed": true,
"site_transc_allowed": true,
"video_transc_allowed": false,
"max_anonymous_messages": 15,
"notification_email_l1": "admin@exemplo.com",
"heat_score_description": "Critérios customizados...",
"sentiment_score_criteria": "Critérios customizados...",
"voice": "echo"
}
Resposta de Sucesso (201):
{
"id": "uuid",
"host_id": "uuid",
"message": "Configuração criada com sucesso",
"created_at": "2024-01-01T00:00:00.000Z"
}
PUT /api/config/hostConfig
Atualiza configurações existentes do host.
Body:
{
"general_level": 2,
"max_tokens": 2000,
"followup": true,
"online_search_allowed": true,
"notification_email_l1": "novo@exemplo.com"
}
Resposta de Sucesso (200):
{
"message": "Configurações atualizadas com sucesso",
"updated_fields": ["general_level", "max_tokens", "followup", "online_search_allowed", "notification_email_l1"],
"updated_at": "2024-01-01T00:00:00.000Z"
}
Endpoints de Configuração do Followup
GET /api/config/followup
Recupera as configurações de followup do host, incluindo valores padrão se não configurado.
Resposta de Sucesso (200):
{
"triggers": {
"idle": {
"active": true,
"auto": false,
"minutes": 15,
"max_notifications": 3,
"hits": [
{
"hit": 0,
"generalInstructions": "Retome a conversa com o usuário",
"dialogueInserts": "",
"smartDecisionInstructions": "NUNCA envie uma mensagem repetida para o usuário.",
"agentCalling": [],
"minutes": 15
}
]
},
"schedule": {
"active": false,
"hits": []
},
"conditionalAgents": {
"active": false,
"preCheck": [],
"decisions": []
}
}
}
PUT /api/config/followup
Atualiza as configurações de followup do host.
Body:
{
"followup_active": true,
"triggers": {
"idle": {
"active": true,
"auto": false,
"minutes": 30,
"max_notifications": 5,
"hits": [
{
"hit": 0,
"generalInstructions": "Olá! Notei que você estava interessado em nossos produtos. Posso ajudar com alguma dúvida?",
"smartDecisionInstructions": "Apenas envie se o usuário demonstrou interesse real no produto.",
"agentCalling": [
{
"schemaName": "public",
"agentName": "buscar_produtos",
"payload": {}
}
],
"minutes": 30
},
{
"hit": 1,
"generalInstructions": "Ainda estou aqui para ajudar! Tem alguma dúvida sobre nossos serviços?",
"smartDecisionInstructions": "NUNCA envie uma mensagem repetida para o usuário.",
"agentCalling": [],
"minutes": 60
}
]
},
"conditionalAgents": {
"active": true,
"preCheck": [
{
"schemaName": "public",
"agentName": "verificar_contexto",
"payload": {}
}
],
"decisions": [
{
"description": "Quando o usuário pediu ajuda ou demonstrou interesse",
"content": "Vamos continuar nossa conversa! Como posso ajudar?",
"agents": [
{
"schemaName": "public",
"agentName": "assistente_vendas",
"payload": {}
}
]
}
]
}
}
}
Resposta de Sucesso (200):
{
"message": "Configuração de followup atualizada com sucesso",
"updated_at": "2024-01-01T00:00:00.000Z",
"config": {
"triggers": {
// Configuração normalizada aplicada
}
}
}
POST /api/config/followup/reset
Reseta as configurações de followup para os valores padrão.
Body (opcional):
{
"keep_active": true,
"custom_minutes": 20,
"custom_max_notifications": 4
}
Resposta de Sucesso (200):
{
"message": "Configuração de followup resetada com sucesso",
"config": {
// Configuração padrão aplicada
},
"followup_active": true,
"updated_at": "2024-01-01T00:00:00.000Z"
}
Configuração de Followup - Detalhamento dos Campos
Estrutura Principal
{
"triggers": {
"idle": { /* Trigger por inatividade */ },
"schedule": { /* Trigger agendado (futuro) */ },
"conditionalAgents": { /* Agentes condicionais */ }
}
}
Trigger Idle (Inatividade)
O trigger idle
é ativado quando não há atividade na conversa por um tempo determinado.
Campos:
active
(boolean): Ativa/desativa o trigger idleauto
(boolean): Se true, o followup é completamente automáticominutes
(integer, 1-10080): Intervalo em minutos entre tentativasmax_notifications
(integer, 1-50): Número máximo de tentativas antes de desistirhits
(array): Lista de configurações por tentativa
Configuração de Hits
Cada hit representa uma tentativa de followup com configurações específicas:
hit
(integer): Número sequencial da tentativa (0, 1, 2…)generalInstructions
(string): Instruções gerais para a IA sobre como abordar o usuáriodialogueInserts
(string): Texto adicional a ser inserido no diálogosmartDecisionInstructions
(string): Instruções específicas para o SmartDecision avaliar se deve enviaragentCalling
(array): Lista de agentes a serem chamados durante este hitminutes
(integer): Minutos específicos para este hit (sobrescreve o global)
Exemplo de Hit Completo:
{
"hit": 0,
"generalInstructions": "Retome a conversa de forma amigável, referenciando o contexto anterior da conversa. Seja útil e não insistente.",
"dialogueInserts": "",
"smartDecisionInstructions": "APENAS envie se: 1) O usuário demonstrou interesse genuíno, 2) Não há sinais de irritação, 3) A conversa não foi encerrada explicitamente pelo usuário.",
"agentCalling": [
{
"schemaName": "public",
"agentName": "buscar_produtos",
"payload": {
"categoria": "interesse_demonstrado"
}
}
],
"minutes": 30
}
Agentes Condicionais
Os conditionalAgents
permitem executar agentes antes de tomar decisões de followup.
Campos:
active
(boolean): Ativa/desativa agentes condicionaispreCheck
(array): Agentes executados antes das decisõesdecisions
(array): Lista de decisões condicionais baseadas no contexto
Estrutura de Decisões:
{
"description": "Descrição da condição",
"content": "Conteúdo da mensagem a ser enviada",
"agents": [
{
"schemaName": "public",
"agentName": "nome_do_agente",
"payload": {}
}
]
}
SmartDecision - Sistema de Decisão Inteligente
O SmartDecision é executado antes de cada envio de followup para avaliar se é seguro e apropriado contactar o usuário.
Campos de Resposta da IA:
shouldSend
(boolean): Se deve enviar (compatibilidade)decision
(enum): “send”|“block”|“delay”confidence
(0-10): Confiança na decisãoexplanation
(string ≤200): Explicação da decisãomanager_rule
(enum): “none”|“priority_send”|“soft_block”|“hard_block”risk_flags
(object): Checklist de riscosreason_code
(enum): Código da razãochannel
(string): Canal de envio (obrigatório se decision=send)suppression_until
(datetime): Até quando adiar (obrigatório se decision=delay)
Risk Flags:
{
"recent_duplicate": false,
"user_opt_out": false,
"quiet_hours": false,
"not_aligned_to_brief": false,
"spam_risk": false,
"legal_restriction": false
}
Exemplos de Uso
1. Ativar Followup Básico
# Primeiro, ative o followup no host
curl -X PUT "https://api.exemplo.com/api/config/hostConfig" \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"followup": true}'
# Configure o followup com 2 tentativas a cada 60 minutos
curl -X PUT "https://api.exemplo.com/api/config/followup" \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"followup_active": true,
"triggers": {
"idle": {
"active": true,
"minutes": 60,
"max_notifications": 2,
"hits": [
{
"hit": 0,
"generalInstructions": "Retome a conversa educadamente",
"smartDecisionInstructions": "Apenas envie se o usuário demonstrou interesse"
}
]
}
}
}'
2. Configuração Avançada com Agentes
curl -X PUT "https://api.exemplo.com/api/config/followup" \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"triggers": {
"idle": {
"active": true,
"minutes": 30,
"max_notifications": 3,
"hits": [
{
"hit": 0,
"generalInstructions": "Primeira tentativa - seja sutil",
"agentCalling": [
{
"schemaName": "public",
"agentName": "verificar_interesse",
"payload": {}
}
]
},
{
"hit": 1,
"generalInstructions": "Segunda tentativa - ofereça ajuda específica",
"minutes": 120
}
]
},
"conditionalAgents": {
"active": true,
"decisions": [
{
"description": "Usuário pediu ajuda anteriormente",
"content": "Vi que você precisava de ajuda. Estou aqui!",
"agents": []
}
]
}
}
}'
3. Reset para Configuração Padrão
curl -X POST "https://api.exemplo.com/api/config/followup/reset" \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"keep_active": true,
"custom_minutes": 45,
"custom_max_notifications": 3
}'
Códigos de Erro
- 400: Dados inválidos ou validação falhou
- 401: Token de autenticação inválido
- 403: Acesso negado
- 404: Recurso não encontrado (host ou configuração)
- 409: Conflito (tentativa de criar configuração duplicada)
- 500: Erro interno do servidor
Validações e Limites
Host Config:
general_level
: 1-10max_tokens
: 100-32000max_chunks
: 1-20max_anonymous_messages
: ≥0notification_email_l1
: formato de email válido
Followup Config:
minutes
: 1-10080 (1 minuto a 1 semana)max_notifications
: 1-50hits
: máximo 50 hits por configuração- Campos de string: tamanho razoável para evitar spam
Webhook Externo
O sistema suporta envio via webhook externo quando configurado no campo webhook_config
de uma conversa específica. Consulte a documentação do followupV1.md para detalhes sobre esta funcionalionalidade.
Cache
As configurações são automaticamente cacheadas para melhor performance. O cache é limpo automaticamente quando configurações são atualizadas.
Logs e Auditoria
Todas as operações são logadas no sistema tolkyRequestLog
para auditoria e troubleshooting. Os logs incluem:
- Parâmetros de entrada
- Resultados de validação
- Decisões do SmartDecision
- Ações executadas (envio, bloqueio, reagendamento)
Suporte
Para dúvidas ou problemas com a API, consulte os logs do sistema ou entre em contato com a equipe de desenvolvimento.