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

> Notificações de eventos em tempo real da KillB

## O que são Webhooks?

**Webhooks** são callbacks HTTP que a KillB envia para seu servidor quando eventos ocorrem. Eles habilitam notificações em tempo real sobre ramps, usuários, contas e transações sem polling constante.

<Info>
  Webhooks são a forma recomendada de monitorar status de transações e manter sua aplicação sincronizada com a KillB.
</Info>

## Como os Webhooks Funcionam

```mermaid theme={null}
sequenceDiagram
    participant KillB
    participant Seu Servidor
    participant Seu App
    
    KillB->>KillB: Status do ramp muda
    KillB->>Seu Servidor: POST evento webhook
    Seu Servidor->>Seu Servidor: Verificar assinatura
    Seu Servidor->>Seu Servidor: Processar evento
    Seu Servidor-->>KillB: 200 OK
    Seu Servidor->>Seu App: Atualizar UI
```

## Estrutura do Evento Webhook

Todos os eventos webhook seguem esta estrutura:

```typescript theme={null}
{
  id: string,              // UUID - ID único do evento
  event: string,           // Tipo de evento: RAMP | USER | TRANSACTION | ACCOUNT | CUSTODIAL_ACCOUNT
  action: string,          // Ação: CREATE | UPDATE | DELETE
  data: object,            // Dados específicos do evento
  createdAt: string,       // Timestamp ISO 8601
  updatedAt: string,       // Timestamp ISO 8601
  attempts: number         // Contagem de tentativas de entrega (0 a 5)
}
```

## Eventos de Webhook

A KillB suporta os seguintes tipos de eventos:

<Tabs>
  <Tab title="RAMP">
    **Eventos de transação ramp**

    **Tipo de Evento:** `RAMP`

    **Ações:**

    * `CREATE` - Novo ramp criado
    * `UPDATE` - Status do ramp mudou
    * `DELETE` - Ramp cancelado (raro)

    **Valores de Status Comuns:**

    * `QUOTE_CREATED` - Cotação criada
    * `PENDING_PAYMENT` - Aguardando pagamento
    * `CASH_IN_PROCESSING` - Pagamento sendo processado
    * `CASH_IN_COMPLETED` - Pagamento confirmado
    * `PROCESSING` - Convertendo moeda
    * `CASH_OUT_PROCESSING` - Enviando fundos
    * `COMPLETED` - Ramp concluído
    * `FAILED` - Ramp falhou
    * `CANCELED` - Ramp cancelado
  </Tab>

  <Tab title="USER">
    **Eventos de usuário e KYC**

    **Tipo de Evento:** `USER`

    **Ações:**

    * `CREATE` - Novo usuário registrado
    * `UPDATE` - Informações do usuário ou status KYC mudou
    * `DELETE` - Usuário excluído (raro)

    **Valores de Status:**

    * `PENDING` - Aguardando verificação
    * `ACTIVE` - Verificado e ativo
    * `REJECTED` - KYC rejeitado
    * `SUSPENDED` - Conta suspensa

    **Níveis de Acesso:**

    * `L0` - Não verificado
    * `L1` - Verificação básica
    * `L2` - Verificação padrão
    * `L3` - Verificação aprimorada
    * `L4` - Verificação premium
  </Tab>

  <Tab title="ACCOUNT">
    **Eventos de verificação de conta**

    **Tipo de Evento:** `ACCOUNT`

    **Ações:**

    * `CREATE` - Nova conta adicionada
    * `UPDATE` - Status ou detalhes da conta mudaram
    * `DELETE` - Conta excluída

    **Tipos de Conta:**

    * `PSE` - Conta bancária colombiana
    * `SPEI` - Conta bancária mexicana
    * `ACH` - Conta bancária dos EUA
    * `WIRE` - Conta de transferência wire
    * `WALLET` - Carteira cripto
    * `PIX` - Conta PIX brasileira
    * `TRANSFIYA` - Carteira móvel colombiana

    **Valores de Status:**

    * `PENDING` - Aguardando verificação
    * `ACTIVE` - Verificado e ativo
    * `REJECTED` - Verificação falhou
    * `INACTIVE` - Desabilitado
  </Tab>
