Skip to main content

Resumo

Os webhooks permitem que você receba notificações em tempo real quando o status de verificação KYC mudar. Gu1 envia automaticamente requisições HTTP POST para seu endpoint webhook configurado sempre que o status de uma validação for atualizado.

Por Que Usar Webhooks?

Atualizações em Tempo Real

Receba notificações instantâneas quando o status de verificação mudar

Eficiente

Não é necessário consultar a API repetidamente

Fluxos Automáticos

Atualize automaticamente contas de usuário com base nos resultados de verificação

Melhor UX

Notifique os clientes imediatamente após a verificação

Eventos de Webhook

Gu1 envia webhooks para os seguintes eventos de validação KYC:
Tipo de EventoDescriçãoQuando é Disparado
kyc.validation_createdSessão de validação criadaQuando você cria uma nova validação KYC
kyc.validation_in_progressCliente iniciou verificaçãoCliente começa o processo de verificação
kyc.validation_approvedVerificação aprovadaIdentidade verificada com sucesso
kyc.validation_rejectedVerificação rejeitadaVerificação de identidade falhou
kyc.validation_abandonedCliente abandonou o processoCliente abandonou sem completar
kyc.validation_expiredSessão de validação expirouSessão expirou (tipicamente após 7 dias)

Configurar Webhooks

Passo 1: Configurar Webhook no Dashboard

Configure a URL do seu webhook no dashboard do Gu1:
  1. Navegar para Configurações de Webhooks
    • Faça login no seu dashboard do Gu1
    • Vá para ConfiguraçõesWebhooks
  2. Criar Novo Webhook
    • Clique em Adicionar Webhook ou Criar Webhook
    • Insira um nome descritivo (ex., “Webhook KYC Produção”)
    • Insira a URL do seu webhook (deve ser HTTPS): https://seuapp.com/webhooks/kyc
  3. Selecionar Ambiente
    • Escolha Sandbox para testes
    • Escolha Produção para eventos ao vivo
  4. Inscrever-se em Eventos
    • Selecione todos os eventos KYC ou específicos:
      • kyc.validation_created
      • kyc.validation_in_progress
      • kyc.validation_approved
      • kyc.validation_rejected
      • kyc.validation_abandoned
      • kyc.validation_expired
  5. Salvar seu Segredo
    • Um segredo de webhook será gerado automaticamente
    • Copie e salve este segredo - você precisará dele para verificar as assinaturas de webhook
    • Não será possível vê-lo novamente (mas você pode regenerá-lo depois)
  6. Ativar o Webhook
    • Ative o webhook marcando-o como Habilitado
    • Clique em Salvar
Você pode criar webhooks separados para os ambientes sandbox e produção com URLs diferentes.

Passo 2: Criar um Endpoint de Webhook

Crie um endpoint na sua aplicação para receber requisições POST de webhook:
const express = require('express');
const crypto = require('crypto');

const app = express();

// IMPORTANTE: Usar body raw para verificação de assinatura
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString('utf8');
  }
}));

app.post('/webhooks/kyc', async (req, res) => {
  try {
    // Verificar assinatura do webhook
    const signature = req.headers['x-webhook-signature'];
    const webhookSecret = process.env.GUENO_WEBHOOK_SECRET;

    if (!verifySignature(req.rawBody, signature, webhookSecret)) {
      console.error('Assinatura de webhook inválida');
      return res.status(401).json({ error: 'Assinatura inválida' });
    }

    // Extrair dados do webhook
    const { event, timestamp, organizationId, payload } = req.body;

    console.log('Webhook KYC recebido:', {
      event,
      validationId: payload.validationId,
      status: payload.status
    });

    // Processar o webhook de acordo com o tipo de evento
    await handleKycWebhook(event, payload);

    // Retornar 200 para confirmar recepção
    res.status(200).json({
      success: true,
      message: 'Webhook recebido'
    });
  } catch (error) {
    console.error('Erro no webhook:', error);
    // Ainda assim retornar 200 para prevenir tentativas
    res.status(200).json({
      success: false,
      error: error.message
    });
  }
});

// Verificar assinatura HMAC
function verifySignature(rawBody, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return signature === expectedSignature;
}

