Saltar al contenido principal

Estructura del evento

Todos los eventos de webhook siguen esta estructura:
{
  id: string,              // UUID - ID único del evento
  event: string,           // Tipo de evento: RAMP | USER | TRANSACTION | ACCOUNT | CUSTODIAL_ACCOUNT
  action: string,          // Acción: CREATE | UPDATE | DELETE
  data: object,            // Datos específicos del evento
  createdAt: string,       // Timestamp ISO 8601
  updatedAt: string,       // Timestamp ISO 8601
  attempts: number         // Conteo de intentos de entrega (0 a 5)
}
Usa el campo id para idempotencia. El campo attempts indica el conteo de reintentos.

Tipos de evento

Eventos RAMP

Tipo: RAMP Acciones:
  • CREATE - Ramp creado
  • UPDATE - Estado del ramp cambió
  • DELETE - Ramp cancelado (raro)
Ejemplo - Ramp creado:
{
  "id": "evt_a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "event": "RAMP",
  "action": "CREATE",
  "data": {
    "id": "be4d353b-00a2-4309-9ef1-594f37dfb1fd",
    "userId": "2bc703f2-1c54-4cbb-a144-993e1d957688",
    "accountId": "f7df00af-5d12-4b33-b5ab-e2b32290ebad",
    "quotationId": "a656a475-0d53-479e-8f30-91582ecf450b",
    "type": "ON",
    "status": "CREATED",
    "cashInMethod": "PSE",
    "cashOutMethod": "POLYGON",
    "fromCurrency": "COP",
    "toCurrency": "USDC",
    "fromAmount": 100000,
    "toAmount": 23.81,
    "isPreFunded": false,
    "active": true,
    "createdAt": "2025-01-15T23:46:10.226Z",
    "updatedAt": "2025-01-15T23:46:10.226Z"
  },
  "createdAt": "2025-01-15T23:46:10.226Z",
  "updatedAt": "2025-01-15T23:46:10.226Z",
  "attempts": 0
}
Ejemplo - Ramp completado:
{
  "id": "evt_b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
  "event": "RAMP",
  "action": "UPDATE",
  "data": {
    "id": "be4d353b-00a2-4309-9ef1-594f37dfb1fd",
    "userId": "2bc703f2-1c54-4cbb-a144-993e1d957688",
    "accountId": "f7df00af-5d12-4b33-b5ab-e2b32290ebad",
    "type": "ON",
    "status": "COMPLETED",
    "fromCurrency": "COP",
    "toCurrency": "USDC",
    "fromAmount": 100000,
    "toAmount": 23.81,
    "transferProof": "0x8e2b1f4645ebc8b6f658c860980567a63d283a42735be7401a7950306a2539b0",
    "createdAt": "2025-01-15T23:46:10.226Z",
    "updatedAt": "2025-01-16T00:29:06.813Z"
  },
  "createdAt": "2025-01-16T00:29:06.813Z",
  "updatedAt": "2025-01-16T00:29:06.813Z",
  "attempts": 0
}
Cómo manejar eventos RAMP:
if (webhookEvent.event === 'RAMP' && webhookEvent.action === 'UPDATE') {
  const { status, transferProof } = webhookEvent.data;
  
  switch(status) {
    case 'CASH_IN_COMPLETED':
      console.log('Payment received');
      break;
    case 'COMPLETED':
      console.log('Ramp completed:', transferProof);
      await notifyUser(webhookEvent.data.userId);
      break;
    case 'FAILED':
      console.log('Ramp failed');
      await handleFailure(webhookEvent.data);
      break;
  }
}
Estados comunes:
  • CREATED - Ramp inicializado
  • CASH_IN_PROCESSING - Pago siendo procesado
  • CASH_IN_COMPLETED - Pago confirmado
  • CONVERSION_PROCESSING - Convirtiendo moneda
  • CONVERSION_COMPLETED - Conversión completada
  • CASH_OUT_PROCESSING - Enviando fondos
  • COMPLETED - Totalmente completado
  • FAILED - Falló
  • CANCELED - Cancelado

Eventos USER

Tipo: USER Acciones:
  • CREATE - Usuario registrado
  • UPDATE - Información o estado del usuario cambió
  • DELETE - Usuario eliminado (raro)