</Tabs>

## Configurando Webhooks

### Criar Configuração de Webhook

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

**Requisição:**

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

**Resposta:**

```json theme={null}
{
  "id": "webhook-id",
  "customerId": "customer-id",
  "url": "https://api.seuapp.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.seuapp.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>

## Segurança de Webhook

### Verificação de Assinatura

A KillB assina todas as requisições webhook com HMAC SHA-256. **Sempre verifique assinaturas** para garantir autenticidade.

**Cabeçalho:** `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 assinatura esperada
    const expectedSignature = crypto
      .createHmac('sha256', process.env.WEBHOOK_SECRET)
      .update(payload)
      .digest('hex');
    
    // Verificar assinatura
    if (signature !== expectedSignature) {
      console.error('Assinatura de webhook inválida');
      return res.status(401).json({ error: 'Assinatura inválida' });
    }
    
    // Analisar e processar 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)
  ):
      # Obter corpo bruto
      body = await request.body()
      
      # Calcular assinatura esperada
      expected_signature = hmac.new(
          WEBHOOK_SECRET.encode(),
          body,
          hashlib.sha256
      ).hexdigest()
      
      # Verificar assinatura
      if not hmac.compare_digest(x_webhook_signature or '', expected_signature):
          raise HTTPException(status_code=401, detail="Assinatura inválida")
      
      # Analisar e processar
      event = await request.json()
      process_webhook_event(event)
      
      return {"received": True}
  ```
</CodeGroup>

<Warning>
  **Nunca pule a verificação de assinatura!** Webhooks não verificados podem ser falsificados por atacantes.
</Warning>

## Requisitos de Webhook

Seu endpoint de webhook deve:

<AccordionGroup>
  <Accordion title="Responder Rapidamente" icon="bolt">
    * Retornar `200 OK` dentro de 5 segundos
    * Processar eventos assincronamente
    * Não esperar por operações lentas
    * Usar filas de jobs para processamento pesado
  </Accordion>

  <Accordion title="Lidar com Retries" icon="rotate">
    * Ser idempotente (lidar com eventos duplicados)
    * Usar ID do evento para detectar duplicatas
    * Armazenar IDs de eventos processados
    * Pular eventos já processados
  </Accordion>

  <Accordion title="Estar Disponível" icon="signal">
    * Manter >99% de uptime
    * Usar apenas HTTPS
    * Ter certificado SSL válido
    * Responder com códigos de status adequados
  </Accordion>

  <Accordion title="Verificar Assinaturas" icon="shield">
    * Sempre verificar `x-signature-sha256`
    * Usar comparação de tempo constante
    * Rejeitar assinaturas inválidas
    * Registrar falhas de verificação
  </Accordion>
</AccordionGroup>

## Lógica de Retry

A KillB automaticamente tenta novamente entregas de webhook falhadas com backoff exponencial:

**Agenda de Retry:**

* Tentativa 0: Entrega imediata
* Tentativa 1: Após 1 minuto
* Tentativa 2: Após 5 minutos
* Tentativa 3: Após 15 minutos
* Tentativa 4: Após 1 hora
* Tentativa 5: Após 6 horas (tentativa final)

**Critérios de Falha:**

* Código de resposta não-2xx
* Timeout de conexão (> 5 segundos)
* Erro de rede

O campo `attempts` no payload do webhook indica a tentativa de entrega atual (0 a 5).

<Tip>
  Após a tentativa 5 falhar, o webhook é marcado como permanentemente falhado e requer intervenção manual. Verifique seus logs de webhook e corrija quaisquer problemas.
</Tip>

## Testando Webhooks

### Teste Local com ngrok

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

# Iniciar seu servidor local
node server.js

# Expor para internet
ngrok http 3000

# Usar URL ngrok na configuração de webhook
# https://abc123.ngrok.io/webhooks/killb
```

### Teste Manual

Acione eventos de teste no sandbox:

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

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

