Skip to main content
Cada canal do Zazz Chat pode ter um webhook de saída: sempre que ocorre um evento assinado, o sistema faz um POST com o payload do evento na URL configurada, assinado com HMAC-SHA256.
A configuração do webhook é feita exclusivamente pelo sistema (interface web). Não há endpoint de API para criar ou alterar a configuração do webhook.

Configuração (pelo sistema)

1

Acesse a aba Webhook do canal

No painel, vá em Dashboard → Suporte → Canais, escolha o canal, abra Configurações e selecione a aba Webhook.
2

Ative e informe a URL

Habilite o webhook e informe a URL de destino (http:// ou https://). Recomendado: HTTPS.
3

Selecione os eventos

Marque quais eventos devem ser entregues (veja a lista abaixo).
4

Gere o secret

Clique em Gerar secret. Ele é exibido uma única vez — guarde-o para validar a assinatura das entregas. Gerar um novo secret invalida o anterior.
5

Teste

Use o botão de teste para disparar um ping imediato (eventType: "webhook.test") e conferir o status HTTP e o tempo de resposta do seu endpoint.
A mesma aba mostra o histórico de entregas (sucesso/falha, status HTTP, duração) e permite reenviar individualmente uma entrega que falhou.

Eventos

Hoje existem dois eventos de negócio que geram entrega, mais o ping de teste:
EventoDescrição
message_receivedMensagem recebida de um contato no canal
message_sentMensagem enviada pelo canal (agente, bot ou API)
webhook.testDisparado manualmente pelo botão de teste na aba Webhook
A interface permite marcar outros eventos na seleção (chat_created, chat_closed, chat_transferred, chat_assigned), mas eles ainda não geram entregas — não há disparo implementado para esses eventos no backend hoje. Marcá-los não tem efeito prático.
Configurações criadas antes da seleção de eventos existir (sem lista de eventos salva) recebem todos os eventos suportados, por compatibilidade.

Verificação de assinatura

Toda entrega inclui dois headers:
HeaderConteúdo
X-Webhook-Signaturesha256=<hex> — HMAC-SHA256 do texto "{timestamp}.{body}" usando o secret do canal
X-Webhook-TimestampTimestamp da assinatura em milissegundos (epoch)
Para validar: concatene o valor de X-Webhook-Timestamp, um ponto (.) e o corpo bruto da requisição, calcule o HMAC-SHA256 com o secret e compare com o valor após sha256=.
const crypto = require("crypto");
const express = require("express");

const app = express();
const SECRET = process.env.ZAZZ_WEBHOOK_SECRET;

// Importante: use o corpo BRUTO (raw), não o objeto já parseado
app.post("/webhook/zazz", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.header("X-Webhook-Signature") || "";
  const timestamp = req.header("X-Webhook-Timestamp") || "";

  const expected =
    "sha256=" +
    crypto
      .createHmac("sha256", SECRET)
      .update(`${timestamp}.${req.body.toString("utf8")}`)
      .digest("hex");

  const valid =
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!valid) return res.status(401).send("invalid signature");

  const event = JSON.parse(req.body.toString("utf8"));
  // ...processe o evento...

  res.sendStatus(200);
});
import hashlib
import hmac
import os

from flask import Flask, request

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

@app.post("/webhook/zazz")
def zazz_webhook():
    signature = request.headers.get("X-Webhook-Signature", "")
    timestamp = request.headers.get("X-Webhook-Timestamp", "")

    payload = timestamp.encode() + b"." + request.get_data()
    expected = "sha256=" + hmac.new(SECRET, payload, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return "invalid signature", 401

    event = request.get_json()
    # ...processe o evento...
    return "", 200
Rejeite entregas com timestamp muito antigo (ex.: mais de 5 minutos) para se proteger contra replay de requisições capturadas.

Entrega, timeout e retries

  • Sucesso: qualquer resposta 2xx. Responda rápido (idealmente 200 imediato) e processe o evento de forma assíncrona.
  • Timeout: 10 segundos por tentativa.
  • Retries automáticos: até 5 tentativas com backoff exponencial (2s, 4s, 8s, 16s entre as tentativas seguintes) para erros de rede, respostas 5xx e 429.
  • Sem retry: respostas 4xx (exceto 429) não são retentadas — normalmente indicam URL ou secret mal configurados no destino.
  • Reenvio manual: entregas com falha podem ser reenviadas pela aba Webhook do canal (histórico de entregas → reenviar).
Como há retries, seu endpoint pode receber o mesmo evento mais de uma vez. Use messageId (ou a combinação messageId + eventType) para deduplicar. Se o webhook for desabilitado ou o secret removido entre o enfileiramento e a entrega, o evento é descartado silenciosamente.