Webhook confiável não é “uma URL que recebe JSON”. É um contrato com autenticação, identificador estável, resposta rápida, novas tentativas e uma regra explícita para não processar o mesmo evento duas vezes.
O contrato do webhook
A Relya envia eventos de conexão, mensagem recebida, envio e mudança de estado. O corpo inclui tipo, instância, horário e dados. Os cabeçalhos carregam a prova de origem e o identificador da entrega:
x-relya-delivery-id: identificador estável;x-relya-timestamp: horário Unix em segundos;x-relya-event-type: categoria do evento;x-relya-signature: HMAC-SHA256 no formatov1=...;idempotency-key: o mesmo identificador da entrega.
{
"event": "messages_update",
"instanceId": "instancia-id",
"timestamp": "2026-07-14T12:00:00.000Z",
"data": {
"status": "delivered"
}
}
Validar a assinatura HMAC
Não confie apenas em IP ou em um campo dentro do JSON. Calcule o HMAC usando o segredo da instância e o corpo bruto recebido. A entrada assinada é:
TIMESTAMP.ID_DA_ENTREGA.CORPO_BRUTOSe o framework converter o JSON e depois serializar novamente, espaços e ordem de campos podem mudar. Capture os bytes brutos antes do parser.
import crypto from "node:crypto";
function webhookValido({ segredo, timestamp, deliveryId, corpoBruto, assinatura }) {
const agora = Math.floor(Date.now() / 1000);
if (Math.abs(agora - Number(timestamp)) > 300) return false;
const digest = crypto
.createHmac("sha256", segredo)
.update(`${timestamp}.${deliveryId}.${corpoBruto}`)
.digest("hex");
const esperado = Buffer.from(`v1=${digest}`);
const recebido = Buffer.from(assinatura || "");
return esperado.length === recebido.length &&
crypto.timingSafeEqual(esperado, recebido);
}A comparação em tempo constante reduz vazamento por diferença de tempo. Rejeite timestamps com diferença superior a cinco minutos para limitar replay. Ao trocar o segredo no painel, atualize o receptor de forma coordenada.
Responda rápido e processe depois
Seu endpoint deve validar, registrar a entrada e responder 2xx. Atualizar CRM, chamar IA, baixar mídia ou enviar e-mail deve ocorrer em uma fila interna. Se você fizer tudo antes de responder, um processamento lento pode gerar timeout e uma nova tentativa desnecessária.
- Receba o corpo bruto.
- Valide timestamp e assinatura.
- Inicie uma transação curta.
- Insira o
delivery-idcom restrição de unicidade. - Grave o evento na fila interna.
- Confirme a transação.
- Responda
204ou outro2xx. - Um worker processa o evento depois.
Responda depois que o evento estiver persistido, não logo ao abrir a conexão. Se o processo cair entre a resposta e a gravação, o provedor acredita que entregou e você perde o evento.
Como funcionam as novas tentativas
Na configuração padrão da Relya são até seis tentativas totais. A primeira é imediata. Erro de rede, timeout ou resposta fora de 200–299 agenda novas tentativas.
| Tentativa | Espera | Mesmo delivery ID? |
|---|---|---|
| 1 | Imediata | Sim |
| 2 | 5 segundos | Sim |
| 3 | 10 segundos | Sim |
| 4 | 20 segundos | Sim |
| 5 | 40 segundos | Sim |
| 6 | 80 segundos | Sim |
Depois da falha definitiva, o painel permite replay operacional. O identificador permanece estável. Isso é intencional: o consumidor pode reconhecer que se trata do mesmo evento.
Idempotência no consumidor
Imagine que seu servidor grava o pedido e começa a responder, mas a conexão cai antes de o provedor receber o 204. A Relya repete. Sem deduplicação, o CRM cria dois tickets ou a automação envia duas respostas.
A solução mais segura é uma coluna única para delivery_id. A inserção e a entrada na fila devem ocorrer na mesma transação. Se a restrição de unicidade falhar, responda 2xx: o evento já foi aceito anteriormente.
CREATE TABLE webhook_inbox (
delivery_id TEXT PRIMARY KEY,
event_type TEXT NOT NULL,
raw_body TEXT NOT NULL,
received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
processed_at TIMESTAMP NULL
);Isso implementa entrega “pelo menos uma vez” no transporte e efeito “uma vez” no seu domínio, dentro do limite da transação local.
Teste de falha antes do cliente real
- Cadastre uma URL HTTPS pública.
- Gere um evento e confirme assinatura e persistência.
- Derrube temporariamente o endpoint.
- Confirme tentativas e atrasos no painel.
- Restaure e verifique a entrega.
- Reenvie o mesmo payload e delivery ID.
- Confirme que seu sistema não repete o efeito.
- Teste assinatura inválida e timestamp antigo.
- Force processamento lento e confirme que a resposta HTTP continua rápida.
- Simule indisponibilidade da fila interna e não responda
2xxantes da gravação.
Esse teste vale mais que um “webhook recebido com sucesso” em ambiente perfeito. Para o fluxo de envio, leia o guia da API. O contrato completo está na documentação pública.
Escopo técnico
Cabeçalhos, assinatura e política de repetição descritos correspondem à Relya em 14/07/2026. Sempre valide a documentação do seu provedor: nomes e prazos não são universais.
