DOCUMENTOS/Webhooks~ 8 MIN DE LEITURA

EVENTS · HMAC · POST

EasyLiveChat liga para você
no instante em que algo acontece.

Webhooks são o canal push do EasyLiveChat — em vez de fazer polling à nossa API segundo um horário, você nos dá uma URL e fazemos POST de cada evento relevante em menos de um segundo. Cada requisição é assinada com HMAC-SHA256 para que seu servidor possa provar que veio mesmo da gente.

§ 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.

  • URLDeve 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.
  • EventosEscolha 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.
  • SecretGerado 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", 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_digestuse 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.

EventoDispara quandoFormato de dados
message.createdUma nova mensagem chega em uma conversa, independente de quem enviou (cliente recebendo, agente enviando, envio via API REST).{ conversationId, message }
conversation.updatedO status de uma conversa (OPEN · SNOOZED · CLOSED) ou seu assignedAgentId muda.{ conversationId, status?, assignedAgentId? }
audit.eventSempre 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.testVocê 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.