Webhooks de Atendimentos

Receba notificacoes em tempo real quando atendimentos mudam de status, lembretes sao enviados e profissionais ou servicos sao atualizados. Cada evento e entregue com assinatura HMAC para verificacao de autenticidade.

Eventos disponíveis

EventoDescricao
appointment.createdNovo atendimento agendado
appointment.updatedDados do atendimento atualizados
appointment.confirmedAtendimento confirmado
appointment.completedAtendimento marcado como realizado
appointment.cancelledAtendimento cancelado
appointment.no_showPaciente nao compareceu
appointment.rescheduledReagendamento realizado
reminder.sentLembrete enviado com sucesso
reminder.failedFalha no envio do lembrete
professional.createdProfissional criado
professional.updatedProfissional atualizado
professional.deletedProfissional desativado
service.createdServico criado
service.updatedServico atualizado
service.deletedServico desativado

Configurar webhook

Webhooks sao configurados por conta. Use a API de webhooks ou o painel em Configuracoes → Integrações → Webhooks.

bash
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/webhooks" \
  -H "api_access_token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook": {
      "url": "https://seu-servidor.com/noovichat-webhook",
      "subscriptions": [
        "appointment_created",
        "appointment_confirmed",
        "appointment_cancelled",
        "appointment_completed",
        "appointment_no_show",
        "appointment_rescheduled",
        "reminder_sent",
        "reminder_failed"
      ]
    }
  }'

Nomes de evento nos webhooks

Na API de webhooks, use underscore: appointment_created. No payload entregue, o campo eventusa ponto: appointment.created.

Formato do payload

Todos os eventos de atendimento seguem a mesma estrutura base. O campodelivery_id e um UUID unico por entrega — use para idempotencia.

json
{
  "event": "appointment.confirmed",
  "occurred_at": "2026-06-15T08:05:00Z",
  "delivery_id": "550e8400-e29b-41d4-a716-446655440000",
  "account_id": 1,
  "data": {
    "appointment": {
      "id": 47,
      "public_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "status": "confirmed",
      "scheduled_at": "2026-06-15T09:00:00Z",
      "ends_at": "2026-06-15T10:00:00Z",
      "price_cents": 25000,
      "currency": "BRL",
      "contact": { "id": 42, "name": "Joao Silva" },
      "professional": { "id": 3, "name": "Dra. Ana Lima" },
      "service": { "id": 7, "name": "Consulta Inicial" }
    },
    "previous_changes": {
      "status": ["scheduled", "confirmed"]
    }
  }
}

Para appointment.rescheduled, o campodata.previous_scheduled_at contem o horario anterior. Para appointment.cancelled, contemdata.cancellation_reason.

Verificar assinatura HMAC

Cada requisicao webhook inclui o headerX-Noovichat-Signaturecom HMAC-SHA256 do body usando o segredo configurado no webhook. Sempre valide a assinatura antes de processar o evento.

const crypto = require('crypto');

function verifyWebhookSignature(req, secret) {
  const signature = req.headers['x-noovichat-signature'];
  if (!signature) return false;

  const body = JSON.stringify(req.body); // use raw body, not re-serialized
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express handler
app.post('/noovichat-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const secret = process.env.NOOVICHAT_WEBHOOK_SECRET;

  if (!verifyWebhookSignature(req, secret)) {
    return res.status(401).json({ error: 'invalid signature' });
  }

  const event = JSON.parse(req.body);
  console.log('Evento recebido:', event.event, event.delivery_id);

  // Processar evento...
  res.json({ ok: true });
});

Retentativas e idempotencia

TentativaDelayCondicao de abandono
1aImediata
2a1 minutoRetorna 2xx na 1a
3a10 minutosRetorna 2xx na 2a
4a1 horaRetorna 2xx na 3a
5a6 horasRetorna 2xx na 4a

Use delivery_id para idempotencia

O mesmo evento pode ser entregue mais de uma vez em caso de retentativa. Salve o delivery_id processado e ignore duplicatas baseando-se nele, nao noappointment.id.