> ## Documentation Index
> Fetch the complete documentation index at: https://docs.killb.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Notificaciones de eventos en tiempo real de KillB

## ¿Qué son los Webhooks?

**Webhooks** son callbacks HTTP que KillB envía a tu servidor cuando ocurren eventos. Habilitan notificaciones en tiempo real sobre ramps, usuarios, cuentas y transacciones sin polling constante.

<Info>
  Los webhooks son la forma recomendada de monitorear el estado de transacciones y mantener tu aplicación sincronizada con KillB.
</Info>

## Cómo Funcionan los Webhooks

```mermaid theme={null}
sequenceDiagram
    participant KillB
    participant Tu Servidor
    participant Tu App
    
    KillB->>KillB: Estado del ramp cambia
    KillB->>Tu Servidor: POST evento webhook
    Tu Servidor->>Tu Servidor: Verificar firma
    Tu Servidor->>Tu Servidor: Procesar evento
    Tu Servidor-->>KillB: 200 OK
    Tu Servidor->>Tu App: Actualizar UI
```

## Estructura del Evento Webhook

Todos los eventos webhook siguen esta estructura:

```typescript theme={null}
{
  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)
}
```

## Eventos de Webhook

KillB soporta los siguientes tipos de eventos:

<Tabs>
  <Tab title="RAMP">
    **Eventos de transacción ramp**

    **Tipo de Evento:** `RAMP`

    **Acciones:**

    * `CREATE` - Nuevo ramp creado
    * `UPDATE` - Estado del ramp cambió
    * `DELETE` - Ramp cancelado (raro)

    **Valores de Estado Comunes:**

    * `QUOTE_CREATED` - Cotización creada
    * `PENDING_PAYMENT` - Esperando pago
    * `CASH_IN_PROCESSING` - Pago siendo procesado
    * `CASH_IN_COMPLETED` - Pago confirmado
    * `PROCESSING` - Convirtiendo moneda
    * `CASH_OUT_PROCESSING` - Enviando fondos
    * `COMPLETED` - Ramp completado
    * `FAILED` - Ramp falló
    * `CANCELED` - Ramp cancelado
  </Tab>

  <Tab title="USER">
    **Eventos de usuario y KYC**

    **Tipo de Evento:** `USER`

    **Acciones:**

    * `CREATE` - Nuevo usuario registrado
    * `UPDATE` - Información del usuario o estado KYC cambió
    * `DELETE` - Usuario eliminado (raro)

    **Valores de Estado:**

    * `PENDING` - Esperando verificación
    * `ACTIVE` - Verificado y activo
    * `REJECTED` - KYC rechazado
    * `SUSPENDED` - Cuenta suspendida

    **Niveles de Acceso:**

    * `L0` - No verificado
    * `L1` - Verificación básica
    * `L2` - Verificación estándar
    * `L3` - Verificación mejorada
    * `L4` - Verificación premium
  </Tab>

  <Tab title="ACCOUNT">
    **Eventos de verificación de cuenta**

    **Tipo de Evento:** `ACCOUNT`

    **Acciones:**

    * `CREATE` - Nueva cuenta agregada
    * `UPDATE` - Estado o detalles de cuenta cambiaron
    * `DELETE` - Cuenta eliminada

    **Tipos de Cuenta:**

    * `PSE` - Cuenta bancaria colombiana
    * `SPEI` - Cuenta bancaria mexicana
    * `ACH` - Cuenta bancaria de EE.UU.
    * `WIRE` - Cuenta de transferencia wire
    * `WALLET` - Billetera cripto
    * `PIX` - Cuenta PIX brasileña
    * `TRANSFIYA` - Billetera móvil colombiana

    **Valores de Estado:**

    * `PENDING` - Esperando verificación
    * `ACTIVE` - Verificado y activo
    * `REJECTED` - Verificación falló
    * `INACTIVE` - Deshabilitado
  </Tab>
</Tabs>

## Configurando Webhooks

### Crear Configuración de Webhook

```bash theme={null}
POST /api/v2/webhooks
```

**Solicitud:**

```json theme={null}
{
  "url": "https://api.tuapp.com/webhooks/killb",
  "secret": "tu-clave-secreta-min-32-caracteres",
  "events": ["RAMP", "USER", "ACCOUNT"]
}
```

**Respuesta:**

