Ir para o conteúdo

Webhooks

A plataforma envia notificações HTTP em tempo real para o endpoint configurado no seu tenant sempre que um evento relevante ocorre.

Os webhooks são opcionais. O fluxo de integração funciona sem eles — você pode sempre consultar o status via GET /api/v1/checkins/{id}.


Como funcionam

Quando um check-in é processado, a plataforma envia uma requisição POST com o evento para o seu endpoint:

Evento na plataforma
POST {seu-endpoint} com payload JSON
Resposta esperada: qualquer 2xx

Se o seu endpoint retornar erro (4xx, 5xx ou timeout), a plataforma realiza novas tentativas automaticamente com intervalos crescentes.


Configuração

O endpoint de webhook é configurado durante o onboarding. Para atualizar o endpoint ou a chave de assinatura, entre em contato com a equipe de suporte.


Estrutura do evento

Todos os eventos seguem o mesmo envelope JSON:

{
  "eventId": "550e8400-e29b-41d4-a716-446655440000",
  "eventType": "checkin.published",
  "occurredAt": "2026-07-03T08:30:10Z",
  "tenantId": "1a2b3c4d-0000-0000-0000-000000000001",
  "correlationId": "2b3c4d5e-aaaa-bbbb-cccc-000000000002",
  "payload": { ... }
}
Campo Tipo Descrição
eventId Guid Identificador único do evento
eventType string Tipo do evento (ver tabela abaixo)
occurredAt DateTime UTC Momento em que o evento foi gerado
tenantId Guid Seu tenant
correlationId Guid Rastreabilidade — igual ao da requisição original
payload object Dados específicos do evento

Headers HTTP

Cada chamada inclui os seguintes headers:

Header Descrição
Content-Type: application/json Sempre JSON
X-Signature: sha256={hash} Assinatura HMAC-SHA256 para verificar autenticidade
X-Event-Type: {tipo} Tipo do evento (ex: checkin.published)
X-Correlation-Id: {uuid} Rastreabilidade
X-Tenant-Id: {uuid} Seu tenant

Verificação de assinatura

Para garantir que o evento foi enviado pela plataforma (e não por terceiros), verifique a assinatura HMAC-SHA256 antes de processar.

Como verificar

  1. Leia o header X-Signature (formato: sha256={hex})
  2. Compute HMAC-SHA256(secret, corpo_raw) usando o secret fornecido no onboarding
  3. Compare o resultado com o valor do header usando comparação em tempo constante

Exemplo em C

using System.Security.Cryptography;
using System.Text;

bool IsSignatureValid(string secret, string requestBody, string signatureHeader)
{
    var expectedHex = signatureHeader.StartsWith("sha256=")
        ? signatureHeader[7..]
        : signatureHeader;

    var key  = Encoding.UTF8.GetBytes(secret);
    var data = Encoding.UTF8.GetBytes(requestBody);
    var hash = HMACSHA256.HashData(key, data);
    var computed = Convert.ToHexString(hash).ToLowerInvariant();

    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(computed),
        Encoding.UTF8.GetBytes(expectedHex));
}

Exemplo em Python

import hmac
import hashlib

def is_valid(secret: str, body: bytes, signature_header: str) -> bool:
    expected = signature_header.removeprefix("sha256=")
    computed = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(computed, expected)

Importante: use sempre comparação em tempo constante (FixedTimeEquals / compare_digest) para prevenir ataques de timing.


Eventos disponíveis

Eventos de Check-in

EventType Quando é enviado
checkin.received Check-in recebido pela plataforma
checkin.identified Aluno identificado com sucesso
checkin.authorization-started Verificação de autorização iniciada
checkin.authorized Aluno autorizado
checkin.registered Check-in registrado no sistema
checkin.published Check-in aprovado e publicado (evento final do fluxo bem-sucedido)
checkin.not-identified Aluno não encontrado — fluxo encerrado
checkin.rejected Aluno sem autorização válida — fluxo encerrado

Payloads dos eventos

checkin.published (fluxo bem-sucedido)

{
  "eventId": "880e8400-e29b-41d4-a716-446655440003",
  "eventType": "checkin.published",
  "occurredAt": "2026-07-03T08:30:10Z",
  "tenantId": "1a2b3c4d-0000-0000-0000-000000000001",
  "correlationId": "2b3c4d5e-aaaa-bbbb-cccc-000000000002",
  "payload": {
    "tenantId": "1a2b3c4d-0000-0000-0000-000000000001",
    "checkinId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "studentId": "9f8e7d6c-0000-0000-0000-000000000099",
    "source": "Internal",
    "checkinOccurredAt": "2026-07-03T08:30:00Z",
    "correlationId": "2b3c4d5e-aaaa-bbbb-cccc-000000000002"
  }
}

checkin.not-identified (aluno não encontrado)

{
  "eventId": "660e8400-e29b-41d4-a716-446655440001",
  "eventType": "checkin.not-identified",
  "occurredAt": "2026-07-03T08:30:02Z",
  "tenantId": "1a2b3c4d-0000-0000-0000-000000000001",
  "correlationId": "2b3c4d5e-aaaa-bbbb-cccc-000000000002",
  "payload": {
    "tenantId": "1a2b3c4d-0000-0000-0000-000000000001",
    "checkinId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "source": "Internal",
    "rejectionReason": "STUDENT_NOT_FOUND",
    "checkinOccurredAt": "2026-07-03T08:30:00Z",
    "correlationId": "2b3c4d5e-aaaa-bbbb-cccc-000000000002"
  }
}

checkin.rejected (aluno sem autorização)

{
  "eventId": "770e8400-e29b-41d4-a716-446655440002",
  "eventType": "checkin.rejected",
  "occurredAt": "2026-07-03T08:30:05Z",
  "tenantId": "1a2b3c4d-0000-0000-0000-000000000001",
  "correlationId": "2b3c4d5e-aaaa-bbbb-cccc-000000000002",
  "payload": {
    "tenantId": "1a2b3c4d-0000-0000-0000-000000000001",
    "checkinId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "studentId": "9f8e7d6c-0000-0000-0000-000000000099",
    "source": "Internal",
    "rejectionReason": "AUTHORIZATION_EXPIRED",
    "checkinOccurredAt": "2026-07-03T08:30:00Z",
    "correlationId": "2b3c4d5e-aaaa-bbbb-cccc-000000000002"
  }
}

Campos do payload de check-in

Campo Tipo Presente em Descrição
tenantId Guid todos Seu tenant
checkinId Guid todos ID do check-in na plataforma
source string todos Origem do check-in
checkinOccurredAt DateTime UTC todos Momento físico do check-in (conforme enviado)
correlationId string todos Rastreabilidade
studentId Guid eventos após identificação ID do aluno. Ausente em received e not-identified.
rejectionReason string not-identified, rejected Motivo do encerramento

Boas práticas para o seu endpoint receptor

  1. Responda com 2xx imediatamente — processe o evento de forma assíncrona se necessário
  2. Verifique sempre a assinatura antes de processar
  3. Trate duplicatas — o mesmo evento pode ser entregue mais de uma vez; use o eventId para detectar duplicatas
  4. Não confie apenas nos webhooks — consulte GET /checkins/{id} como fallback se não receber um evento esperado
  5. Retorne 2xx mesmo que você já tenha processado o evento — isso evita re-tentativas desnecessárias