DOCUMENTOS/Webhooks~ 8 MIN DE LECTURA

EVENTS · HMAC · POST

EasyLiveChat te llama
en el momento en que algo ocurre.

Los webhooks son el canal de push de EasyLiveChat — en lugar de hacer polling a nuestra API según un horario, nos das una URL y nosotros enviamos POST de cada evento relevante en menos de un segundo desde que ocurre. Cada petición se firma con HMAC-SHA256 para que tu servidor pueda demostrar que realmente viene de nosotros.

§ 00Qué son los webhooks

Cuando ocurre algo del lado de EasyLiveChat — un cliente escribe en tu widget, un agente cierra una conversación, sucede una acción relevante para auditoría — hacemos un POST HTTPS saliente a cada URL que hayas registrado. El cuerpo es JSON y contiene el payload completo del evento, para que tu servidor reaccione sin hacer polling.

Piénsalo como la imagen espejo de la API REST. La API es para que tu código alcance EasyLiveChat. Los webhooks son para que EasyLiveChat alcance tu código. La mayoría de integraciones usan ambos.

Cada endpoint registrado tiene su propio secreto. Firmamos cada cuerpo de petición con HMAC-SHA256 usando ese secreto y enviamos el hex en la cabecera x-easylivechat-signature. Tu servidor recomputa el HMAC con el mismo secreto y compara — coincidencia significa que la petición vino realmente de nosotros, no-coincidencia significa descartarla.

§ 01Disponibilidad por plan

Los endpoints de webhook se pueden crear en todos los planes de pago y durante la prueba gratuita de 14 días. No hay facturación por evento — el fan-out a múltiples endpoints está incluido.

El stream audit.event es sólo de Enterprise, gestionado por la habilitación auditWebhookExport. message.created y conversation.updated se disparan en todo plan que permita acceso al API.

§ 02Configurar un endpoint

Añade un endpoint en https://app.livechattools.com/settings/webhooks. Generaremos un secreto aleatorio por endpoint y te lo mostraremos una sola vez en la creación. Trátalo como una contraseña — cópialo al gestor de secretos de tu servidor inmediatamente.

  • URLDebe ser https:// y resolver a una IP pública. Rechazamos direcciones loopback, privadas y de metadata cloud tanto en la creación como en la entrega.
  • EventosElige nombres de eventos específicos o suscríbete a * (todos los eventos). Los wildcards son cómodos para prototipos, pero listas más estrechas hacen tu handler más fácil de razonar.
  • SecretoGenerado automáticamente, mostrado una vez. Se usa para firmar cada entrega. Si lo pierdes, borra el endpoint y crea uno nuevo — no hay recuperación.

Activo por defecto. Puedes revocar un endpoint en cualquier momento desde la misma página — las entregas se detienen al instante.

§ 03Forma del payload

Cada entrega es un POST con content-type: application/json. Las cabeceras identifican el evento y llevan la firma; el envelope del body es siempre { event, deliveredAt, data }:

POST https://your-server.example.com/webhook
content-type: application/json
user-agent: EasyLiveChat-Webhooks/1.0
x-easylivechat-event: message.created
x-easylivechat-signature: sha256=<hex>

{
  "event": "message.created",
  "deliveredAt": "2026-05-27T08:55:25.841Z",
  "data": {
    "conversationId": "cmp...",
    "message": {
      "id": "cmp...",
      "body": "Hi!",
      "senderType": "CUSTOMER",
      "createdAt": "2026-05-27T08:55:25.840Z"
    }
  }
}

event es el mismo valor que la cabecera x-easylivechat-event. La forma de data varía según el evento — ver catálogo abajo.

§ 04Verificar la firma

Cualquiera que conozca la URL de tu endpoint puede hacerle POST con basura. La firma HMAC es cómo distingues nuestras peticiones de las falsificadas. Recomputa con tu secreto y rechaza la petición ante mismatch.

Lee los bytes crudos del body (NO hagas json.parse primero — re-serializar reordena las claves y rompe el hash). Quita el prefijo sha256= de la cabecera. Computa HMAC-SHA256(rawBody, secret) y compara con seguridad temporal contra el hex recibido.