async function handleKycWebhook(event, data) {
  const { validationId, entityId, entity, status } = data;

  // Atualizar seu banco de dados com o ID de validação do Gu1
  await db.updateEntity(entity.externalId, {
    kycValidationId: validationId,
    kycStatus: status,
    lastUpdated: new Date()
  });

  // Realizar ações de acordo com o tipo de evento
  switch (event) {
    case 'kyc.validation_created':
      console.log('Validação KYC criada para:', entity.name);
      break;

    case 'kyc.validation_in_progress':
      await notifyCustomer(entity.externalId, 'verificacao-iniciada');
      break;

    case 'kyc.validation_approved':
      // Extrair dados verificados
      const { extractedData, verifiedFields } = data;

      await db.updateEntity(entity.externalId, {
        verifiedData: extractedData,
        verifiedFields: verifiedFields,
        verifiedAt: data.verifiedAt,
        isVerified: true
      });

      await activateCustomerAccount(entity.externalId);
      await notifyCustomer(entity.externalId, 'verificacao-aprovada');
      break;

    case 'kyc.validation_rejected':
      await db.updateEntity(entity.externalId, {
        isVerified: false,
        rejectionReasons: data.warnings
      });

      await notifyCustomer(entity.externalId, 'verificacao-rejeitada');
      break;

    case 'kyc.validation_abandoned':
      await notifyCustomer(entity.externalId, 'verificacao-incompleta');
      break;

    case 'kyc.validation_expired':
      await notifyCustomer(entity.externalId, 'verificacao-expirada');
      break;
  }
}

Passo 3: Tornar seu Endpoint Publicamente Acessível

Seu endpoint de webhook deve:
  • Ser publicamente acessível via HTTPS
  • Poder receber requisições POST
  • Retornar código de status 200 rapidamente (dentro de 30 segundos)
Para desenvolvimento local, use ferramentas como ngrok para criar uma URL pública que faça túnel para seu servidor local.

Segurança: Verificar Assinaturas de Webhook

Sempre verifique as assinaturas de webhook para garantir que as requisições vêm do Gu1.

Como Funciona a Verificação de Assinaturas

  1. Gu1 gera uma assinatura HMAC SHA-256 do payload do webhook usando seu segredo
  2. A assinatura é enviada no header X-Webhook-Signature
  3. Seu servidor recalcula a assinatura usando o mesmo segredo
  4. Compara as assinaturas - se coincidirem, o webhook é autêntico

Exemplos de Verificação de Assinatura

const crypto = require('crypto');

function verifySignature(rawBody, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return signature === expectedSignature;
}

// No seu endpoint de webhook:
app.post('/webhooks/kyc', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const secret = process.env.GUENO_WEBHOOK_SECRET;

  if (!verifySignature(req.rawBody, signature, secret)) {
    return res.status(401).json({ error: 'Assinatura inválida' });
  }

  // Processar webhook...
});
Nunca omita a verificação de assinatura em produção. Sem ela, qualquer pessoa pode enviar webhooks falsos para seu endpoint.

Headers HTTP

Cada requisição de webhook inclui estes headers:
HeaderDescriçãoExemplo
Content-TypeSempre application/jsonapplication/json
X-Webhook-EventTipo de eventokyc.validation_approved
X-Webhook-IDID de configuração do webhook550e8400-e29b-41d4-a716-446655440000
X-Webhook-TimestampTimestamp ISO 86012025-01-15T10:30:00.000Z
X-Webhook-SignatureAssinatura HMAC SHA-256abc123...

Estrutura do Payload de Webhook

Todos os webhooks seguem esta estrutura padrão:
{
  "event": "kyc.validation_approved",
  "timestamp": "2025-01-15T11:00:00Z",
  "organizationId": "org-123",
  "payload": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "externalId": "customer_xyz789",
      "name": "John Doe",
      "type": "person"
    },
    "status": "approved"
    // ... campos específicos do evento
  }
}

Campos Comuns do Payload

event
string
O tipo de evento (ex., kyc.validation_approved)
timestamp
string
Timestamp ISO 8601 quando o evento ocorreu
organizationId
string
Seu ID de organização
payload.validationId
string
O ID de validação KYC no Gu1
payload.entityId
string
O ID da entidade (pessoa) sendo verificada
payload.entity
object
Informações da entidade incluindo seu externalId para fácil busca
payload.status
string
Status atual de validação: pending, in_progress, approved, rejected, abandoned, expired

Payloads Específicos por Evento

kyc.validation_created

Enviado quando uma nova validação KYC é criada.
{
  "event": "kyc.validation_created",
  "timestamp": "2025-01-15T10:30:00Z",
  "organizationId": "org-123",
  "payload": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "externalId": "customer_xyz789",
      "name": "John Doe",
      "type": "person"
    },
    "status": "pending"
  }
}

kyc.validation_in_progress

Enviado quando um cliente inicia o processo de verificação.
{
  "event": "kyc.validation_in_progress",
  "timestamp": "2025-01-15T10:35:00Z",
  "organizationId": "org-123",
  "payload": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "in_progress"
  }
}

kyc.validation_approved

