Pular para conteúdo

WhatsApp — Chronos

Visao Geral

O Chronos usa WhatsApp para dois fluxos principais:

  1. Confirmacao 24h antes: Envia mensagem pedindo ao cliente que confirme o agendamento
  2. Cancelamento agendado: Quando o profissional cancela, a mensagem de aviso e enviada com 1h de delay (permitindo desfazer)

Arquitetura

graph TB
    subgraph Chronos
        S1[Reminder Scheduler<br/>15 min]
        S2[Message Sender<br/>5 min]
        WH[Webhook Controller]
        AS[AppointmentService]
        SM[(ScheduledMessage)]
    end

    subgraph Herald
        H[Herald API]
    end

    subgraph WhatsApp
        EVO[Evolution API]
        WA[WhatsApp]
    end

    S1 -->|Confirmacao 24h| H
    S2 -->|Msgs pendentes| H
    AS -->|Cancelamento| SM
    SM -->|Due| S2
    H --> EVO --> WA
    WA -->|Resposta| EVO --> H
    H -->|Forward| WH
    WH -->|CONFIRMAR| AS

Fluxo de Confirmacao

1. Scheduler envia pedido (24h antes)

O AppointmentReminderScheduler roda a cada 15 minutos e:

  1. Busca agendamentos 23-25 horas no futuro
  2. Filtra: confirmationRequestSentAt == null e customer tem telefone
  3. Verifica se o WhatsApp do tenant esta conectado
  4. Envia mensagem:
Ola {nome}! Seu agendamento de {servico} esta marcado para
amanha ({data}) as {hora} com {profissional}.
Para confirmar, responda CONFIRMAR.
  1. Atualiza appointment.confirmationRequestSentAt

2. Cliente responde

O cliente responde com "CONFIRMAR" ou "CONFIRMO" no WhatsApp.

3. Herald encaminha para Chronos

Herald recebe a mensagem via Evolution API e faz POST para:

POST /webhooks/whatsapp/incoming
{
  "ownerRef": "chronos-{tenantId}",
  "phoneFrom": "5511987654321",
  "message": "CONFIRMAR",
  "timestamp": "2026-02-20T10:00:00Z"
}

4. Chronos confirma o agendamento

O WhatsAppWebhookController:

  1. Extrai tenantId do ownerRef
  2. Normaliza mensagem para maiuscula
  3. Verifica se contem "CONFIRMAR" ou "CONFIRMO"
  4. Busca agendamentos proximos (48h) com telefone correspondente
  5. Confirma o mais proximo
  6. Atualiza confirmedViaWhatsAppAt
  7. Envia ack: "Seu agendamento foi confirmado!"

Fluxo de Cancelamento

1. Profissional cancela

No AppointmentService.updateStatus(), quando status muda para CANCELLED:

  1. Email imediato: Envia notificacao de cancelamento ao cliente
  2. WhatsApp agendado: Cria ScheduledMessage com scheduledFor = now + 1h

2. Mensagem fica pendente

A ScheduledMessage fica na collection MongoDB:

{
  "channel": "WHATSAPP",
  "status": "PENDING",
  "scheduledFor": "2026-02-20T11:00:00Z",
  "referenceId": "appointment-id",
  "referenceType": "APPOINTMENT"
}

3. Desfazer cancelamento (antes de 1h)

Se o profissional desfizer o cancelamento:

  1. Status volta de CANCELLED para CONFIRMED
  2. Todas as ScheduledMessage PENDING deste appointment sao canceladas
  3. A mensagem de WhatsApp nao e enviada

4. Envio automatico (apos 1h)

O ScheduledMessageSenderScheduler roda a cada 5 minutos:

  1. Busca mensagens PENDING com scheduledFor <= now
  2. Envia via ChronosWhatsAppService
  3. Atualiza status para SENT

Configuracao

ownerRef

Cada tenant tem um ownerRef no formato:

chronos-{tenantId}

application.properties

quarkus.rest-client.herald-api.url=${HERALD_API_URL:http://localhost:5004}
chronos.herald.auth.user=${HERALD_AUTH_EMAIL:chronos}
chronos.herald.auth.password=${HERALD_AUTH_PASSWORD:CHRONOS987!@#}

Conectar WhatsApp

A configuracao do numero WhatsApp e feita via Herald API. Cada tenant precisa:

  1. Registrar config no Herald: POST /whatsapp/configs
  2. Conectar via QR Code: GET /whatsapp/configs/{ownerRef}/qr
  3. Verificar conexao: GET /whatsapp/configs/{ownerRef}/connection

Circuit Breaker

O ChronosWhatsAppService implementa:

  • Token caching: Token do OATH reutilizado ate expirar
  • Circuit breaker: 5 falhas → aberto por 1 minuto
  • Status cache: Verifica conexao a cada 5 minutos (ConcurrentHashMap)
  • Fallback: Se WhatsApp desconectado, loga erro mas nao impede o fluxo

Entidades

ScheduledMessage

Campo Tipo Descricao
tenantId ObjectId Tenant
channel String WHATSAPP ou EMAIL
recipientPhone String Telefone do destinatario
content String Conteudo da mensagem
triggerType String CANCELLATION_NOTICE, etc.
referenceId String ID do agendamento
scheduledFor Instant Quando enviar
status String PENDING, SENT, CANCELLED

Campos no Appointment

Campo Tipo Descricao
confirmationRequestSentAt Instant Quando a msg 24h foi enviada
confirmedViaWhatsAppAt Instant Quando o cliente confirmou