Ejemplo - KYC aprobado:
{
  "id": "evt_f1e2d3c4-b5a6-4978-8c9d-0e1f2a3b4c5d",
  "event": "USER",
  "action": "UPDATE",
  "data": {
    "id": "e3d5c4ca-839a-4067-af76-89b33b19696e",
    "type": "PERSON",
    "customerId": "c650925e-a4aa-4a5f-a0a9-6c97be6c6ce5",
    "externalId": "user-ext-12345",
    "status": "ACTIVE",
    "accessLevel": "L2",
    "data": {
      "firstName": "Carlos",
      "lastName": "Rodriguez",
      "email": "[email protected]",
      "phone": "+573001234567"
    }
  },
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T14:22:00.000Z",
  "attempts": 0
}
Cómo manejar eventos USER:
if (webhookEvent.event === 'USER' && webhookEvent.action === 'UPDATE') {
  const { status, accessLevel } = webhookEvent.data;
  
  if (status === 'ACTIVE' && accessLevel !== 'L0') {
    // KYC aprobado - habilitar funcionalidades
    await enableUserFeatures(webhookEvent.data.id, accessLevel);
  }
  
  if (status === 'REJECTED') {
    // KYC rechazado - notificar al usuario
    await sendKYCRejectionEmail(webhookEvent.data);
  }
}
Estados del usuario:
  • PENDING - Esperando verificación
  • ACTIVE - Verificado y activo
  • REJECTED - KYC rechazado
  • SUSPENDED - Cuenta suspendida
Niveles de acceso:
  • L0 - No verificado
  • L1 - Básico ($1K diario)
  • L2 - Estándar ($10K diario)
  • L3 - Mejorado ($50K diario)
  • L4 - Premium (Ilimitado)

Eventos ACCOUNT

Tipo: ACCOUNT Acciones:
  • CREATE - Cuenta agregada
  • UPDATE - Estado de la cuenta cambió
  • DELETE - Cuenta eliminada
Ejemplo - Cuenta verificada:
{
  "id": "evt_c9b8a7f6-d5e4-4321-9876-543210fedcba",
  "event": "ACCOUNT",
  "action": "UPDATE",
  "data": {
    "id": "543ab81d-0b1e-4b9d-88bc-58ba5a365f16",
    "type": "PSE",
    "status": "ACTIVE",
    "externalId": "bank-acc-7890",
    "data": {
      "firstName": "María",
      "lastName": "González",
      "email": "[email protected]",
      "countryCode": "CO"
    },
    "complianceUrl": "https://in.sumsub.com/websdk/p/AbCd1234EfGh5678"
  },
  "createdAt": "2025-01-15T09:15:00.000Z",
  "updatedAt": "2025-01-15T09:45:00.000Z",
  "attempts": 0
}
Cómo manejar eventos ACCOUNT:
if (webhookEvent.event === 'ACCOUNT' && webhookEvent.action === 'UPDATE') {
  const { status, type } = webhookEvent.data;
  
  if (status === 'ACTIVE') {
    console.log(`${type} account verified and ready`);
    await notifyAccountReady(webhookEvent.data.id);
  }
  
  if (status === 'REJECTED') {
    console.log('Account verification failed');
    await requestNewAccountInfo(webhookEvent.data.id);
  }
}
Tipos de cuenta:
  • PSE - Banco colombiano
  • SPEI - Banco mexicano
  • ACH - Banco en EE. UU.
  • WIRE - Transferencia bancaria
  • WALLET - Wallet cripto
  • PIX - PIX de Brasil
  • TRANSFIYA - Wallet móvil en Colombia
Estados de cuenta:
  • PENDING - Esperando verificación
  • ACTIVE - Verificada
  • REJECTED - Verificación fallida
  • INACTIVE - Deshabilitada

Eventos TRANSACTION

Tipo: TRANSACTION Acciones:
  • CREATE - Transacción iniciada
  • UPDATE - Estado de la transacción cambió
  • DELETE - Transacción cancelada (raro)
Ejemplo - Depósito en ahorros:
{
  "id": "evt_1a2b3c4d-5e6f-7890-abcd-ef1234567890",
  "event": "TRANSACTION",
  "action": "UPDATE",
  "data": {
    "id": "txn_9876543210abcdef",
    "userId": "e3d5c4ca-839a-4067-af76-89b33b19696e",
    "savingsAccountId": "sav_1234567890abcdef",
    "type": "DEPOSIT",
    "status": "COMPLETED",
    "amount": "1000.00",
    "currency": "USD",
    "method": "ACH"
  },
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:35:00.000Z",
  "attempts": 0
}
Tipos de transacción:
  • DEPOSIT - Depósito en ahorros
  • WITHDRAWAL - Retiro desde ahorros
  • INTEREST - Pago de intereses
  • FEE - Cobro de comisión

