Saltar al contenido
LinkProfit

Webhooks

Entregas de eventos firmadas: eventos disponibles, verificación de X-LinkProfit-Signature en JavaScript y Python, reintentos y estado de fallo.

Actualizado el 13 de agosto de 2026

Los webhooks envían eventos a tu endpoint como solicitudes POST en JSON, sin sondeo. Registra endpoints en el panel (Ajustes → Webhooks) o mediante POST /v1/webhooks; el secreto de firma se muestra una sola vez al crearlo.

Eventos

| Evento | Se dispara cuando | Disponible para | |---|---|---| | link.created / link.updated / link.deleted | Cambia un enlace, desde el panel, la API o una importación masiva | endpoints de espacio de trabajo y de partner | | domain.pending_ssl | El DNS está verificado y se está emitiendo el certificado | espacio de trabajo y partner | | domain.activated | El dominio ya sirve tráfico | espacio de trabajo y partner | | domain.error | Falló el aprovisionamiento; los detalles van dentro | espacio de trabajo y partner | | click.threshold | Se alcanza el 80% / 100% del límite mensual de clics medidos | espacio de trabajo y partner | | conversion.created | Se registra una conversión: un pedido, un registro o un objetivo personalizado atribuido a un clic | espacio de trabajo y partner | | workspace.subscribed | Un cliente paga la primera factura de una suscripción | solo partner | | workspace.past_due | Falla el pago de un cliente | solo partner | | workspace.suspended | Se suspende a un cliente (por impago o manualmente) | solo partner | | payment.succeeded | Se liquida el pago de un cliente, con el desglose de comisiones | solo partner | | payment.refunded | Se reembolsa un pago | solo partner |

La entrega

POST /your/endpoint HTTP/1.1
Content-Type: application/json
X-LinkProfit-Event: link.created
X-LinkProfit-Delivery: dlv_8Yq2…
X-LinkProfit-Signature: t=1755081600,v1=5f8a2c…
{
  "id": "dlv_8Yq2…",
  "type": "link.created",
  "created_at": "2026-08-13T10:00:00.000Z",
  "data": { "id": "lnk_…", "domain": "go.your-brand.com", "slug": "x7f2q1z", "url": "https://example.com" }
}

Responde con cualquier 2xx en menos de 10 segundos. Cualquier otra cosa —un tiempo de espera agotado incluido— cuenta como fallo y programa un reintento. Las entregas pueden llegar desordenadas y, rara vez, dos veces: deduplica por id.

Verificar la firma

La firma es un HMAC-SHA256 sobre `${t}.${rawBody}` con el secreto de tu endpoint, codificado en hexadecimal. Verifica siempre contra el cuerpo en bruto de la solicitud, antes de cualquier análisis de JSON, y rechaza las marcas de tiempo de más de 5 minutos.

import { createHmac, timingSafeEqual } from "node:crypto";

function verifySignature(secret, rawBody, header, toleranceSeconds = 300) {
  const parts = Object.fromEntries(
    header.split(",").map((piece) => piece.split("=", 2)),
  );
  if (!parts.t || !parts.v1) return false;
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false;

  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1);
  return a.length === b.length && timingSafeEqual(a, b);
}
import hashlib
import hmac
import time


def verify_signature(secret: str, raw_body: bytes, header: str, tolerance: int = 300) -> bool:
    parts = dict(piece.split("=", 1) for piece in header.split(",") if "=" in piece)
    timestamp, signature = parts.get("t"), parts.get("v1")
    if not timestamp or not signature:
        return False
    if abs(time.time() - int(timestamp)) > tolerance:
        return False

    expected = hmac.new(
        secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Reintentos y estado de fallo

El primer intento es inmediato. Los fallos se reintentan con un calendario fijo: 1 min → 5 min → 30 min → 2 h → 12 h. Tras el quinto reintento, el endpoint se marca como failing, deja de recibir eventos nuevos y su propietario recibe un correo.

Arregla el endpoint y vuelve a activarlo desde el panel o con PATCH /v1/webhooks/{id} y {"status": "active"}. Los eventos perdidos no se reenvían automáticamente: el registro de entregas (GET /v1/webhooks/{id}/deliveries o el panel) muestra todos los payloads para que puedas conciliar.

Una entrega correcta pone a cero la racha de fallos.

Pruebas

El botón Enviar prueba (o POST /v1/webhooks/{id}/test) entrega un evento real y firmado con "data": { "test": true } por la misma tubería que los eventos de producción, y el registro de entregas muestra la respuesta de tu endpoint.