> ## 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.

# Configuración de Webhook

> Implementa y configura endpoints de webhook

## Configuración Paso a Paso

<Steps>
  <Step title="Crear Endpoint">
    Construye endpoint POST para recibir webhooks

    ```javascript theme={null}
    app.post('/webhooks/killb', async (req, res) => {
      // Manejar webhook
      res.status(200).json({ received: true });
    });
    ```
  </Step>

  <Step title="Verificar Firmas">
    Implementa verificación HMAC

    ```javascript theme={null}
    const crypto = require('crypto');

    const verifySignature = (payload, signature, secret) => {
      const expected = crypto
        .createHmac('sha256', secret)
        .update(payload)
        .digest('hex');
      return signature === expected;
    };
    ```
  </Step>

  <Step title="Registrar Webhook">
    Configura en KillB

    ```bash theme={null}
    POST /api/v2/webhooks
    {
      "url": "https://api.tuapp.com/webhooks/killb",
      "secret": "tu-clave-secreta",
      "events": ["RAMP", "USER"]
    }
    ```
  </Step>

  <Step title="Probar">
    Crea ramp de prueba y verifica recepción de webhook
  </Step>
</Steps>

## Implementación Completa

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

  const app = express();

  // IMPORTANTE: Usa cuerpo crudo para verificación de firma
  app.use('/webhooks/killb', express.raw({type: 'application/json'}));

  app.post('/webhooks/killb', async (req, res) => {
    try {
      // 1. Verificar firma
      const signature = req.headers['x-signature-sha256'];
      const payload = req.body.toString();
      
      const expectedSignature = crypto
        .createHmac('sha256', process.env.WEBHOOK_SECRET)
        .update(payload)
        .digest('hex');
      
      if (signature !== expectedSignature) {
        console.error('Firma inválida');
        return res.status(401).json({ error: 'Firma inválida' });
      }
      
      // 2. Reconocer inmediatamente
      res.status(200).json({ received: true });
      
      // 3. Procesar asincrónicamente
      const event = JSON.parse(payload);
      processWebhookAsync(event).catch(console.error);
      
    } catch (error) {
      console.error('Error de webhook:', error);
      res.status(500).json({ error: 'Procesamiento falló' });
    }
  });

  app.listen(3000);
  ```

  ```python FastAPI theme={null}
  from fastapi import FastAPI, Header, HTTPException, Request, BackgroundTasks
  import hashlib
  import json
  from typing import Optional

  app = FastAPI()

  @app.post("/webhooks/killb")
  async def webhook_handler(
      request: Request,
      background_tasks: BackgroundTasks,
      x_webhook_signature: Optional[str] = Header(None)
  ):
      # Obtener cuerpo como JSON
      event = await request.json()
      
      # Verificar firma
      payload_string = f"{json.dumps(event, separators=(',', ':'))}_{WEBHOOK_SECRET}"
      expected_signature = hashlib.sha256(payload_string.encode()).hexdigest()
      
      if x_webhook_signature != expected_signature:
          raise HTTPException(status_code=401, detail="Firma inválida")
      
      # Reconocer inmediatamente
      background_tasks.add_task(process_webhook, event)
      
      return {"received": True}

  async def process_webhook(event):
      event_type = event.get('event')
      data = event.get('data')
      
      if event_type == 'ramp.completed':
          await handle_ramp_completed(data)
      elif event_type == 'ramp.failed':
          await handle_ramp_failed(data)
  ```

  ```typescript AdonisJS theme={null}
  import { HttpContext } from '@adonisjs/core/http'
  import { createHash } from 'node:crypto'
  import env from '#start/env'
  import emitter from '@adonisjs/core/services/emitter'

  export default class WebhooksController {
    async handleKillB({ request, response }: HttpContext) {
      try {
        // 1. Verificar firma
        const signature = request.header('x-signature-sha256')
        const body = request.body()
        
        const expectedSignature = createHash('sha256')
          .update(`${JSON.stringify(body)}_${env.get('WEBHOOK_SECRET')}`)
          .digest('hex')
        
        if (signature !== expectedSignature) {
          return response.unauthorized({ error: 'Firma inválida' })
        }
        
        // 2. Reconocer inmediatamente
        response.ok({ received: true })
        
        // 3. Procesar asincrónicamente usando event emitter
        emitter.emit('webhook:received', body)
        
      } catch (error) {
        console.error('Error de webhook:', error)
        return response.internalServerError({ error: 'Procesamiento falló' })
      }
    }
  }

  // app/listeners/webhook_listener.ts
  import emitter from '@adonisjs/core/services/emitter'

  emitter.on('webhook:received', async (event) => {
    console.log('Procesando evento:', event.event)
    
    switch(event.event) {
      case 'ramp.completed':
        await handleRampCompleted(event.data)
        break
      case 'ramp.failed':
        await handleRampFailed(event.data)
        break
      case 'user.kyc_approved':
        await handleKYCApproved(event.data)
        break
    }
  })

  // start/routes.ts
  import router from '@adonisjs/core/services/router'
  const WebhooksController = () => import('#controllers/webhooks_controller')

  router.post('/webhooks/killb', [WebhooksController, 'handleKillB'])
  ```
</CodeGroup>

## Registrar Webhook

```bash theme={null}
curl --request POST \
  --url https://sandbox.killb.app/api/v2/webhooks \
  --header 'Authorization: Bearer TU_TOKEN_DE_ACCESO' \
  --header 'Content-Type: application/json' \
  --data '{
    "url": "https://api.tuapp.com/webhooks/killb",
    "secret": "tu-clave-secreta-al-menos-32-caracteres",
    "events": ["RAMP", "USER", "ACCOUNT"]
  }'
```

## Probando Localmente

Usa ngrok para pruebas locales:

```bash theme={null}
# Iniciar servidor local
npm start

# Exponer vía ngrok
ngrok http 3000

# Usar URL ngrok
# https://abc123.ngrok.io/webhooks/killb
```

## Próximos Pasos

<CardGroup cols={2}>
  <Card title="Referencia de Eventos" icon="list" href="/es/guides/webhooks/events">
    Todos los tipos de evento de webhook
  </Card>

  <Card title="Seguridad" icon="shield" href="/es/guides/webhooks/security">
    Asegura tus webhooks
  </Card>
</CardGroup>
