Configura webhooks en el panel, recibe las entregas y verifica la firma HMAC, con ejemplos para curl, Node.js y PHP.
Un webhook envía una petición HTTP a tu servidor en cuanto pasa algo en tu espacio de trabajo, por ejemplo cuando se publica una página. Así no tienes que estar preguntando todo el rato si algo ha cambiado. Cada entrega va firmada para que tu servidor pueda distinguir los mensajes auténticos de los falsificados.
Requisitos
- Ser propietario o admin del espacio de trabajo; la opción de menú «Webhooks» solo aparece para estos roles
- Desde Creator Pro; en Creator Free aparece «Los webhooks están disponibles desde Creator Pro.»
- Hasta 5 webhooks en Creator Pro, 25 en Creator Agency y 100 en Enterprise (ajustable)
- Un servidor accesible en una dirección https pública
Los webhooks se gestionan solo en el panel. No se pueden crear ni cambiar con claves de API.
Crear un webhook
- Abre «Webhooks» en la navegación
- Haz clic en «+ Nuevo webhook»
- Introduce la URL de destino y elige los eventos
- Opcional: escribe una descripción y elige el estado
- Haz clic en «Crear webhook»
- En el paso «Listo», copia el secreto de firma («Secreto de firma: solo visible ahora») y guárdalo en tu servidor; solo se muestra ahora
- Prueba el webhook con «Probar». El resultado se queda en la tarjeta hasta la siguiente prueba: «Entregado correctamente» o «No entregado» con el código HTTP (o «sin respuesta»), la duración y el inicio de la respuesta
- «URL de destino (https://…)»
- Hasta 2048 caracteres. Solo https, sin credenciales en la URL, sin direcciones localhost y sin nombres internos como .local, .internal o .lan. El nombre debe apuntar a direcciones IP públicas; LinkHarbor lo vuelve a comprobar antes de cada entrega.
- «Eventos»
- Al menos un evento. Cada grupo tiene «Seleccionar todo» y «Deseleccionar todo».
- «Descripción»
- Opcional, hasta 255 caracteres; por ejemplo, para qué sirve el webhook.
- «Estado»
- «Activo» o «Deshabilitado». Los webhooks deshabilitados no reciben entregas.
Eventos
| Evento | En el panel | Cuándo | Contenido de data |
|---|---|---|---|
| page.published | Página publicada | Se publicó una página: en el panel, mediante la API o por un asistente de IA | page_id, version, checksum, published_by_user_id (null para claves de API y asistentes de IA) |
| form.submitted | Formulario enviado | Un visitante envió un formulario en una de tus páginas | form_id, form_name, submission_id, page_id, block_id, submitted_at, values (clave del campo → valor), consent (clave del consentimiento → true/false) |
| domain.verified | Dominio verificado | Un dominio propio se verificó correctamente | domain_id, hostname, verification_method, status |
| domain.disabled | Dominio desactivado | Un dominio propio se desactivó en el panel (no al bajar de plan) | domain_id, hostname, status |
| webhook.test | – (botón «Probar») | Haces clic en «Probar» | message (un texto en el idioma de la cuenta de quien inicia la prueba; no lo uses para comparar) |
form.submitted contiene los valores exactamente como los envió el visitante, pero nunca su dirección IP ni su navegador. La entrega se hace en segundo plano: el visitante no la espera, y una entrega fallida no impide el envío.
Estructura de una entrega
LinkHarbor envía cada entrega como POST con un cuerpo JSON y estos encabezados:
| Encabezado | Contenido |
|---|---|
| Content-Type | application/json |
| X-LinkHarbor-Event | Nombre del evento, por ejemplo page.published |
| X-LinkHarbor-Delivery-Id | ID único de esta entrega (UUID), igual que id en el cuerpo |
| X-LinkHarbor-Timestamp | Momento del envío en segundos Unix |
| X-LinkHarbor-Signature | sha256= seguido de la firma HMAC-SHA256 en 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
}
} Los reenvíos reciben un id nuevo y, además, los campos redelivery_of (el ID de la entrega que se repite) y delivery_root (el ID de la primera entrega de esta cadena).
Verificar la firma
Cada webhook tiene su propio secreto de firma (32 caracteres hexadecimales). Lo ves exactamente una vez: en el último paso al crear el webhook y justo después de «Secreto ↺». Cópialo enseguida en tu servidor, porque LinkHarbor no vuelve a mostrarlo. LinkHarbor lo usa para calcular la firma así:
- Construye el mensaje: timestamp, punto, ID de entrega, punto, cuerpo sin procesar, es decir, timestamp.delivery_id.body
- Calcula HMAC-SHA256 sobre este mensaje con el secreto de firma como clave
- Envía el resultado como cadena hexadecimal con el prefijo sha256=
Tu servidor debe comprobar la firma con una comparación de tiempo constante, rechazar las entregas con un timestamp de más de 300 segundos y guardar los ID de entrega al menos ese tiempo para reconocer las repeticiones. Importante: se firma el cuerpo sin procesar, tal como llega, no un JSON que hayas vuelto 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(); // en producción: una base de datos o Redis con caducidad
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) // cuerpo sin procesar 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; // reconocer los reenvíos de la misma cadena
if (!seen.has(key)) {
seen.add(key);
// procesar aquí, idealmente de forma asíncrona en una cola
}
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);
// Reconocer las entregas duplicadas por $event['delivery_root'] ?? $event['id'],
// y después procesar.
http_response_code(200); Probar tu endpoint con curl
Este script envía una entrega de prueba firmada correctamente a tu propio servidor; es útil antes de crear el webhook en LinkHarbor:
SECRET="tu-secreto-de-firma"
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 es un evento de prueba 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" Respuesta, tiempo de espera y reenvío
- Cualquier respuesta con estado 2xx cuenta como éxito; las redirecciones (3xx) no se siguen
- Tu servidor tiene 10 segundos para responder, y como máximo 5 segundos para establecer la conexión
- Todos los webhooks de un evento comparten un total de 25 segundos; los que no han tenido su turno para entonces se marcan como fallidos y se pueden reenviar
- LinkHarbor no repite automáticamente las entregas fallidas
- Puedes reenviar a mano las entregas fallidas, pero no una que entretanto se haya entregado correctamente. El reenvío tiene un id nuevo y un created_at nuevo
- Se permiten como máximo 3 intentos por evento, incluida la primera entrega; después acaba en «Mensajes fallidos» y ya no se puede reenviar
Entregas en el panel
«Entregas» abre el registro de un webhook. Tiene tres pestañas:
- «Todas las entregas»
- Todas las entregas, con filtros por evento, estado («Éxito (2xx)», «Error de cliente (4xx)», «Error de servidor (5xx)», «Fallidos») y «Límite». «↺ Reintentar» vuelve a enviar una entrega fallida, siempre que no haya una posterior correcta. Al hacer clic en una fila se ven la petición, la respuesta, el cuerpo y el JSON sin formato.
- «Repeticiones»
- Cadenas de reenvíos con el resultado «Recuperado» o «Sigue fallando» y el número de intentos.
- «Mensajes fallidos»
- Entregas en las que fallaron todos los intentos permitidos: la primera entrega y los reenvíos juntos. La pestaña es solo de consulta: ya no se pueden reenviar. Revisa tu endpoint; después, los eventos nuevos se entregan con normalidad. Si necesitas los datos, obtenlos mediante la API.
Si un reenvío no sale, el panel indica el motivo:
- «No reenviado: este evento ya falló … veces y no se permiten más intentos. Lo encontrarás en “Mensajes fallidos”.»
- Se han agotado los 3 intentos.
- «No reenviado: este evento ya se entregó correctamente.»
- Una entrega posterior de la misma cadena ya se completó con éxito.
Verificación en el panel
En «Verificación» encontrarás dos ayudas:
- «Kit de verificación»
- «Cargar kit» muestra el algoritmo, los nombres de los encabezados, el formato del mensaje firmado y la tolerancia recomendada de 300 segundos. El secreto en sí no se incluye.
- «Verificar firma»
- Introduce el ID de entrega, el timestamp, la firma y el cuerpo sin procesar de una entrega que hayas recibido. LinkHarbor te dice entonces «Firma válida» o «Firma no válida». Copia el timestamp y la firma de los encabezados X-LinkHarbor-Timestamp y X-LinkHarbor-Signature. La comprobación solo funciona con entregas de los últimos 5 minutos; con las más antiguas indica «Firma no válida» aunque la firma sea correcta.
Rotar el secreto, deshabilitar, eliminar
- «Secreto ↺» genera un secreto de firma nuevo y lo muestra una vez; el anterior deja de funcionar de inmediato y tu servidor tiene que usar el nuevo
- En «Editar», pon el estado en «Deshabilitado» para pausar las entregas
- El icono de la papelera elimina el webhook; hasta que cierres el mensaje, puedes cancelarlo con «Deshacer»
Al pasar a un plan más pequeño, se deshabilitan los webhooks que superan el nuevo límite, y en Creator Free todos. Se conservan; tras una mejora, LinkHarbor vuelve a activar automáticamente los webhooks deshabilitados hasta el límite del nuevo plan, incluidos los que deshabilitaste tú.
También te puede ser útil
¿No has encontrado nada?
Cuéntanos qué necesitas y nuestro equipo de soporte te ayudará personalmente.