Eventos CUSTODIAL_ACCOUNT

Tipo: CUSTODIAL_ACCOUNT Acciones:
  • CREATE - Cuenta creada
  • UPDATE - Balance o estado actualizado
  • DELETE - Cuenta cerrada (raro)
Ejemplo - Balance actualizado:
{
  "id": "evt_abcd1234-ef56-7890-1234-567890abcdef",
  "event": "CUSTODIAL_ACCOUNT",
  "action": "UPDATE",
  "data": {
    "id": "cust_1234567890abcdef",
    "userId": "e3d5c4ca-839a-4067-af76-89b33b19696e",
    "type": "SAVINGS",
    "status": "ACTIVE",
    "balance": "5250.00",
    "currency": "USD",
    "previousBalance": "5000.00",
    "changeAmount": "250.00",
    "changeReason": "DEPOSIT",
    "interestRate": "4.50"
  },
  "createdAt": "2025-01-15T10:35:00.000Z",
  "updatedAt": "2025-01-15T10:35:00.000Z",
  "attempts": 0
}
Motivos de cambio:
  • DEPOSIT - Fondos depositados
  • WITHDRAWAL - Fondos retirados
  • INTEREST - Interés acreditado
  • FEE - Comisión cobrada
  • ADJUSTMENT - Ajuste manual

Mejores prácticas para el manejo de eventos

Filtra por evento y acción

const handleWebhook = (webhookEvent) => {
  const { event, action, data } = webhookEvent;
  
  // Enrutar por tipo de evento
  if (event === 'RAMP') {
    if (action === 'CREATE') {
      handleRampCreated(data);
    } else if (action === 'UPDATE') {
      handleRampUpdated(data);
    }
  } else if (event === 'USER') {
    if (action === 'UPDATE') {
      handleUserUpdated(data);
    }
  }
  // ... otros tipos de eventos
};

Valida el estado específico

const handleRampUpdated = (data) => {
  switch(data.status) {
    case 'CASH_IN_COMPLETED':
      console.log('Payment received');
      break;
    case 'COMPLETED':
      console.log('Ramp completed:', data.transferProof);
      updateDatabase(data);
      notifyUser(data.userId);
      break;
    case 'FAILED':
      console.log('Ramp failed:', data.details);
      handleFailure(data);
      break;
  }
};

Usa el ID del evento para idempotencia

const processWebhook = async (webhookEvent) => {
  // Verificar si ya fue procesado
  const existing = await db.webhookEvents.findOne({ 
    eventId: webhookEvent.id 
  });
  
  if (existing) {
    console.log('Duplicate event, skipping');
    return { received: true, duplicate: true };
  }
  
  // Guardar y procesar
  await db.webhookEvents.create({
    eventId: webhookEvent.id,
    eventType: webhookEvent.event,
    action: webhookEvent.action,
    payload: webhookEvent.data,
    attempts: webhookEvent.attempts,
    receivedAt: new Date()
  });
  
  await handleWebhook(webhookEvent);
  
  return { received: true };
};

Monitorea los reintentos

if (webhookEvent.attempts > 3) {
  console.warn(`High retry count for event ${webhookEvent.id}`);
  // Alertar al sistema de monitoreo
  alertOps('webhook-retries-high', {
    eventId: webhookEvent.id,
    attempts: webhookEvent.attempts,
    event: webhookEvent.event
  });
}

Referencia rápida

Tipo de eventoAcciones comunesCampos clave en data
RAMPCREATE, UPDATEstatus, transferProof, fromAmount, toAmount
USERCREATE, UPDATEstatus, accessLevel, data
ACCOUNTCREATE, UPDATE, DELETEstatus, type, data
TRANSACTIONCREATE, UPDATEtype, status, amount
CUSTODIAL_ACCOUNTUPDATEbalance, changeReason, changeAmount

Próximos pasos

Configuración de Webhook

Configura endpoints de webhook

Guía de Seguridad

Verifica firmas de webhook

Concepto de Webhook

Aprende sobre la arquitectura de webhooks

Referencia API

Documentación de la API de Webhooks