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

# Webhooks

> Configura notificaciones en tiempo real con webhooks de NotMeta

## ¿Qué son los webhooks?

Los webhooks son notificaciones HTTP que NotMeta envía a tu aplicación cuando ocurren eventos específicos. Esto te permite reaccionar en tiempo real a cambios en conversaciones, mensajes y usuarios.

## Eventos disponibles

### Conversaciones

* `conversation.created` - Nueva conversación creada
* `conversation.assigned` - Conversación asignada a un usuario
* `conversation.unassigned` - Conversación desasignada
* `conversation.closed` - Conversación cerrada
* `conversation.reopened` - Conversación reabierta

### Mensajes

* `message.received` - Nuevo mensaje recibido
* `message.sent` - Mensaje enviado
* `message.delivered` - Mensaje entregado
* `message.read` - Mensaje leído
* `message.failed` - Error al enviar mensaje

### Usuarios

* `user.created` - Nuevo usuario creado
* `user.updated` - Usuario actualizado
* `user.deleted` - Usuario eliminado
* `user.online` - Usuario conectado
* `user.offline` - Usuario desconectado

### Sistema

* `webhook.verified` - Webhook verificado
* `webhook.failed` - Error en webhook

## Configurar webhooks

### Crear webhook

```bash theme={null}
curl -X POST "https://api.notmeta.com/v1/webhooks" \
  -H "Authorization: Bearer TU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://tu-dominio.com/webhook",
    "events": ["message.received", "conversation.assigned"],
    "secret": "tu_secret_webhook"
  }'
```

### Respuesta

```json theme={null}
{
  "id": "wh_123456789",
  "url": "https://tu-dominio.com/webhook",
  "events": ["message.received", "conversation.assigned"],
  "secret": "whsec_abc123...",
  "created_at": "2024-01-01T00:00:00Z",
  "status": "active"
}
```

## Estructura de eventos

### Evento de mensaje recibido

```json theme={null}
{
  "id": "evt_123456789",
  "type": "message.received",
  "created_at": "2024-01-01T12:00:00Z",
  "data": {
    "message": {
      "id": "msg_123456789",
      "conversation_id": "conv_123456789",
      "from": "1234567890",
      "to": "0987654321",
      "type": "text",
      "text": "Hola, necesito ayuda",
      "timestamp": "2024-01-01T12:00:00Z"
    },
    "conversation": {
      "id": "conv_123456789",
      "status": "open",
      "assigned_to": null,
      "created_at": "2024-01-01T11:55:00Z"
    }
  }
}
```

### Evento de conversación asignada

```json theme={null}
{
  "id": "evt_123456790",
  "type": "conversation.assigned",
  "created_at": "2024-01-01T12:05:00Z",
  "data": {
    "conversation": {
      "id": "conv_123456789",
      "status": "assigned",
      "assigned_to": "user_123456789",
      "assigned_at": "2024-01-01T12:05:00Z"
    },
    "user": {
      "id": "user_123456789",
      "name": "Juan Pérez",
      "email": "juan@empresa.com",
      "role": "agent"
    }
  }
}
```

## Implementar endpoint webhook

### Node.js con Express

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

app.use(express.json());

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-notmeta-signature'];
  const payload = JSON.stringify(req.body);
  
  // Verificar firma
  if (!verifySignature(payload, signature)) {
    return res.status(400).send('Invalid signature');
  }
  
  // Procesar evento
  const event = req.body;
  handleWebhookEvent(event);
  
  res.status(200).send('OK');
});

function verifySignature(payload, signature) {
  const secret = process.env.NOTMETA_WEBHOOK_SECRET;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return signature === `sha256=${expectedSignature}`;
}

function handleWebhookEvent(event) {
  switch (event.type) {
    case 'message.received':
      handleNewMessage(event.data.message);
      break;
    case 'conversation.assigned':
      handleConversationAssigned(event.data.conversation);
      break;
    default:
      console.log(`Evento no manejado: ${event.type}`);
  }
}

function handleNewMessage(message) {
  console.log(`Nuevo mensaje de ${message.from}: ${message.text}`);
  // Tu lógica aquí
}

function handleConversationAssigned(conversation) {
  console.log(`Conversación ${conversation.id} asignada a ${conversation.assigned_to}`);
  // Tu lógica aquí
}

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

### Python con Flask

```python theme={null}
from flask import Flask, request, jsonify
import hmac
import hashlib
import json

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-NotMeta-Signature')
    payload = request.get_data()
    
    # Verificar firma
    if not verify_signature(payload, signature):
        return 'Invalid signature', 400
    
    # Procesar evento
    event = request.json
    handle_webhook_event(event)
    
    return 'OK', 200

def verify_signature(payload, signature):
    secret = os.environ.get('NOTMETA_WEBHOOK_SECRET')
    expected_signature = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    return signature == f'sha256={expected_signature}'

def handle_webhook_event(event):
    event_type = event['type']
    
    if event_type == 'message.received':
        handle_new_message(event['data']['message'])
    elif event_type == 'conversation.assigned':
        handle_conversation_assigned(event['data']['conversation'])
    else:
        print(f'Evento no manejado: {event_type}')

def handle_new_message(message):
    print(f'Nuevo mensaje de {message["from"]}: {message["text"]}')
    # Tu lógica aquí

def handle_conversation_assigned(conversation):
    print(f'Conversación {conversation["id"]} asignada')
    # Tu lógica aquí

if __name__ == '__main__':
    app.run(port=3000)
```

### PHP