Node.js

import crypto from 'node:crypto';
import express from 'express';

const SECRET = process.env.WEBHOOK_SECRET;
const app = express();
app.use(express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
  const received = (req.header('x-easylivechat-signature') || '').replace(/^sha256=/, '');
  const expected = crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
  const ok =
    received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received, 'hex'), Buffer.from(expected, 'hex'));
  if (!ok) return res.status(401).send('bad signature');

  const event = JSON.parse(req.body.toString('utf8'));
  // handle event.event, event.data...
  res.status(200).send('ok');
});

Python (Flask)

import hmac, hashlib, os
from flask import Flask, request, abort

SECRET = os.environ["WEBHOOK_SECRET"].encode()
app = Flask(__name__)

@app.post("/webhook")
def webhook():
    received = request.headers.get("x-easylivechat-signature", "").removeprefix("sha256=")
    expected = hmac.new(SECRET, request.get_data(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(received, expected):
        abort(401)
    event = request.get_json()
    # handle event["event"], event["data"]...
    return "ok", 200

curl (debugging)

# Recompute the signature locally and compare with the header.
echo -n "$RAW_BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET"

timingSafeEqual / hmac.compare_digestusa siempre comparación de tiempo constante para que los atacantes no puedan sondear el secreto byte a byte.

§ 05Catálogo de eventos

Hoy la plataforma dispara cuatro tipos de eventos. Añadimos más a medida que enviamos funciones — suscríbete a * si quieres compatibilidad futura.

EventoSe dispara cuandoForma de datos
message.createdLlega un nuevo mensaje a una conversación, sin importar quién lo envió (cliente entrante, agente saliente, envío por API REST).{ conversationId, message }
conversation.updatedEl estado de una conversación (OPEN · SNOOZED · CLOSED) o su assignedAgentId cambia.{ conversationId, status?, assignedAgentId? }
audit.eventCada vez que se escribe una fila de audit-log — login de agente, cambio de rol, integración creada, clave revocada, etc. (Plan Enterprise.){ action, entity, entityId, actorId, metadata }
webhook.testHaces clic en "Test" en Ajustes → Webhooks. Firmado y con forma exactamente igual a un evento real.{ tenantId, test: true }

Suscríbete a * para recibir todo evento actual y futuro. Suscríbete a nombres específicos si quieres restringir lo que tu handler debe conocer.

§ 06Probar la integración

El botón "Test" en Ajustes → Webhooks dispara un evento webhook.test a tu endpoint y reporta el estado HTTP upstream y tiempo transcurrido. Úsalo para validar tu handler antes de dirigir tráfico real.

El payload de test usa exactamente el mismo envelope de body, nombres de cabecera y esquema HMAC que los eventos de producción — si tu verificación pasa para webhook.test, pasará para message.created y todo lo demás.

§ 07Reintentos y timeouts

Damos a tu servidor 10 segundos para responder. Cualquier estado no-2xx o error de red es fallo. Las entregas fallidas se reintentan hasta 5 veces con backoff exponencial desde 2 segundos, así un endpoint inestable tiene varias oportunidades antes de que abandonemos ese evento.

NO garantizamos entrega exactly-once — bajo flapping de red el mismo evento puede llegar dos veces. Haz tu handler idempotente: deduplica por el data.message.id interno (o el id que tenga sentido para ese evento).

§ 08Checklist de seguridad

  • Usa siempre https:// — los endpoints http:// puros se rechazan en la creación.
  • Bloqueamos IPs privadas, loopback y de metadata cloud tanto en creación como entrega, así un registro DNS mal configurado no puede usarse para pivotar dentro de tu red.
  • Compara las firmas con una igualdad timing-safe (Node crypto.timingSafeEqual, Python hmac.compare_digest, Go hmac.Equal).
  • Rota borrando el endpoint y creando uno nuevo con la misma URL. Actualiza tu secret store de forma atómica.