Saltar al contenido principal

Introducción a los Webhooks

Los webhooks te permiten recibir notificaciones en tiempo real sobre eventos en tu organización de Altur. Cuando ocurre un evento (por ejemplo, el fin de una llamada), Altur envía una solicitud HTTP POST con los datos del evento al endpoint que configures, manteniendo tus sistemas o CRM al día sin necesidad de hacer polling.

Cómo Funcionan los Webhooks

  1. Configura tu Endpoint
    Configura la URL del webhook en la plataforma de Altur. Esta URL recibirá las solicitudes POST cuando se dispare un evento.
  2. Recibe Notificaciones
    Cuando ocurre un evento, Altur envía un POST con los detalles del evento en formato JSON a tu endpoint.
  3. Confirma la Recepción
    Tu endpoint debe responder con un código 200 OK para confirmar la recepción. Altur espera hasta 5 segundos por la respuesta. Si la solicitud falla, hace timeout o devuelve un estado distinto de 2xx, Altur reintenta con backoff exponencial (hasta 5 intentos en total).

Tipos de Evento

Los webhooks pueden disparar los siguientes tipos de evento:
  • on_call_end: Se dispara al final de una llamada. Envía información detallada de la llamada junto con datos básicos del agente de IA y del usuario final.
  • campaign.status_changed: Se dispara cuando una Campaña cambia de estado. Incluye el snapshot de analíticas cuando la campaña llega a finished.
  • campaign.cycle_completed: Se dispara cuando una iteración del ciclo de una Campaña termina, con el número de iteración completada y el timestamp de la siguiente iteración.
Los webhooks de campaña usan un envelope versionado (event_id, event_type, occurred_at, api_version, project_id, data). Usa event_id para procesamiento idempotente. El evento on_call_end es anterior a este envelope y mantiene su forma plana en el nivel superior.

Asegurando tus Webhooks

Para garantizar comunicaciones seguras y verificar la autenticidad de las solicitudes, Altur incluye una firma HMAC en el header X-Altur-Signature de cada solicitud.

Cómo Funciona

  • Secreto Compartido: Cada integración de webhook se configura con una llave secreta única codificada en base64.
  • Generación del HMAC: Altur genera un hash HMAC SHA-256 usando el secreto compartido (decodificado de base64) y el payload JSON de la solicitud (serializado sin espacios). El hash se codifica en base64 y se incluye en el header X-Altur-Signature.
  • Validación: Tu endpoint debe validar la firma usando el mismo secreto compartido y el mismo formato de serialización JSON.

Función de Validación de Ejemplo

import json
import base64
import hmac
import hashlib

def is_valid_signature(secret: str, payload: Union[str, dict], signature: str) -> bool:
    """
    Valida la firma de un webhook de Altur.

    Args:
        secret: Llave secreta compartida codificada en base64
        payload: Payload de la solicitud (dict o string JSON)
        signature: Firma codificada en base64 del header X-Altur-Signature
    """
    # Convertir el payload a un formato JSON consistente (compacto, sin espacios)
    if isinstance(payload, dict):
        payload_bytes = json.dumps(payload, separators=(',', ':')).encode('utf-8')
    else:
        payload_bytes = payload.encode('utf-8')

    # Decodificar el secreto base64
    secret_bytes = base64.b64decode(secret)

    # Generar la firma esperada
    expected_signature = base64.b64encode(
        hmac.new(secret_bytes, payload_bytes, hashlib.sha256).digest()
    ).decode()

    return hmac.compare_digest(signature, expected_signature)

Ejemplo de Uso

Ejemplo 1: Usando un dict JSON ya parseado (recomendado)
import json
request_body = request.get_json()  # JSON parseado por tu framework web
signature = request.headers.get('X-Altur-Signature')
shared_secret = "tu-secreto-codificado-en-base64"

if is_valid_signature(shared_secret, request_body, signature):
    print("Webhook válido!")
else:
    print("Webhook inválido!")
Ejemplo 2: Usando un string JSON crudo
payload = '{"type":"on_call_end","status":"ended"}' # Payload crudo de la solicitud (JSON compacto)
signature = "firma-del-header" # Header X-Altur-Signature
shared_secret = "tu-secreto-codificado-en-base64" # Secreto compartido en base64

if is_valid_signature(shared_secret, payload, signature):
    print("Webhook válido!")
else:
    print("Webhook inválido!")

Headers Enviados con el Webhook

  • Content-Type: application/json
  • X-Altur-Signature: hash HMAC SHA-256 codificado en base64 del payload JSON compacto

Por Qué Importa

Este mecanismo garantiza que:
  • La solicitud proviene de Altur.
  • El payload no fue modificado en tránsito.

Política de Reintentos

Si tu endpoint no responde con un estado 2xx (o no responde antes de los 5 segundos del request timeout), Altur reintenta la entrega:
  • Intentos máximos: 5 (entrega inicial más 4 reintentos).
  • Backoff: 60s después del primer fallo, duplicándose en cada intento (60s, 2m, 4m, 8m).