Enviado quando a verificação é completada com sucesso.
{
  "event": "kyc.validation_approved",
  "timestamp": "2025-01-15T11:00:00Z",
  "organizationId": "org-123",
  "payload": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "approved",
    "verifiedAt": "2025-01-15T11:00:00Z",
    "extractedData": {
      "firstName": "John",
      "lastName": "Doe",
      "dateOfBirth": "1990-05-20",
      "nationality": "US",
      "documentNumber": "AB123456",
      "documentType": "passport"
    },
    "verifiedFields": [
      "firstName",
      "lastName",
      "dateOfBirth",
      "nationality",
      "documentNumber"
    ],
    "warnings": [],
    "decision": {
      "id_verification": "pass",
      "face_match": "pass",
      "liveness": "pass"
    }
  }
}
Campos Adicionais:
  • verifiedAt: Timestamp quando a verificação foi aprovada
  • extractedData: Informações pessoais extraídas do documento
  • verifiedFields: Array de campos que foram verificados com sucesso
  • warnings: Array de avisos detectados durante a verificação
  • decision: Resultados de verificação (imagens/URLs removidas por segurança)

kyc.validation_rejected

Enviado quando a verificação falha.
{
  "event": "kyc.validation_rejected",
  "timestamp": "2025-01-15T11:00:00Z",
  "organizationId": "org-123",
  "payload": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "rejected",
    "verifiedAt": "2025-01-15T11:00:00Z",
    "extractedData": {},
    "verifiedFields": [],
    "warnings": [
      "Falhou a verificação de autenticidade do documento",
      "Confiança baixa na correspondência facial"
    ],
    "decision": {
      "id_verification": "fail",
      "face_match": "fail",
      "liveness": "pass"
    }
  }
}

kyc.validation_abandoned

Enviado quando um cliente inicia mas não completa a verificação.
{
  "event": "kyc.validation_abandoned",
  "timestamp": "2025-01-15T10:45:00Z",
  "organizationId": "org-123",
  "payload": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "abandoned"
  }
}

kyc.validation_expired

Enviado quando uma sessão de validação expira sem ser completada.
{
  "event": "kyc.validation_expired",
  "timestamp": "2025-01-15T12:00:00Z",
  "organizationId": "org-123",
  "payload": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "expired",
    "expiresAt": "2025-01-22T10:30:00Z"
  }
}

Política de Tentativas

Se seu endpoint de webhook falhar em responder com um código de status 2xx, Gu1 automaticamente tentará novamente a entrega. Política de Tentativas Padrão:
  • Tentativas Máximas: 3 tentativas
  • Delay Inicial: 1000ms (1 segundo)
  • Multiplicador de Backoff: 2x
  • Sequência de Tentativas: 1s → 2s → 4s
Exemplo de Timeline:
  1. Tentativa inicial em T+0s
  2. Primeira nova tentativa em T+1s
  3. Segunda nova tentativa em T+3s (1s + 2s)
  4. Terceira nova tentativa em T+7s (1s + 2s + 4s)
Critério de Sucesso:
  • Códigos de status HTTP 200-299 são considerados bem-sucedidos
  • Qualquer outro código de status ou erro de rede dispara uma nova tentativa
Timeout:
  • Cada tentativa tem um timeout de 30 segundos
  • Se seu endpoint não responder dentro de 30 segundos, a tentativa é marcada como falhada
Você pode ver todas as tentativas de entrega de webhook (incluindo novas tentativas) na seção Webhooks → Histórico do seu dashboard.

Melhores Práticas

Sempre retorne um código de status 200 o mais rápido possível para confirmar a recepção. Processe o webhook assincronamente se necessário.
app.post('/webhooks/kyc', async (req, res) => {
  // Confirmar imediatamente
  res.status(200).send('OK');

  // Processar assincronamente
  processWebhook(req.body).catch(console.error);
});
Sempre verifique o header X-Webhook-Signature para garantir que o webhook é autêntico.
const signature = req.headers['x-webhook-signature'];
if (!verifySignature(req.rawBody, signature, secret)) {
  return res.status(401).json({ error: 'Assinatura inválida' });
}
Você pode receber o mesmo webhook múltiplas vezes. Use o validationId para garantir que você processa cada evento apenas uma vez.
async function handleWebhook(webhook) {
  const alreadyProcessed = await db.checkWebhookProcessed(
    webhook.payload.validationId,
    webhook.event
  );

  if (alreadyProcessed) {
    return; // Pular duplicado
  }

  // Processar webhook
  await processValidation(webhook.payload);

  // Marcar como processado
  await db.markWebhookProcessed(
    webhook.payload.validationId,
    webhook.event
  );
}
O webhook inclui entity.externalId que é o ID que você forneceu ao criar a entidade. Use-o para buscar o cliente no seu banco de dados.
const customer = await db.findCustomer({
  externalId: data.entity.externalId
});
Armazene o validationId do Gu1 no seu banco de dados. Isso permite que você consulte detalhes de validação posteriormente se necessário.
await db.updateCustomer(customer.id, {
  kycValidationId: data.validationId,
  kycStatus: data.status
});
Se o processamento falhar, registre o erro mas ainda assim retorne 200 para prevenir novas tentativas. Armazene webhooks falhados para revisão manual.
try {
  await processWebhook(payload);
} catch (error) {
  await db.saveFailedWebhook({
    payload,
    error: error.message,
    receivedAt: new Date()
  });

  // Ainda assim retornar 200
  res.status(200).json({ success: false });
}
Crie configurações de webhook separadas para os ambientes sandbox e produção.
  • Sandbox: Usar para testes com dados de teste
  • Produção: Usar para verificações de clientes ao vivo