```json theme={null}
{
  "id": "webhook-id",
  "customerId": "customer-id",
  "url": "https://api.tuapp.com/webhooks/killb",
  "events": ["RAMP", "USER", "ACCOUNT"],
  "active": true,
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

<CodeGroup>
  ```javascript Node.js theme={null}
  const setupWebhook = async () => {
    const response = await fetch('https://sandbox.killb.app/api/v2/webhooks', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        url: 'https://api.tuapp.com/webhooks/killb',
        secret: process.env.WEBHOOK_SECRET,
        events: ['RAMP', 'USER', 'ACCOUNT', 'TRANSACTION']
      })
    });
    
    return await response.json();
  };
  ```

  ```python Python theme={null}
  def setup_webhook(api_key, access_token, webhook_url, secret):
      response = requests.post(
          'https://sandbox.killb.app/api/v2/webhooks',
          headers={
              'Authorization': f'Bearer {access_token}',
              'Content-Type': 'application/json'
          },
          json={
              'url': webhook_url,
              'secret': secret,
              'events': ['RAMP', 'USER', 'ACCOUNT', 'TRANSACTION']
          }
      )
      return response.json()
  ```
</CodeGroup>

## Seguridad de Webhook

### Verificación de Firma

KillB firma todas las solicitudes webhook con HMAC SHA-256. **Siempre verifica firmas** para asegurar autenticidad.

**Encabezado:** `x-signature-sha256`

<CodeGroup>
  ```javascript Node.js (Express) theme={null}
  const crypto = require('crypto');
  const express = require('express');

  app.post('/webhooks/killb', express.raw({type: 'application/json'}), (req, res) => {
    const signature = req.headers['x-signature-sha256'];
    const payload = req.body.toString();
    
    // Calcular firma esperada
    const expectedSignature = crypto
      .createHmac('sha256', process.env.WEBHOOK_SECRET)
      .update(payload)
      .digest('hex');
    
    // Verificar firma
    if (signature !== expectedSignature) {
      console.error('Firma de webhook inválida');
      return res.status(401).json({ error: 'Firma inválida' });
    }
    
    // Analizar y procesar evento
    const event = JSON.parse(payload);
    processWebhookEvent(event);
    
    res.status(200).json({ received: true });
  });
  ```

  ```python FastAPI theme={null}
  import hmac
  import hashlib
  from fastapi import FastAPI, Header, HTTPException, Request

  app = FastAPI()

  @app.post("/webhooks/killb")
  async def webhook_handler(
      request: Request,
      x_webhook_signature: str = Header(None)
  ):
      # Obtener cuerpo crudo
      body = await request.body()
      
      # Calcular firma esperada
      expected_signature = hmac.new(
          WEBHOOK_SECRET.encode(),
          body,
          hashlib.sha256
      ).hexdigest()
      
      # Verificar firma
      if not hmac.compare_digest(x_webhook_signature or '', expected_signature):
          raise HTTPException(status_code=401, detail="Firma inválida")
      
      # Analizar y procesar
      event = await request.json()
      process_webhook_event(event)
      
      return {"received": True}
  ```
</CodeGroup>

<Warning>
  **¡Nunca omitas la verificación de firma!** Webhooks no verificados pueden ser falsificados por atacantes.
</Warning>

## Requisitos de Webhook

Tu endpoint de webhook debe:

<AccordionGroup>
  <Accordion title="Responder Rápidamente" icon="bolt">
    * Retornar `200 OK` dentro de 5 segundos
    * Procesar eventos asincrónicamente
    * No esperar por operaciones lentas
    * Usar colas de jobs para procesamiento pesado
  </Accordion>

  <Accordion title="Manejar Reintentos" icon="rotate">
    * Ser idempotente (manejar eventos duplicados)
    * Usar ID del evento para detectar duplicados
    * Almacenar IDs de eventos procesados
    * Saltar eventos ya procesados
  </Accordion>

  <Accordion title="Estar Disponible" icon="signal">
    * Mantener >99% de uptime
    * Usar solo HTTPS
    * Tener certificado SSL válido
    * Responder con códigos de estado apropiados
  </Accordion>

  <Accordion title="Verificar Firmas" icon="shield">
    * Siempre verificar `x-signature-sha256`
    * Usar comparación de tiempo constante
    * Rechazar firmas inválidas
    * Registrar fallas de verificación
  </Accordion>
</AccordionGroup>

## Lógica de Reintento

KillB automáticamente reintenta entregas de webhook fallidas con backoff exponencial:

**Agenda de Reintento:**

* Intento 0: Entrega inmediata
* Intento 1: Después de 1 minuto
* Intento 2: Después de 5 minutos
* Intento 3: Después de 15 minutos
* Intento 4: Después de 1 hora
* Intento 5: Después de 6 horas (intento final)

**Criterios de Falla:**

* Código de respuesta no-2xx
* Timeout de conexión (> 5 segundos)
* Error de red

El campo `attempts` en el payload del webhook indica el intento de entrega actual (0 a 5).

<Tip>
  Después de que el intento 5 falla, el webhook se marca como permanentemente fallido y requiere intervención manual. Verifica tus logs de webhook y corrige cualquier problema.
</Tip>

## Probando Webhooks

### Prueba Local con ngrok

```bash theme={null}
# Instalar ngrok
npm install -g ngrok

# Iniciar tu servidor local
node server.js

# Exponer a internet
ngrok http 3000

# Usar URL ngrok en configuración de webhook
# https://abc123.ngrok.io/webhooks/killb
```

### Prueba Manual

Activa eventos de prueba en sandbox:

```javascript theme={null}
// Crear ramp de prueba
const ramp = await createRamp(quoteId, userId, accountId);

