§ 00O que são webhooks
Quando algo acontece do lado do EasyLiveChat — um cliente escreve no seu widget, um agente fecha uma conversa, ocorre uma ação relevante para auditoria — fazemos um POST HTTPS de saída para cada URL que você registrou. O corpo é JSON e contém o payload completo do evento, para o seu servidor reagir sem polling.
Pense nele como a imagem espelhada da API REST. A API é para o seu código alcançar o EasyLiveChat. Webhooks são para o EasyLiveChat alcançar o seu código. A maioria das integrações usa os dois.
Cada endpoint registrado tem seu próprio secret. Assinamos cada corpo de requisição com HMAC-SHA256 usando esse secret e enviamos o digest hex no cabeçalho x-easylivechat-signature. Seu servidor recomputa o HMAC com o mesmo secret e compara — bate significa que a requisição veio mesmo de nós, não-bate significa descartá-la.
§ 01Disponibilidade por plano
Endpoints de webhook podem ser criados em todos os planos pagos e durante o teste gratuito de 14 dias. Não há cobrança por evento — fan-out para múltiplos endpoints está incluído.
O stream audit.event é exclusivo do Enterprise, controlado pelo entitlement auditWebhookExport. message.created e conversation.updated disparam em todo plano que permita acesso à API.
§ 02Configurar um endpoint
Adicione um endpoint em https://app.livechattools.com/settings/webhooks. Geraremos um secret aleatório por endpoint e o exibiremos exatamente uma vez na criação. Trate-o como senha — copie-o imediatamente para o secret store do seu servidor.
- URL — Deve ser https:// e resolver para um IP público. Recusamos endereços loopback, privados e de metadata em nuvem tanto na criação quanto na entrega.
- Eventos — Escolha nomes específicos de eventos ou assine * (todos os eventos). Wildcards são convenientes para prototipagem, mas listas mais restritas tornam seu handler mais fácil de raciocinar.
- Secret — Gerado automaticamente, exibido uma vez. Usado para assinar cada entrega. Se você perder, exclua o endpoint e crie um novo — não há caminho de recuperação.
Ativo por padrão. Você pode revogar um endpoint a qualquer momento na mesma página — as entregas param instantaneamente.
§ 03Formato do payload
Cada entrega é um POST com content-type: application/json. Os cabeçalhos identificam o evento e carregam a assinatura; o envelope do corpo é sempre { 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 é o mesmo valor do cabeçalho x-easylivechat-event. O formato de data varia por evento — veja o catálogo abaixo.
§ 04Verificar a assinatura
Qualquer um que conheça a URL do seu endpoint pode jogar lixo via POST. A assinatura HMAC é como você distingue nossas requisições das falsas. Recalcule com seu secret e rejeite na divergência.
Leia os bytes brutos do body (NÃO faça json.parse antes — reserializar reordena chaves e quebra o hash). Remova o prefixo sha256= do header. Compute HMAC-SHA256(rawBody, secret) e compare em modo timing-safe com o hex recebido.
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", 200curl (debugging)
# Recompute the signature locally and compare with the header. echo -n "$RAW_BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET"
timingSafeEqual / hmac.compare_digest — use sempre comparação em tempo constante para que atacantes não consigam sondar o secret byte a byte.
§ 05Catálogo de eventos
Hoje a plataforma dispara quatro tipos de evento. Vamos adicionando à medida que recursos saem — assine * se quiser compatibilidade futura.
| Evento | Dispara quando | Formato de dados |
|---|---|---|
| message.created | Uma nova mensagem chega em uma conversa, independente de quem enviou (cliente recebendo, agente enviando, envio via API REST). | { conversationId, message } |
| conversation.updated | O status de uma conversa (OPEN · SNOOZED · CLOSED) ou seu assignedAgentId muda. | { conversationId, status?, assignedAgentId? } |
| audit.event | Sempre que uma linha de audit-log é gravada — login de agente, mudança de role, integração criada, chave revogada, etc. (Plano Enterprise.) | { action, entity, entityId, actorId, metadata } |
| webhook.test | Você clica em "Test" em Ajustes → Webhooks. Assinado e formatado exatamente como um evento real. | { tenantId, test: true } |
Assine * para receber todo evento atual e futuro. Assine nomes específicos se quiser restringir o que seu handler precisa conhecer.
§ 06Testar a integração
O botão "Test" em Ajustes → Webhooks dispara um evento webhook.test no seu endpoint e reporta o status HTTP upstream e o tempo decorrido. Use para validar seu handler antes de apontar tráfego real.
O payload de teste usa exatamente o mesmo envelope de body, nomes de cabeçalho e esquema HMAC dos eventos de produção — se sua verificação passa para webhook.test, passa para message.created e todo o resto.
§ 07Retries e timeouts
Damos 10 segundos para seu servidor responder. Qualquer status não-2xx ou erro de rede é falha. Entregas falhas tentam novamente até 5 vezes com backoff exponencial começando em 2 segundos, então um endpoint instável tem várias chances antes de desistirmos daquele evento.
NÃO garantimos entrega exactly-once — sob flapping de rede o mesmo evento pode chegar duas vezes. Faça seu handler idempotente: dedupe pelo data.message.id interno (ou o id que faça sentido para aquele evento).
§ 08Checklist de segurança
- Sempre use https:// — endpoints http:// puros são rejeitados na criação.
- Bloqueamos IPs privados, loopback e de metadata em nuvem tanto na criação quanto na entrega, então um registro DNS mal configurado não pode ser usado para pivotar dentro da sua rede.
- Compare assinaturas com um equal timing-safe (Node crypto.timingSafeEqual, Python hmac.compare_digest, Go hmac.Equal).
- Faça rotação excluindo o endpoint e criando um novo com a mesma URL. Atualize seu secret store de forma atômica.