Webhooks
Webhooks permitem reagir a eventos do Workfuse em tempo real, sem polling. São um
recurso da plataforma: qualquer módulo pode emitir eventos, e você assina os
que interessam. Quando um evento ocorre, o Workfuse faz um POST assinado para a
URL que você registrou.
O catálogo de eventos cresce por módulo — veja, na doc de cada módulo, os eventos que ele emite. O mecanismo abaixo (configuração, envelope, assinatura, entrega) é o mesmo para todos.
Configurar
Webhooks são gerenciados no painel, em Configurações → Webhooks (perfil de administrador). Ao criar, você informa a URL de recebimento e os eventos que quer assinar (ou nenhum = todos). O secret de assinatura é exibido uma única vez na criação — guarde-o para validar as entregas.
Envelope do payload
Todo evento, de qualquer módulo, chega no mesmo envelope. O campo event
identifica o tipo e data carrega o conteúdo específico daquele evento:
{
"id": "evt_...",
"event": "<modulo>.<acao>",
"data": { "...": "..." }
}
Verificar a assinatura (HMAC)
Cada entrega vem com o header X-Workfuse-Signature no formato sha256=<hex>,
um HMAC-SHA256 do corpo cru usando o seu secret. Valide antes de confiar no
payload:
import crypto from "node:crypto";
function isValid(rawBody: string, signatureHeader: string, secret: string): boolean {
if (!signatureHeader) return false;
const expected = `sha256=${crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex")}`;
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader);
// timingSafeEqual exige buffers do mesmo tamanho.
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Dois detalhes que costumam quebrar a verificação:
- Inclua o prefixo
sha256=ao comparar (ou remova dos dois lados). - Calcule o HMAC sobre o corpo exatamente como recebido (os bytes crus) — não re-serialize o JSON, senão a assinatura não bate.
Código executável (Node, sem dependências): nodejs-webhook — receiver que valida a assinatura HMAC antes de processar o payload.
Entrega e retry
A entrega é best-effort e independente do processamento que originou o evento
(uma falha de entrega não desfaz nem afeta o que já aconteceu na plataforma).
Responda 2xx rapidamente e processe de forma assíncrona do seu lado.
Exemplo: eventos do módulo Correção
A título de exemplo, o módulo Correção de Redação emite:
| Evento | Quando dispara |
|---|---|
essay.corrected | A correção concluiu com sucesso. |
essay.failed | A correção falhou. |
Entrega correspondente:
{
"id": "evt_...",
"event": "essay.corrected",
"data": { "essayId": "clz...", "...": "..." }
}
Aqui você usaria o data.essayId para consultar a redação completa via
GET /essays/:id. Outros módulos seguem o mesmo
formato, mudando apenas event e data.