Skip to main content

Resumen

Los webhooks te permiten recibir notificaciones en tiempo real when a KYC verification status changes. gu1 enviará automáticamente solicitudes HTTP POST a tu endpoint webhook configurado cuando se actualice el estado de una validación.

¿Por Qué Usar Webhooks?

Actualizaciones en Tiempo Real

Recibe notificaciones instantáneas cuando cambia el estado de verificación

Eficiente

No necesitas consultar la API repetidamente

Flujos Automáticos

Actualiza automáticamente cuentas de usuario según los resultados de verificación

Mejor UX

Notifica a los clientes inmediatamente después de la verificación

Eventos de Webhook

gu1 envía webhooks para los siguientes eventos de validación KYC:
Tipo de EventoDescripciónCuándo se Dispara
kyc.validation_createdSesión de validación creadaCuando creas una nueva validación KYC
kyc.validation_in_progressCliente inició verificaciónCliente comienza el proceso de verificación
kyc.validation_approvedVerificación aprobadaIdentidad verificada exitosamente
kyc.validation_rejectedVerificación rechazadaVerificación de identidad falló
kyc.validation_abandonedCliente abandonó el procesoCliente abandonó sin completar
kyc.validation_expiredSesión de validación expiróSesión expiró (típicamente después de 7 días)

Configurar Webhooks

Step 1: Configurar URL del Webhook

