Skip to main content
Exemplos de como integrar follow-ups com seu fluxo de negócio.
// followup-automation.ts
import { CordialyClient } from './cordialy';

const cordialy = new CordialyClient(process.env.CORDIALY_API_KEY!);

// IDs das configs de follow-up (buscar via GET /followups/configs)
const CONFIG = {
  REATIVACAO: process.env.FOLLOWUP_CONFIG_REATIVACAO!,
  POS_REUNIAO: process.env.FOLLOWUP_CONFIG_POS_REUNIAO!,
  CARRINHO: process.env.FOLLOWUP_CONFIG_CARRINHO!,
  COBRANCA: process.env.FOLLOWUP_CONFIG_COBRANCA!,
};

// ── Agendamento automático ──────────────────────────────────────────────────

// Carrinho abandonado → follow-up em 2 horas
export async function onCarrinhoAbandonado(leadId: string) {
  const scheduledAt = new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString();
  await cordialy.followups.schedule({
    lead_id: leadId,
    config_id: CONFIG.CARRINHO,
    scheduled_at: scheduledAt,
  });
}

// Reunião finalizada → follow-up no dia seguinte às 10h
export async function onReuniaoFinalizada(leadId: string) {
  const amanha = new Date();
  amanha.setDate(amanha.getDate() + 1);
  amanha.setHours(10, 0, 0, 0);

  await cordialy.followups.schedule({
    lead_id: leadId,
    config_id: CONFIG.POS_REUNIAO,
    scheduled_at: amanha.toISOString(),
  });
}

// Fatura em atraso → follow-up imediato
export async function onFaturaAtrasada(leadId: string) {
  await cordialy.followups.schedule({
    lead_id: leadId,
    config_id: CONFIG.COBRANCA,
    // sem scheduled_at → usa trigger_delay_minutes da config
  });
}

// ── Cancelamento automático ─────────────────────────────────────────────────

// Cancela todos os follow-ups pendentes de um lead
export async function cancelarFollowupsPendentes(leadId: string) {
  const todos = await cordialy.followups.scheduled(leadId);
  const pendentes = todos.filter((f: any) => f.status === 'pending');

  await Promise.all(pendentes.map((f: any) => cordialy.followups.cancel(f.id)));

  return pendentes.length;
}

// Lead converteu → cancela follow-ups e marca status
export async function onLeadConverteu(leadId: string) {
  const cancelados = await cancelarFollowupsPendentes(leadId);
  await cordialy.leads.update(leadId, { status: 'converted' });
  console.log(`Lead ${leadId} convertido. ${cancelados} follow-up(s) cancelado(s).`);
}

// Compra finalizada → cancela carrinho e reativa
export async function onCompraConcluida(leadId: string) {
  await cancelarFollowupsPendentes(leadId);
  await onLeadConverteu(leadId);
}

// ── Cron: verificar follow-ups ──────────────────────────────────────────────

// Rodar a cada hora para verificar se há follow-ups que precisam de atenção
export async function verificarFollowupsPendentes() {
  const pendentes = await cordialy.followups.scheduled();
  const agora = new Date();

  for (const followup of pendentes as any[]) {
    const scheduledAt = new Date(followup.scheduled_at);
    const minutosRestantes = Math.round((scheduledAt.getTime() - agora.getTime()) / 60000);

    if (minutosRestantes <= 30 && minutosRestantes > 0) {
      // Notifica internamente antes do disparo
      await slack.send('#vendas',
        `⏰ Follow-up em ${minutosRestantes} min para lead ${followup.lead_id}`
      );
    }
  }
}