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

# Gestión

> Administrar, probar y monitorear tus webhooks

# Gestión de Webhooks

Endpoints para administrar tus webhooks: listar, actualizar, probar, ver logs y eliminar.

***

## Listar Webhooks

Obtén todos los webhooks configurados.

```bash theme={null}
curl -X GET https://api.whaapy.com/user-webhooks \
  -H "Authorization: Bearer wha_TU_API_KEY"
```

**Response:**

```json theme={null}
{
  "data": [
    {
      "id": "wh-uuid",
      "name": "Mi Webhook",
      "url": "https://mi-servidor.com/webhooks",
      "events": ["message.received"],
      "isActive": true,
      "failureCount": 0,
      "createdAt": "2025-01-15T10:00:00Z"
    }
  ]
}
```

***

## Actualizar Webhook

Modifica los eventos o estado de un webhook.

```bash theme={null}
curl -X PATCH https://api.whaapy.com/user-webhooks/<id> \
  -H "Authorization: Bearer wha_TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["message.received", "message.sent"],
    "isActive": true
  }'
```

| Campo      | Tipo      | Descripción            |
| ---------- | --------- | ---------------------- |
| `name`     | string    | Nuevo nombre           |
| `url`      | string    | Nueva URL              |
| `events`   | string\[] | Nueva lista de eventos |
| `isActive` | boolean   | Activar/desactivar     |

***

## Probar Webhook

Envía un evento de prueba para verificar tu endpoint.

```bash theme={null}
curl -X POST https://api.whaapy.com/user-webhooks/<id>/test \
  -H "Authorization: Bearer wha_TU_API_KEY"
```

**Response:**

```json theme={null}
{
  "success": true,
  "message": "Test webhook sent successfully",
  "data": {
    "status": 200,
    "duration_ms": 150,
    "response_preview": "{\"received\":true}"
  }
}
```

<Tip>
  Usa el endpoint de test para verificar que tu servidor responde correctamente antes de activar el webhook.
</Tip>

***

## Ver Logs

Obtén el historial de entregas de un webhook.

```bash theme={null}
curl -X GET "https://api.whaapy.com/user-webhooks/<id>/logs?limit=50" \
  -H "Authorization: Bearer wha_TU_API_KEY"
```

**Response:**

```json theme={null}
{
  "data": [
    {
      "id": "log-uuid",
      "event": "message.received",
      "status": 200,
      "duration_ms": 120,
      "timestamp": "2025-01-15T10:30:00Z",
      "error": null
    },
    {
      "id": "log-uuid-2",
      "event": "message.sent",
      "status": 500,
      "duration_ms": 30000,
      "timestamp": "2025-01-15T10:31:00Z",
      "error": "Connection timeout"
    }
  ]
}
```

***

## Eliminar Webhook

```bash theme={null}
curl -X DELETE https://api.whaapy.com/user-webhooks/<id> \
  -H "Authorization: Bearer wha_TU_API_KEY"
```

***

## Política de Reintentos

Cuando tu endpoint falla, Whaapy reintenta automáticamente:

| Intento | Delay      | Tiempo total |
| ------- | ---------- | ------------ |
| 1       | Inmediato  | 0s           |
| 2       | 2 segundos | 2s           |
| 3       | 4 segundos | 6s           |
| 4       | 8 segundos | 14s          |

<Note>
  **Máximo 3 reintentos** con backoff exponencial. Timeout por request: 30 segundos.
</Note>

***

## Desactivación Automática

Si un webhook falla **10 veces consecutivas**, se desactiva automáticamente para proteger ambos sistemas.

Para reactivarlo:

```bash theme={null}
curl -X PATCH https://api.whaapy.com/user-webhooks/<id> \
  -H "Authorization: Bearer wha_TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"isActive": true}'
```

<Warning>
  Antes de reactivar, verifica que tu endpoint esté funcionando correctamente.
</Warning>

***

## Mejores Prácticas

### 1. Responde Rápido

Tu endpoint debe responder en menos de 30 segundos. Procesa async si necesitas hacer trabajo pesado.

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={null}
    app.post('/webhook', async (req, res) => {
      // Responde inmediatamente
      res.json({ received: true });
      
      // Procesa después (async)
      await processWebhook(req.body).catch(console.error);
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from threading import Thread

    @app.route('/webhook', methods=['POST'])
    def webhook():
        payload = request.get_json()
        
        # Responde inmediatamente
        response = jsonify({'received': True})
        
        # Procesa en background
        Thread(target=process_webhook, args=(payload,)).start()
        
        return response
    ```
  </Tab>
</Tabs>

### 2. Implementa Idempotencia

Los webhooks pueden llegar más de una vez. Usa el timestamp y evento para detectar duplicados.

```javascript theme={null}
const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const eventId = `${req.body.event}-${req.body.timestamp}`;
  
  if (processedEvents.has(eventId)) {
    return res.json({ received: true, duplicate: true });
  }
  
  processedEvents.add(eventId);
  // Procesar...
  
  res.json({ received: true });
});
```

<Tip>
  En producción, usa Redis o una base de datos en lugar de un Set en memoria.
</Tip>

### 3. Verifica la Firma

**Siempre** verifica la firma HMAC antes de procesar. Ver [Seguridad](/api-reference/webhooks/security).

### 4. Usa HTTPS

Los webhooks solo se envían a endpoints HTTPS. Para desarrollo local, usa:

* [ngrok](https://ngrok.com) - Túnel HTTPS gratuito
* [localtunnel](https://localtunnel.github.io/www/) - Alternativa open source
* [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/) - Opción empresarial

### 5. Loggea para Debugging

```javascript theme={null}
app.post('/webhook', (req, res) => {
  console.log('Webhook received:', {
    event: req.body.event,
    timestamp: req.headers['x-webhook-timestamp'],
    id: req.headers['x-webhook-id'],
  });
  
  // Procesar...
});
```

***

## Ejemplo Completo: Integración con CRM

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

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.WHAAPY_WEBHOOK_SECRET;
const CRM_API = 'https://mi-crm.com/api';

function verifySignature(payload, signature) {
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(JSON.stringify(payload))
    .digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

app.post('/webhooks/whaapy', async (req, res) => {
  // 1. Verificar firma
  const signature = req.headers['x-webhook-signature'];
  if (!verifySignature(req.body, signature)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // 2. Responder inmediatamente
  res.json({ received: true });

  // 3. Procesar según evento
  const { event, data } = req.body;

  try {
    switch (event) {
      case 'message.received':
        // Extraer texto del formato Meta
        const text = data.message?.text?.body || '[Media]';
        await axios.post(`${CRM_API}/activities`, {
          type: 'whatsapp_message',
          contact_phone: data.message.from,
          content: text,
          direction: 'inbound',
          whaapy_id: data.whaapy_message_id,
        });
        break;

      case 'contact.created':
        await axios.post(`${CRM_API}/contacts`, {
          phone: data.phone_number,
          name: data.name,
          source: 'whaapy',
        });
        break;

      case 'conversation.closed':
        await axios.patch(`${CRM_API}/deals`, {
          phone: data.phone_number,
          status: 'contacted',
        });
        break;
    }
  } catch (error) {
    console.error('Error procesando webhook:', error.message);
  }
});

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});
```

***

## Recursos Adicionales

<CardGroup cols={2}>
  <Card title="Dashboard" icon="gauge" href="https://app.whaapy.com/settings/webhooks">
    Gestiona webhooks desde la UI
  </Card>

  <Card title="Eventos Disponibles" icon="list" href="/api-reference/webhooks/events">
    Lista completa de eventos
  </Card>
</CardGroup>
