Webhooks
Hoje o Canverly entrega um evento por webhook: lead.created, disparado a
cada envio aceito de um formulário. Os webhooks são
configurados no painel do site, na área do aplicativo Formulários.
Configurar
Seção intitulada “Configurar”- Informe a URL de destino. Tem de ser
https, sem usuário e senha na URL e sem porta de serviço. A URL é validada ao salvar e de novo a cada entrega. - Escolha o escopo. Um formulário específico, ou todos os formulários do site.
- Guarde o segredo. O segredo de assinatura é gerado pela plataforma e aparece uma única vez, na criação. Para trocá-lo, apague o webhook e crie outro.
Se o seu destino exige autenticação própria, dá para cadastrar um cabeçalho
(nome e valor). O valor é guardado cifrado e nunca é devolvido depois. Nomes
com o prefixo x-canverly- são recusados, porque esse prefixo carrega a
assinatura.
O que chega
Seção intitulada “O que chega”Um POST com corpo JSON e estes cabeçalhos:
| Cabeçalho | Conteúdo |
|---|---|
x-canverly-signature |
t=<unix>,v1=<hmac>: a assinatura (veja abaixo). |
x-canverly-timestamp |
O mesmo instante, em claro, só para diagnóstico. |
x-canverly-event |
O evento, hoje sempre lead.created. |
x-canverly-delivery |
Identificador da entrega, para deduplicar. |
Sem mapeamento configurado, o corpo é o evento com os campos event,
site_id, form_id, form_slug, lead_id, created_at (RFC 3339), data
(os campos preenchidos) e meta (página de origem, UTM e dados técnicos do
envio). Com mapeamento, o corpo tem a forma que o dono desenhou no painel, e a
pré-visualização do painel mostra o JSON exato que vai sair.
Verificar a assinatura
Seção intitulada “Verificar a assinatura”A assinatura é um HMAC-SHA256, em hexadecimal (64 caracteres), calculado
com o segredo sobre o texto <t>.<corpo>: o valor de t, um ponto e o corpo
cru, byte a byte, como chegou.
import crypto from 'node:crypto';
function assinaturaValida(cabecalho, corpoCru, segredo, toleranciaSeg = 300) { const partes = Object.fromEntries(cabecalho.split(',').map((p) => p.split('='))); const t = Number(partes.t); if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranciaSeg) return false; const esperado = crypto.createHmac('sha256', segredo).update(`${t}.${corpoCru}`).digest('hex'); const recebido = String(partes.v1 ?? ''); return recebido.length === esperado.length && crypto.timingSafeEqual(Buffer.from(recebido), Buffer.from(esperado));}O prefixo v1= existe para uma futura troca de algoritmo: durante a transição,
o destinatário poderá aceitar as duas versões.
Entrega e novas tentativas
Seção intitulada “Entrega e novas tentativas”- A entrega é pelo menos uma vez: uma resposta perdida faz o mesmo lead
chegar de novo. Use o
x-canverly-deliverypara descartar repetições. - Se o destino falhar, a plataforma tenta de novo com espera crescente (30 s, 1 min, 2 min, 4 min…, com teto de 6 horas entre tentativas), até 10 tentativas, cerca de 8,5 horas no total.
- Depois da última, a entrega fica marcada como falha e continua visível no painel.