GUIAS PARA INTEGRADORES
Webhooks de assinatura: HMAC, eventos e retentativas
Como validar webhooks CLP com HMAC-SHA256, evitar processamento duplicado e sincronizar a assinatura com o ERP.
Configure um receptor da sua empresa
Use PUT /v1/account/webhook com JSON {"url":"https://seu-dominio.com/webhooks/clp","rotate_secret":false}. A URL precisa ser HTTPS pública, porta 443, sem redirecionamentos ou destinos internos. Guarde o segredo retornado no cofre do servidor. Cada empresa tem sua configuração; não descubra o segredo usando um account_id ainda não autenticado do corpo.
POST /v1/account/webhook/test enfileira um evento de teste. Consulte as últimas entregas em GET /v1/account/webhook/deliveries. Um 202 apenas confirma o enfileiramento. Não confunda o webhook do ERP com o webhook financeiro interno do Asaas.
Valide os bytes recebidos
Capture o corpo bruto antes de qualquer parser JSON. O HMAC é calculado sobre timestamp + "." + corpo bruto. Os cabeçalhos são X-Clp-Timestamp e X-Clp-Signature; o último usa prefixo sha256=. Comparação deve ser em tempo constante. O exemplo admite diferença de relógio de até cinco minutos; mantenha relógios sincronizados.
import { createHmac, timingSafeEqual } from 'node:crypto';
// Supply the original request bytes, before any JSON parser or body transformation.
// Resolve the secret from the receiver's account configuration, never from the body.
export function verifyClpWebhook({ rawBody, timestamp, signature, secret, nowMs = Date.now() }) {
if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || secret.length === 0) return false;
if (typeof timestamp !== 'string' || !/^\d{1,12}$/.test(timestamp)) return false;
if (typeof signature !== 'string' || !/^sha256=[0-9a-fA-F]{64}$/.test(signature)) return false;
if (!Number.isFinite(nowMs) || Math.abs(nowMs / 1000 - Number(timestamp)) > 300) return false;
const expected = createHmac('sha256', secret).update(timestamp + '.', 'utf8').update(rawBody).digest();
const supplied = Buffer.from(signature.slice(7), 'hex');
return supplied.length === expected.length && timingSafeEqual(supplied, expected);
}
// After authentication: validate JSON, match body.id/body.event to delivery headers,
// persist the event with a unique (account, event.id) constraint, then acknowledge 2xx.
// Download files in a durable background job. An HMAC check alone is not deduplication.
Reformatar JSON muda os bytes e invalida a assinatura. Em Express, por exemplo, a rota deve receber bytes brutos antes de um middleware JSON global. A autenticação acima não persiste eventos nem executa o negócio.
Persistência antes do sucesso
- Limite o tamanho do corpo, leia bytes brutos e valide HMAC e horário.
- Interprete o JSON autenticado. Use o
iddo corpo como identificador de entrega e confira sua correspondência comX-Clp-Delivery-Id; useeventdo corpo para decidir o tratamento. - Em uma transação, grave a entrega e um trabalho pendente, com restrição única por empresa e ID da entrega.
- Se a entrega já foi persistida, retorne sucesso sem duplicar o trabalho. Se o banco falhar, retorne erro para permitir nova tentativa.
- Responda 2xx depois de persistir, e faça download ou atualização do ERP em um trabalhador separado.
Use uma chave de negócio adicional, como empresa + envelope + ação, para que entregas distintas não arquivem duas vezes o mesmo resultado. Não prometa processamento exatamente uma vez só porque validou HMAC.
Quais eventos existem hoje?
envelope.completed: consultar e arquivar o resultado.envelope.expiring: antecipar a exportação antes do prazo.envelope.expired: encerrar a disponibilidade local de links.webhook.test: validar o receptor, sem concluir nenhum contrato.
Não há evento individual de leitura, aceite, recusa ou cancelamento nesta versão. Consulte o envelope para esses estados. Não dependa da ordem de chegada; consulte o estado atual e compare sequence quando disponível, para não regredir o estado do ERP.
Teste corpo alterado, segredo errado, horário vencido, entrega repetida, falha do banco e reinício do trabalhador. Uma entrega só deve aparecer como processada depois que o efeito no ERP estiver confirmado.