// Seu webhook deve receber evento ramp.cash_in_completed
```

## Processamento de Eventos

### Exemplo de Handler

```javascript theme={null}
const processWebhookEvent = (webhookEvent) => {
  const { id, event, action, data, attempts } = webhookEvent;
  
  console.log(`Processando ${event}.${action} (tentativa ${attempts})`);
  
  // Rotear baseado no tipo de evento e ação
  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 não tratado: ${event}`);
  }
};

const handleRampEvent = async (action, data) => {
  if (action === 'UPDATE' && data.status === 'COMPLETED') {
    // Atualizar banco de dados
    await db.ramps.update(data.id, {
      status: data.status,
      updatedAt: data.updatedAt,
      transferProof: data.transferProof
    });
    
    // Notificar usuário
    await sendEmail(data.userId, 'Transação Concluída', {
      amount: data.toAmount,
      currency: data.toCurrency,
      txHash: data.transferProof
    });
  }
};
```

## Idempotência

Lide com entregas de webhook duplicadas usando o ID do evento:

```javascript theme={null}
const handleWebhook = async (webhookEvent) => {
  const { id, event, action, data } = webhookEvent;
  
  // Verificar se já processado (usando ID único do evento)
  const existing = await db.webhookEvents.findOne({ eventId: id });
  
  if (existing) {
    console.log(`Evento duplicado ${id}, pulando`);
    return { received: true, duplicate: true };
  }
  
  // Armazenar evento imediatamente para prevenir processamento duplicado
  await db.webhookEvents.create({
    eventId: id,
    eventType: event,
    action: action,
    payload: data,
    receivedAt: new Date(),
    processed: false
  });
  
  try {
    // Processar evento
    await processWebhookEvent(webhookEvent);
    
    // Marcar como processado
    await db.webhookEvents.update(
      { eventId: id },
      { processed: true, processedAt: new Date() }
    );
    
    return { received: true };
  } catch (error) {
    // Registrar erro mas ainda marcar como recebido
    await db.webhookEvents.update(
      { eventId: id },
      { error: error.message, failedAt: new Date() }
    );
    
    throw error;
  }
};
```

## Melhores Práticas

<AccordionGroup>
  <Accordion title="Reconhecer Rapidamente" icon="bolt">
    ```javascript theme={null}
    app.post('/webhooks/killb', async (req, res) => {
      // Verificar assinatura
      verifySignature(req);
      
      // Reconhecer imediatamente
      res.status(200).json({ received: true });
      
      // Processar assincronamente
      processAsync(req.body);
    });
    ```

    * Retornar 200 OK imediatamente
    * Processar eventos em background
    * Usar filas de jobs (Bull, Celery, etc.)
    * Não esperar por serviços externos
  </Accordion>

  <Accordion title="Lidar com Falhas Graciosamente" icon="shield">
    ```javascript theme={null}
    const processWebhook = async (event) => {
      try {
        await processEvent(event);
      } catch (error) {
        console.error('Processamento de webhook falhou:', error);
        // Registrar em serviço de monitoramento
        await logError(error, event);
        // Não lançar - já processamos
        // KillB tentará novamente de qualquer forma
      }
    };
    ```

    * Capturar todos os erros
    * Registrar falhas
    * Não lançar após reconhecer
    * Monitorar taxas de erro
  </Accordion>

  <Accordion title="Armazenar Histórico 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
    });
    ```

    * Manter trilha de auditoria
    * Habilitar replay se necessário
    * Depurar problemas facilmente
    * Atender requisitos de compliance
  </Accordion>
</AccordionGroup>

## Próximos Passos

<CardGroup cols={2}>
  <Card title="Guia de Configuração de Webhook" icon="wrench" href="/pt/guides/webhooks/setup">
    Implementação de webhook passo a passo
  </Card>

  <Card title="Referência de Eventos" icon="list" href="/pt/guides/webhooks/events">
    Lista completa de eventos webhook
  </Card>

  <Card title="Guia de Segurança" icon="lock" href="/pt/guides/webhooks/security">
    Proteja seus endpoints de webhook
  </Card>

  <Card title="Referência da API" icon="code" href="/api-reference/endpoint/webhooks-create">
    Veja a documentação da API de Webhooks
  </Card>
</CardGroup>