```php theme={null}
<?php
// webhook.php

$secret = $_ENV['NOTMETA_WEBHOOK_SECRET'];
$signature = $_SERVER['HTTP_X_NOTMETA_SIGNATURE'];
$payload = file_get_contents('php://input');

// Verificar firma
if (!verifySignature($payload, $signature, $secret)) {
    http_response_code(400);
    echo 'Invalid signature';
    exit;
}

// Procesar evento
$event = json_decode($payload, true);
handleWebhookEvent($event);

echo 'OK';

function verifySignature($payload, $signature, $secret) {
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);
    return hash_equals($expectedSignature, $signature);
}

function handleWebhookEvent($event) {
    $eventType = $event['type'];
    
    switch ($eventType) {
        case 'message.received':
            handleNewMessage($event['data']['message']);
            break;
        case 'conversation.assigned':
            handleConversationAssigned($event['data']['conversation']);
            break;
        default:
            error_log("Evento no manejado: $eventType");
    }
}

function handleNewMessage($message) {
    error_log("Nuevo mensaje de {$message['from']}: {$message['text']}");
    // Tu lógica aquí
}

function handleConversationAssigned($conversation) {
    error_log("Conversación {$conversation['id']} asignada");
    // Tu lógica aquí
}
?>
```

## Verificación de webhooks

### Verificación automática

Cuando creas un webhook, NotMeta envía un evento de verificación:

```json theme={null}
{
  "id": "evt_verify_123",
  "type": "webhook.verified",
  "created_at": "2024-01-01T00:00:00Z",
  "data": {
    "webhook_id": "wh_123456789",
    "challenge": "challenge_string_123"
  }
}
```

### Responder al desafío

```javascript theme={null}
app.post('/webhook', (req, res) => {
  if (req.body.type === 'webhook.verified') {
    // Responder al desafío de verificación
    return res.status(200).send(req.body.data.challenge);
  }
  
  // Procesar otros eventos...
});
```

## Manejo de errores

### Reintentos automáticos

NotMeta reintenta automáticamente webhooks que fallan:

* **Primer reintento**: 1 minuto
* **Segundo reintento**: 5 minutos
* **Tercer reintento**: 15 minutos
* **Cuarto reintento**: 1 hora
* **Máximo**: 4 reintentos

### Manejo de errores en tu endpoint

```javascript theme={null}
app.post('/webhook', async (req, res) => {
  try {
    // Procesar evento
    await handleWebhookEvent(req.body);
    res.status(200).send('OK');
  } catch (error) {
    console.error('Error procesando webhook:', error);
    
    // Para errores temporales, devolver 5xx
    if (isTemporaryError(error)) {
      res.status(503).send('Service Unavailable');
    } else {
      // Para errores permanentes, devolver 4xx
      res.status(400).send('Bad Request');
    }
  }
});
```

## Mejores prácticas

### Idempotencia

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

function handleWebhookEvent(event) {
  // Evitar procesar el mismo evento dos veces
  if (processedEvents.has(event.id)) {
    console.log(`Evento ${event.id} ya procesado`);
    return;
  }
  
  // Procesar evento
  processEvent(event);
  
  // Marcar como procesado
  processedEvents.add(event.id);
}
```

### Procesamiento asíncrono

```javascript theme={null}
const Queue = require('bull');
const webhookQueue = new Queue('webhook processing');

app.post('/webhook', (req, res) => {
  // Verificar firma
  if (!verifySignature(req.body, req.headers['x-notmeta-signature'])) {
    return res.status(400).send('Invalid signature');
  }
  
  // Agregar a cola para procesamiento asíncrono
  webhookQueue.add('process-event', req.body);
  
  res.status(200).send('OK');
});

webhookQueue.process('process-event', async (job) => {
  const event = job.data;
  await handleWebhookEvent(event);
});
```

### Logging

```javascript theme={null}
function logWebhookEvent(event) {
  console.log(JSON.stringify({
    timestamp: new Date().toISOString(),
    event_id: event.id,
    event_type: event.type,
    webhook_url: req.url
  }));
}
```

## Monitoreo y debugging

### Estado de webhooks

```bash theme={null}
curl -X GET "https://api.notmeta.com/v1/webhooks" \
  -H "Authorization: Bearer TU_TOKEN"
```

### Logs de webhook

```bash theme={null}
curl -X GET "https://api.notmeta.com/v1/webhooks/wh_123456789/logs" \
  -H "Authorization: Bearer TU_TOKEN"
```

### Estadísticas de webhooks

```bash theme={null}
curl -X GET "https://api.notmeta.com/v1/webhooks/wh_123456789/stats" \
  -H "Authorization: Bearer TU_TOKEN"
```

## Solución de problemas

### Webhook no recibe eventos

1. Verifica que la URL sea accesible desde internet
2. Confirma que uses HTTPS en producción
3. Revisa los logs del webhook en NotMeta
4. Verifica que los eventos estén configurados correctamente

### Errores de firma

1. Confirma que el secret sea correcto
2. Verifica que uses el payload completo
3. Revisa la codificación (UTF-8)

### Timeouts

1. Asegúrate de responder rápidamente (máximo 30 segundos)
2. Usa procesamiento asíncrono para tareas pesadas
3. Implementa circuit breakers para servicios externos

## Recursos adicionales

<Card title="Configuración inicial" icon="gear" href="/developers/setup">
  Configura tu entorno de desarrollo
</Card>

<Card title="Mejores prácticas" icon="star" href="/developers/best-practices">
  Aprende las mejores prácticas para integrar con NotMeta
</Card>