Isso permite que você teste o tratamento de webhooks de forma segura sem afetar sistemas de produção.

Testar Webhooks

Testar no Dashboard

  1. Vá para ConfiguraçõesWebhooks
  2. Selecione seu webhook
  3. Clique em Testar Webhook
  4. Gu1 enviará um evento de teste para seu endpoint
  5. Verifique o status de resposta e logs

Desenvolvimento Local

Use ngrok para expor seu servidor local:
# Iniciar ngrok
ngrok http 3000

# Use a URL do ngrok como sua URL de webhook
https://abc123.ngrok.io/webhooks/kyc

Fluxo de Teste

  1. Crie um webhook sandbox apontando para seu endpoint de desenvolvimento
  2. Crie uma validação KYC de teste
  3. Seu endpoint de webhook recebe kyc.validation_created
  4. Complete a verificação (ou simule diferentes resultados)
  5. Seu endpoint de webhook recebe atualizações de status

Monitoramento e Debugging

Logs de Webhook

Ver histórico de entrega de webhook no seu dashboard:
  1. Vá para ConfiguraçõesWebhooks
  2. Selecione seu webhook
  3. Clique em Ver Logs ou Histórico
Os logs incluem:
  • Timestamp de cada tentativa de entrega
  • Código de status HTTP recebido
  • Body de resposta
  • Tempo de resposta
  • Mensagens de erro (se houver)
  • Tentativas de nova tentativa

Estatísticas de Webhook

Cada webhook mostra:
  • Disparos Totais: Número total de vezes que o webhook foi disparado
  • Contagem de Sucessos: Entregas bem-sucedidas
  • Contagem de Falhas: Entregas falhadas
  • Último Disparado: Timestamp da última tentativa
  • Último Sucesso: Timestamp da última entrega bem-sucedida
  • Última Falha: Timestamp da última falha

Solução de Problemas

Verifique estes elementos:
  • URL do webhook é publicamente acessível via HTTPS
  • Firewall permite requisições POST de entrada do Gu1
  • Endpoint retorna código de status 200 dentro de 30 segundos
  • Webhook está configurado e habilitado no dashboard
  • Ambiente correto selecionado (sandbox vs produção)
  • Verifique logs do servidor para requisições de entrada
  • Verifique que o webhook está inscrito nos tipos de evento corretos
Causas comuns:
  • Usando segredo incorreto (verifique o dashboard para o segredo atual)
  • Verificando assinatura no JSON com parsing em vez do body raw
  • Segredo não salvo corretamente após criar webhook
  • Problemas de codificação (garantir UTF-8)
Solução:
// INCORRETO - verificando body com parsing
const signature = crypto.createHmac('sha256', secret)
  .update(JSON.stringify(req.body))
  .digest('hex');

// CORRETO - usar body raw antes do parsing
const signature = crypto.createHmac('sha256', secret)
  .update(req.rawBody)
  .digest('hex');
Este é um comportamento normal. Os webhooks podem ser enviados múltiplas vezes devido a:
  • Problemas de rede
  • Timeouts
  • Novas tentativas após falhas
Sempre implemente idempotência usando o validationId do webhook e o tipo de event.
Seu endpoint deve responder dentro de 30 segundos. Se o processamento levar mais tempo:
app.post('/webhooks', async (req, res) => {
  // Responder imediatamente
  res.status(200).json({ received: true });

  // Processar em segundo plano
  await queueWebhookProcessing(req.body);
});
extractedData e verifiedFields só são incluídos em:
  • kyc.validation_approved
  • kyc.validation_rejected
Não estão presentes em outros tipos de evento como validation_created ou validation_in_progress.
Se você perdeu seu segredo de webhook:
  1. Vá para ConfiguraçõesWebhooks
  2. Selecione seu webhook
  3. Clique em Regenerar Segredo
  4. Salve o novo segredo nas suas variáveis de ambiente
  5. Atualize sua aplicação com o novo segredo
Nota: O segredo antigo deixará de funcionar imediatamente após a regeneração.

Próximos Passos