Configure webhooks no painel, receba entregas e verifique a assinatura HMAC – com exemplos para curl, Node.js e PHP.
Um webhook envia um pedido HTTP para o seu servidor assim que algo acontece no seu espaço de trabalho – por exemplo, quando uma página é publicada. Assim, não precisa de perguntar constantemente se algo mudou. Cada entrega é assinada, para que o seu servidor consiga distinguir mensagens autênticas de mensagens falsificadas.
Requisitos
- Proprietário ou admin no espaço de trabalho – o item de menu «Webhooks» só aparece para estas funções
- A partir do Creator Pro; no Creator Free aparece «Os webhooks estão disponíveis a partir do Creator Pro.»
- Até 5 webhooks no Creator Pro, 25 no Creator Agency, 100 no Enterprise (ajustável)
- Um servidor acessível num endereço https público
Os webhooks só se gerem no painel. Não podem ser criados nem alterados com chaves de API.
Criar um webhook
- Abra «Webhooks» no menu
- Clique em «+ Novo webhook»
- Introduza o URL de destino e escolha os eventos
- Opcional: introduza uma descrição e escolha o estado
- Clique em «Criar webhook»
- No passo «Concluído», copie o segredo de assinatura («Segredo de assinatura – visível só agora») e guarde-o no seu servidor – só é mostrado nesse momento
- Teste o webhook com «Testar». O resultado fica no cartão até ao teste seguinte: «Entregue com sucesso» ou «Não entregue» com o código HTTP (ou «sem resposta»), a duração e o início da resposta
- «URL de destino (https://…)»
- Até 2048 caracteres. Apenas https, sem credenciais no URL, sem endereços localhost e sem nomes internos como .local, .internal ou .lan. O nome tem de apontar para endereços IP públicos; o LinkHarbor volta a verificar isto antes de cada entrega.
- «Eventos»
- Pelo menos um evento. Cada grupo tem «Selecionar tudo» e «Desmarcar tudo».
- «Descrição»
- Opcional, até 255 caracteres – por exemplo, para que serve o webhook.
- «Estado»
- «Ativo» ou «Desativado». Os webhooks desativados não recebem entregas.
Eventos
| Evento | No painel | Quando | Conteúdo de data |
|---|---|---|---|
| page.published | Página publicada | Uma página foi publicada – no painel, através da API ou por um assistente de IA | page_id, version, checksum, published_by_user_id (null para chaves de API e assistentes de IA) |
| form.submitted | Formulário enviado | Um visitante enviou um formulário numa das suas páginas | form_id, form_name, submission_id, page_id, block_id, submitted_at, values (chave do campo → valor), consent (chave do consentimento → true/false) |
| domain.verified | Domínio verificado | Um domínio próprio foi verificado com sucesso | domain_id, hostname, verification_method, status |
| domain.disabled | Domínio desativado | Um domínio próprio foi desativado no painel (não num downgrade) | domain_id, hostname, status |
| webhook.test | – (botão «Testar») | Clica em «Testar» | message (texto no idioma da conta de quem faz o teste – não o use para comparações) |
form.submitted contém os valores exatamente como o visitante os enviou, mas nunca o endereço IP nem o browser. A entrega decorre em segundo plano: o visitante não espera por ela, e uma entrega falhada não impede a submissão.
Estrutura de uma entrega
O LinkHarbor envia cada entrega como POST com um corpo JSON e estes cabeçalhos:
| Cabeçalho | Conteúdo |
|---|---|
| Content-Type | application/json |
| X-LinkHarbor-Event | Nome do evento, por exemplo page.published |
| X-LinkHarbor-Delivery-Id | ID único desta entrega (UUID), idêntico a id no corpo |
| X-LinkHarbor-Timestamp | Momento do envio em segundos Unix |
| X-LinkHarbor-Signature | sha256= seguido da assinatura HMAC-SHA256 em hexadecimal |
| X-LinkHarbor-Signature-Version | v1 |
{
"id": "3f1c2a9e-8d4b-4a51-9f0e-6b7c1d2e3f40",
"event": "page.published",
"created_at": "2026-11-01T09:30:12+00:00",
"data": {
"page_id": 42,
"version": 8,
"checksum": "9b1e5c…",
"published_by_user_id": 7
}
} Os reenvios recebem um novo id e ainda os campos redelivery_of (o ID da entrega que é repetida) e delivery_root (o ID da primeira entrega desta cadeia).
Verificar a assinatura
Cada webhook tem o seu próprio segredo de assinatura (32 caracteres hexadecimais). Vê-o exatamente uma vez: no último passo, ao criar o webhook, e logo após «Segredo ↺». Copie-o de imediato para o seu servidor – o LinkHarbor não o volta a mostrar mais tarde. O LinkHarbor usa-o para calcular a assinatura desta forma:
- Formar a mensagem: timestamp, ponto, ID da entrega, ponto, corpo em bruto – ou seja, timestamp.delivery_id.body
- Calcular o HMAC-SHA256 sobre esta mensagem com o segredo de assinatura como chave
- Enviar o resultado como string hexadecimal com o prefixo sha256=
O seu servidor deve verificar a assinatura com uma comparação de tempo constante, recusar entregas com um timestamp com mais de 300 segundos e guardar os IDs das entregas pelo menos durante esse tempo, para reconhecer repetições. Importante: é assinado o corpo em bruto, exatamente como chega – não um JSON que tenha voltado a codificar.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.LH_WEBHOOK_SECRET;
const seen = new Set(); // em produção: uma base de dados ou Redis com expiração
app.post('/webhooks/linkharbor', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-LinkHarbor-Timestamp') || '';
const id = req.get('X-LinkHarbor-Delivery-Id') || '';
const sig = req.get('X-LinkHarbor-Signature') || '';
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
return res.status(400).send('timestamp');
}
const expected = 'sha256=' + crypto
.createHmac('sha256', SECRET)
.update(ts + '.' + id + '.')
.update(req.body) // corpo em bruto como Buffer
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(sig);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).send('signature');
}
const event = JSON.parse(req.body.toString('utf8'));
const key = event.delivery_root || event.id; // reconhecer reenvios da mesma cadeia
if (!seen.has(key)) {
seen.add(key);
// processar aqui, de preferência de forma assíncrona numa fila
}
res.sendStatus(200);
});
app.listen(3000); <?php
$secret = getenv('LH_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_LINKHARBOR_TIMESTAMP'] ?? '';
$id = $_SERVER['HTTP_X_LINKHARBOR_DELIVERY_ID'] ?? '';
$sig = $_SERVER['HTTP_X_LINKHARBOR_SIGNATURE'] ?? '';
if (!ctype_digit($ts) || abs(time() - (int)$ts) > 300) {
http_response_code(400);
exit('timestamp');
}
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $id . '.' . $body, $secret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
exit('signature');
}
$event = json_decode($body, true);
// Reconhecer entregas duplicadas por $event['delivery_root'] ?? $event['id'],
// e depois processar.
http_response_code(200); Testar o seu endpoint com curl
Este script envia uma entrega de teste corretamente assinada para o seu próprio servidor – útil antes de criar o webhook no LinkHarbor:
SECRET="o-seu-segredo-de-assinatura"
URL="https://example.com/webhooks/linkharbor"
TS=$(date +%s)
ID=$(uuidgen | tr 'A-Z' 'a-z')
BODY='{"id":"'"$ID"'","event":"webhook.test","created_at":"2026-11-01T09:30:12+00:00","data":{"message":"Este é um evento de teste para este webhook."}}'
SIG="sha256=$(printf '%s' "$TS.$ID.$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')"
curl -X POST "$URL" \
-H "Content-Type: application/json" \
-H "X-LinkHarbor-Event: webhook.test" \
-H "X-LinkHarbor-Delivery-Id: $ID" \
-H "X-LinkHarbor-Timestamp: $TS" \
-H "X-LinkHarbor-Signature: $SIG" \
-H "X-LinkHarbor-Signature-Version: v1" \
-d "$BODY" Resposta, timeout e reenvio
- Qualquer resposta com estado 2xx conta como sucesso; os redirecionamentos (3xx) não são seguidos
- O seu servidor tem 10 segundos para responder e no máximo 5 segundos para estabelecer a ligação
- Todos os webhooks de um evento partilham um total de 25 segundos; os que ainda não tiveram a sua vez nessa altura são marcados como falhados e podem ser reenviados
- O LinkHarbor não repete automaticamente as entregas falhadas
- Pode reenviar manualmente as entregas falhadas, mas não uma que entretanto tenha tido sucesso. O reenvio tem um novo id e um novo created_at
- São permitidas no máximo 3 tentativas por evento, incluindo a primeira entrega; depois disso, a entrega vai para «Dead Letters» e já não pode ser reenviada
Entregas no painel
«Entregas» abre o registo de um webhook. Tem três separadores:
- «Todas as entregas»
- Todas as entregas, com filtros por evento, estado («Sucesso (2xx)», «Erro do cliente (4xx)», «Erro do servidor (5xx)», «Falhou») e «Limite». «↺ Repetir» volta a enviar uma entrega falhada, desde que não exista uma posterior com sucesso. Um clique numa linha mostra o pedido, a resposta, o corpo e o JSON em bruto.
- «Reenvios»
- Cadeias de reenvios com o resultado «Recuperado» ou «Continua a falhar» e o número de tentativas.
- «Dead Letters»
- Entregas em que todas as tentativas permitidas falharam – a primeira entrega e os reenvios em conjunto. O separador é só de consulta: já não podem ser reenviadas. Verifique o seu endpoint; depois, os novos eventos são entregues normalmente. Se precisar dos dados, obtenha-os através da API.
Se um reenvio não for feito, o painel indica o motivo:
- «Não reenviado: este evento já falhou … vezes, não são possíveis mais tentativas. Encontra-o em “Dead Letters”.»
- As 3 tentativas foram todas usadas.
- «Não reenviado: este evento entretanto já foi entregue com sucesso.»
- Uma entrega posterior da mesma cadeia já teve sucesso.
Verificação no painel
Em «Verificação» encontra duas ajudas:
- «Kit de verificação»
- «Carregar kit» mostra o algoritmo, os nomes dos cabeçalhos, o formato da mensagem assinada e a tolerância recomendada de 300 segundos. O segredo propriamente dito não está incluído.
- «Verificar assinatura»
- Introduza o ID da entrega, o timestamp, a assinatura e o corpo do pedido em bruto de uma entrega recebida. O LinkHarbor indica então «Assinatura válida» ou «Assinatura inválida». Copie o timestamp e a assinatura dos cabeçalhos X-LinkHarbor-Timestamp e X-LinkHarbor-Signature. A verificação só funciona para entregas dos últimos 5 minutos; para entregas mais antigas, indica «Assinatura inválida» mesmo que a assinatura esteja correta.
Rodar o segredo, desativar, eliminar
- «Segredo ↺» gera um novo segredo de assinatura e mostra-o uma vez; o antigo deixa de funcionar de imediato, e o seu servidor tem de passar a usar o novo
- Em «Editar», defina o estado como «Desativado» para pausar as entregas
- O ícone do caixote do lixo elimina o webhook; até fechar a mensagem, pode cancelar com «Anular»
Quando passa para um plano mais pequeno, os webhooks acima do novo limite são desativados, e no Creator Free são todos desativados. Mantêm-se guardados; depois de um upgrade, o LinkHarbor volta a ativar automaticamente os webhooks desativados até ao limite do novo plano – incluindo os que desativou manualmente.
Também lhe pode ser útil
Não encontrou o que procura?
Diga-nos de que precisa e a nossa equipa de suporte ajuda-o pessoalmente.