Si todos los intentos fallan, el evento se marca como fallido y no vuelve a entregarse.

Ejemplo de Flujo

Un flujo típico para manejar webhooks:
  1. Configura tu Endpoint Crea un endpoint en tu backend, por ejemplo /webhooks/altur/on-call-end. Debe ser accesible públicamente y aceptar solicitudes POST con Content-Type: application/json.
  2. Parsea el Payload Extrae y procesa el payload JSON enviado por Altur. Maneja casos como payloads malformados o campos faltantes para evitar errores en runtime.
  3. Valida la Autenticidad Usa el header X-Altur-Signature para verificar la autenticidad. Esto implica:
    • Recomputar la firma HMAC usando el secreto compartido y el payload.
    • Compararla con la firma del header.
  4. Responde Rápido Devuelve 200 OK para confirmar la recepción. Altur hace timeout a los 5 segundos, así que responde mucho antes de ese límite para evitar reintentos innecesarios.
  5. Registra los Eventos Guarda logs de las solicitudes y del procesamiento para tener trazabilidad. Es especialmente útil al depurar problemas con payloads o reintentos.
  6. Maneja los Reintentos Diseña tu sistema para tolerar reintentos. Asegúrate de que tu endpoint sea idempotente: procesar el mismo evento varias veces no debe duplicar acciones (por ejemplo, inserts en BD o llamadas a APIs).

Buenas Prácticas

  1. Asegura tu Endpoint
    • Autentica las Solicitudes: Verifica la firma HMAC en cada request. Usa un secreto único y seguro por integración.
    • Restringe el Acceso: Usa whitelisting de IP o firewall para permitir solicitudes únicamente desde los servidores de Altur.
    • Cifra la Comunicación: Usa HTTPS para que los datos viajen cifrados.
  2. Registra los Eventos
    • Registra Todo: Guarda timestamps, headers, payloads y respuestas de cada solicitud entrante.
    • Monitorea Fallos: Configura alertas para fallos repetidos o solicitudes inválidas.
    • Mantén Logs: Retén los logs por un periodo razonable para auditoría o debugging.
  3. Prueba Bien
    • Simula Eventos: Usa el dashboard de Altur para simular eventos y confirmar que tu endpoint los maneja correctamente.
    • Prueba Casos Borde: Payloads malformados, payloads grandes, campos faltantes.
    • Usa Entornos de Staging: Mantén un endpoint dedicado para pruebas, separado de producción.
  4. Optimiza el Rendimiento
    • Responde Rápido: Altur hace timeout a los 5 segundos. Si el procesamiento es más largo, responde 200 OK de inmediato y procesa en background.
    • Minimiza el Procesamiento: Haz solo validación ligera y encolado en el endpoint; deja el trabajo pesado a workers.
    • Usa Caching: Cuando aplique, cachea respuestas para solicitudes redundantes.
  5. Sé Idempotente
    • Evita Acciones Duplicadas: Diseña el sistema para que los reintentos no causen operaciones duplicadas (inserts en BD, llamadas a APIs). Usa el event_id único de Altur para deduplicar.
  6. Comunica los Fallos
    • Devuelve Status Codes Correctos: Usa 400 para solicitudes inválidas, 500 para errores del servidor, etc.
    • Loguea y Notifica: Registra los fallos y considera notificar al equipo si los reintentos críticos se agotan.

Solución de Problemas con la Validación de Firma

Si tienes problemas validando la firma, revisa estos puntos comunes:

1. Formato de Serialización JSON

Usa el mismo formato JSON que Altur:
  • Formato compacto: Sin espacios después de comas ni de dos puntos.
  • Orden consistente: Usa exactamente el payload recibido.
# ✅ Correcto - JSON compacto
json.dumps(data, separators=(',', ':'))

# ❌ Incorrecto - incluye espacios
json.dumps(data)  # Resultado: {"key": "value"}

2. Formato de la Llave Secreta

  • Tu secreto debe estar codificado en base64.
  • Debe ser el mismo configurado en tu integración de webhook en Altur.

3. Nombre del Header

  • El header es X-Altur-Signature (algunos frameworks son case-sensitive).
  • Confirma que estás leyendo el header correcto.

4. Timing Attacks

Usa siempre funciones de comparación timing-safe:
  • Python: hmac.compare_digest()
  • Node.js: crypto.timingSafeEqual()
  • PHP: hash_equals()

5. Probar tu Implementación

Puedes probar tu validación con este ejemplo:
# Datos de prueba
test_payload = {"test": "data"}
test_secret = "dGVzdC1zZWNyZXQ="  # base64 de "test-secret"
expected_signature = "ZjM2ZTc4YWJkZDQ1ZGZlYjM4NTIwYWY1ZmY1MzFkMTk4YmM0YzJmMzU0MTJjMmE3MGZjZGY4ZDhkOTYzOWY4OA=="

# Debe devolver True
result = is_valid_signature(test_secret, test_payload, expected_signature)
print(f"Resultado de validación: {result}")