// Simular finalización (activa webhooks)
await fetch('/api/v2/faker/cash-in', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}` },
  body: JSON.stringify({ rampId: ramp.id })
});

// Tu webhook debe recibir evento ramp.cash_in_completed
```

## Procesamiento de Eventos

### Ejemplo de Handler

```javascript theme={null}
const processWebhookEvent = (webhookEvent) => {
  const { id, event, action, data, attempts } = webhookEvent;
  
  console.log(`Procesando ${event}.${action} (intento ${attempts})`);
  
  // Enrutar basado en tipo de evento y acción
  switch(event) {
    case 'RAMP':
      handleRampEvent(action, data);
      break;
      
    case 'USER':
      handleUserEvent(action, data);
      break;
      
    case 'ACCOUNT':
      handleAccountEvent(action, data);
      break;
      
    case 'TRANSACTION':
      handleTransactionEvent(action, data);
      break;
      
    default:
      console.log(`Tipo de evento no manejado: ${event}`);
  }
};

const handleRampEvent = async (action, data) => {
  if (action === 'UPDATE' && data.status === 'COMPLETED') {
    // Actualizar base de datos
    await db.ramps.update(data.id, {
      status: data.status,
      updatedAt: data.updatedAt,
      transferProof: data.transferProof
    });
    
    // Notificar usuario
    await sendEmail(data.userId, 'Transacción Completada', {
      amount: data.toAmount,
      currency: data.toCurrency,
      txHash: data.transferProof
    });
  }
};
```

## Idempotencia

Maneja entregas de webhook duplicadas usando el ID del evento:

```javascript theme={null}
const handleWebhook = async (webhookEvent) => {
  const { id, event, action, data } = webhookEvent;
  
  // Verificar si ya procesado (usando ID único del evento)
  const existing = await db.webhookEvents.findOne({ eventId: id });
  
  if (existing) {
    console.log(`Evento duplicado ${id}, saltando`);
    return { received: true, duplicate: true };
  }
  
  // Almacenar evento inmediatamente para prevenir procesamiento duplicado
  await db.webhookEvents.create({
    eventId: id,
    eventType: event,
    action: action,
    payload: data,
    receivedAt: new Date(),
    processed: false
  });
  
  try {
    // Procesar evento
    await processWebhookEvent(webhookEvent);
    
    // Marcar como procesado
    await db.webhookEvents.update(
      { eventId: id },
      { processed: true, processedAt: new Date() }
    );
    
    return { received: true };
  } catch (error) {
    // Registrar error pero aún marcar como recibido
    await db.webhookEvents.update(
      { eventId: id },
      { error: error.message, failedAt: new Date() }
    );
    
    throw error;
  }
};
```

## Mejores Prácticas

<AccordionGroup>
  <Accordion title="Reconocer Rápidamente" icon="bolt">
    ```javascript theme={null}
    app.post('/webhooks/killb', async (req, res) => {
      // Verificar firma
      verifySignature(req);
      
      // Reconocer inmediatamente
      res.status(200).json({ received: true });
      
      // Procesar asincrónicamente
      processAsync(req.body);
    });
    ```

    * Retornar 200 OK inmediatamente
    * Procesar eventos en segundo plano
    * Usar colas de jobs (Bull, Celery, etc.)
    * No esperar por servicios externos
  </Accordion>

  <Accordion title="Manejar Fallas Graciosamente" icon="shield">
    ```javascript theme={null}
    const processWebhook = async (event) => {
      try {
        await processEvent(event);
      } catch (error) {
        console.error('Procesamiento de webhook falló:', error);
        // Registrar en servicio de monitoreo
        await logError(error, event);
        // No lanzar - ya procesamos
        // KillB reintentará de todos modos
      }
    };
    ```

    * Capturar todos los errores
    * Registrar fallas
    * No lanzar después de reconocer
    * Monitorear tasas de error
  </Accordion>

  <Accordion title="Almacenar Historial de Eventos" icon="database">
    ```javascript theme={null}
    await db.webhookLogs.create({
      eventId: event.id,
      eventType: event.event,
      payload: event.data,
      receivedAt: new Date(),
      processed: true
    });
    ```

    * Mantener pista de auditoría
    * Habilitar replay si es necesario
    * Depurar problemas fácilmente
    * Cumplir requisitos de compliance
  </Accordion>
</AccordionGroup>

## Próximos Pasos

<CardGroup cols={2}>
  <Card title="Guía de Configuración de Webhook" icon="wrench" href="/es/guides/webhooks/setup">
    Implementación de webhook paso a paso
  </Card>

  <Card title="Referencia de Eventos" icon="list" href="/es/guides/webhooks/events">
    Lista completa de eventos webhook
  </Card>

  <Card title="Guía de Seguridad" icon="lock" href="/es/guides/webhooks/security">
    Asegura tus endpoints de webhook
  </Card>

  <Card title="Referencia API" icon="code" href="/api-reference/endpoint/webhooks-create">
    Ve la documentación de la API de Webhooks
  </Card>
</CardGroup>