Configura la URL de tu webhook en el panel de gu1:
  1. Ve a SettingsWebhooks
  2. Agrega un nuevo endpoint webhook (e.g., https://yourapp.com/webhooks/kyc)
  3. Suscríbete a eventos KYC (kyc.*)
  4. Guarda y activa el webhook
Alternativamente, puedes especificar un webhookUrl al crear validaciones individuales.

Step 2: Crear un Endpoint Webhook

Crea un endpoint en tu aplicación para recibir solicitudes POST webhook:
app.post('/webhooks/kyc', async (req, res) => {
  try {
    const { type, data } = req.body;

    console.log('Received KYC webhook:', {
      type,
      validationId: data.validationId,
      status: data.status
    });

    // Process the webhook based on event type
    await handleKycWebhook(type, data);

    // Return 200 para confirmar recepción
    res.status(200).json({
      success: true,
      message: 'Webhook received'
    });
  } catch (error) {
    console.error('Webhook error:', error);
    // Still return 200 to prevent retries
    res.status(200).json({
      success: false,
      error: error.message
    });
  }
});

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

  // Update your database with validation ID from gu1
  await db.updateEntity(entity.externalId, {
    kycValidationId: validationId,
    kycStatus: status,
    lastUpdated: new Date()
  });

  // Perform actions based on event type
  switch (type) {
    case 'kyc.validation_created':
      console.log('KYC validation created for:', entity.name);
      break;

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

    case 'kyc.validation_approved':
      // Extract verified data
      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, 'verification-approved');
      break;

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

      await notifyCustomer(entity.externalId, 'verification-rejected');
      break;

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

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

Step 3: Hacer tu Endpoint Públicamente Accesible

Tu endpoint webhook debe ser:
  • Públicamente accesible vía HTTPS
  • Capaz de recibir solicitudes POST
  • Devolver un código de estado 200 para confirmar recepción
Para desarrollo local, usa herramientas como ngrok para crear una URL pública que se conecte a tu servidor local.

Estructura del Payload del Webhook

Todos los webhooks siguen esta estructura estándar:
{
  "id": "evt_abc123",
  "type": "kyc.validation_approved",
  "created": "2025-01-15T11:00:00Z",
  "data": {
    "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 Comunes del Payload

id
string
Identificador único para este evento webhook
type
string
El tipo de evento (e.g., kyc.validation_approved)
created
string
Marca de tiempo ISO 8601 cuando ocurrió el evento
data
object
Datos específicos del evento (ver abajo para cada tipo de evento)
data.validationId
string
El ID de validación KYC en gu1
data.entityId
string
El ID de la entidad (persona) siendo verificada
data.entity
object
Información de la entidad incluyendo tu externalId para búsqueda fácil
data.status
string
Estado de validación actual: pending, in_progress, approved, rejected, abandoned, expired

Payloads Específicos por Evento

kyc.validation_created

Enviado cuando se crea una nueva validación KYC.
{
  "type": "kyc.validation_created",
  "data": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "pending"
  }
}

kyc.validation_in_progress

Enviado cuando un cliente inicia el proceso de verificación.
{
  "type": "kyc.validation_in_progress",
  "data": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "in_progress"
  }
}

kyc.validation_approved

Enviado cuando la verificación se completa exitosamente.
{
  "type": "kyc.validation_approved",
  "data": {
    "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 Adicionales:
  • verifiedAt: Marca de tiempo cuando la verificación fue aprobada
  • extractedData: Información personal extraída del documento
  • verifiedFields: Arreglo de campos que fueron verificados exitosamente
  • warnings: Arreglo de advertencias detectadas durante la verificación
  • decision: Resultados de verificación (imágenes/URLs removidas por seguridad)

kyc.validation_rejected

Enviado cuando la verificación falla.
{
  "type": "kyc.validation_rejected",
  "data": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "rejected",
    "verifiedAt": "2025-01-15T11:00:00Z",
    "extractedData": {},
    "verifiedFields": [],
    "warnings": [
      "Document authenticity check failed",
      "Face match confidence low"
    ],
    "decision": {
      "id_verification": "fail",
      "face_match": "fail",
      "liveness": "pass"
    }
  }
}

kyc.validation_abandoned

Enviado cuando un cliente inicia pero no completa la verificación.
{
  "type": "kyc.validation_abandoned",
  "data": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "abandoned"
  }
}

kyc.validation_expired

Enviado cuando una sesión de validación expira sin completarse.
{
  "type": "kyc.validation_expired",
  "data": {
    "validationId": "550e8400-e29b-41d4-a716-446655440000",
    "entityId": "123e4567-e89b-12d3-a456-426614174000",
    "entity": { ... },
    "status": "expired",
    "expiresAt": "2025-01-22T10:30:00Z"
  }
}

Mejores Prácticas

Siempre devuelve un código 200 lo más rápido posible para confirmar recepción. Procesa el webhook asincrónicamente si es necesario.
app.post('/webhooks/kyc', async (req, res) => {
  // Acknowledge immediately
  res.status(200).send('OK');

  // Process asynchronously
  processWebhook(req.body).catch(console.error);
});
Podrías recibir el mismo webhook múltiples veces. Usa el id campo para asegurar que procesas cada evento solo una vez.
async function handleWebhook(webhook) {
  const alreadyProcessed = await db.checkWebhookProcessed(webhook.id);

  if (alreadyProcessed) {
    return; // Skip duplicate
  }

  // Process webhook
  await processValidation(webhook.data);

  // Mark as processed
  await db.markWebhookProcessed(webhook.id);
}
El webhook incluye entity.externalId que es el ID que proporcionaste al crear la entidad. Usa esto para buscar el cliente en tu base de datos.
const customer = await db.findCustomer({
  externalId: data.entity.externalId
});
Almacena el validationId de gu1 en tu base de datos. Esto te permite consultar detalles de validación después si es necesario.
await db.updateCustomer(customer.id, {
  kycValidationId: data.validationId,
  kycStatus: data.status
});
Si el procesamiento falla, registra el error pero aún devuelve 200 para prevenir reintentos. Almacena webhooks fallidos para revisión manual.
try {
  await processWebhook(payload);
} catch (error) {
  await db.saveFailedWebhook({
    payload,
    error: error.message,
    receivedAt: new Date()
  });

  // Still return 200
  res.status(200).json({ success: false });
}
Versiones futuras incluirán firmas de webhook. Por ahora, asegura que tu URL webhook sea privada y use HTTPS.

Probando Webhooks

Desarrollo Local

Usa ngrok para exponer tu servidor local:
# Iniciar ngrok
ngrok http 3000

# Usa el ngrok URL as your webhook URL
https://abc123.ngrok.io/webhooks/kyc

Flujo de Pruebas

  1. Crea una validación KYC de prueba
  2. Tu endpoint webhook recibe kyc.validation_created
  3. Cliente completa verificación
  4. Tu endpoint webhook recibe kyc.validation_approved or kyc.validation_rejected

Solución de Problemas

Verifica estos elementos:
  • URL webhook es públicamente accesible vía HTTPS
  • Firewall permite solicitudes POST entrantes
  • Endpoint devuelve código 200
  • Webhook está configurado y activo en el panel de gu1
  • Verifica logs del servidor por solicitudes entrantes
Esto es normal. Implementa verificaciones de idempotencia usando el id campo para manejar duplicados.
Procesa webhooks asincrónicamente. Devuelve 200 inmediatamente y maneja lógica de negocio en trabajos en segundo plano.
extractedData and verifiedFields solo se incluyen en kyc.validation_approved and kyc.validation_rejected eventos.

Próximos Pasos