AlertHub + Hookdeck: alertas de productos Polar sin servidor

AlertHub + Hookdeck: alertas de productos Polar sin servidor

Polar, Stripe y GitHub hablan su propio JSON. AlertHub habla ah_title, ah_level y ah_key. Hookdeck Event Gateway se pone en medio: acusa recibo al proveedor al instante, verifica la firma, reescribe el payload si hace falta y hace POST de una alerta limpia a AlertHub. No montas servidor.

No son alternativas. Hookdeck es fontanería de webhooks (cola, HMAC, reintentos, inspeccionar/reenviar). AlertHub es la capa humana: dashboard con historial más Slack, Telegram, ntfy, Gotify, email, Discord, Teams. Cambias el chat en Connections; Polar sigue hablando con Hookdeck.

Polar  →  Hookdeck (verificar → filtrar → transformar)  →  ingest AlertHub  →  dashboard + canales

El plan Developer gratuito de Hookdeck incluye 10.000 eventos/mes. Sobran para AlertHub Pro (1.000 alertas/mes).

Por qué poner Hookdeck delante de AlertHub

Apuntar Polar directo a https://api.thealerthub.com/sendAlert funciona si metes placeholders en la query — el mismo truco que Stripe y GitHub. Se queda corto en cuanto el payload es rico:

HuecoURL nativa a AlertHubCon Hookdeck
HMAC / Standard Webhooksingest no verifica la firma de PolarEl Source valida webhook-id / webhook-timestamp / webhook-signature
Archivar vs renombrarPolar no tiene evento product.archived — archivar es product.updated + data.is_archivedLa transformación pone critical o warning
Secreto en el panel de Polarah_key en la URL del endpointah_key vive en una variable de la transformación de Hookdeck
Polar reintenta / tú depurasPolar reintenta a ciegasCola, Issues, replay masivo

Usa Hookdeck cuando la fuente es un webhook SaaS (Polar, Stripe, Shopify). Sáltatelo para cron, bash, n8n o la API de ingestión que ya controlas.

Cómo encajan las piezas

  1. Polar hace POST de su evento nativo a la URL de un Source de Hookdeck. Polar recibe 200 al momento.
  2. Un Filter (opcional) tira todo lo que no sea product.updated.
  3. Una Transformation mapea el producto Polar a campos AlertHub e inyecta ah_key desde process.env.
  4. El Destination es POST https://api.thealerthub.com/sendAlert.
  5. AlertHub autentica el proyecto, guarda la fila y handle-new-alert reparte. Solo warning y critical salen del dashboard; info es historial. Ver niveles de alerta.

Orden de reglas: Filter y después Transform. Hookdeck aplica el filtro después de la transformación por defecto. Arrastra Filter encima de Transform, o el filtro buscará body.type en un body que ya solo tiene ah_*.

Configuración (una vez)

1. Proyecto AlertHub

En la app, abre el proyecto y copia la clave secreta (ah_key). Conecta Slack, Telegram, ntfy o Gotify en Connections para que el primer evento real tenga destino.

2. Source y Destination en Hookdeck

En el dashboard de Hookdeck:

  1. Crea un Source. Nombre polar. Copia la URL (https://hkdk.events/…).
  2. Crea un Destination. URL https://api.thealerthub.com/sendAlert, método POST. No pongas ah_key en esa URL.
  3. Crea una Connection de ese Source a ese Destination.

Autenticación del Source: Polar firma con Standard Webhooks. En Polar puedes poner tu propio secreto al crear el endpoint — genéralo, pégalo en Polar y en el campo de verificación del Source de Hookdeck. Si Polar genera el secreto al guardar, cópialo a Hookdeck antes de fiarte del tráfico de producción.

3. Webhook en Polar

En Polar: Settings → Webhooks → Add endpoint.

  • URL: la del Source de Hookdeck (no AlertHub).
  • Format: Raw.
  • Events: product.updated (añade product.created si también quieres altas de catálogo). Polar no emite product.archived.

4. Filtro (opcional)

En la connection, añade una regla Filter encima de Transform:

{
  "type": "product.updated"
}

Solo archivos (sin cambios de nombre/precio):

{
  "type": "product.updated",
  "data": { "is_archived": true }
}

Reintentos hacia AlertHub: 5xx y timeouts sí. No reintentes 401 (ah_key mala) ni 400 (falta título). Un 429 es la cuota mensual de AlertHub — martillear ingest no ayuda hasta el ciclo siguiente.

Guardar ah_key como variable de Hookdeck

No pegues la clave del proyecto AlertHub en Polar, en la URL del Destination ni como literal en el código de la transformación. Las transformaciones de Hookdeck tienen variables de entorno para secretos.

  1. Abre la connection → Transform → Create new transformation (o Editor si ya existe).
  2. En el editor, abre el desplegable Variables (arriba a la derecha del panel de código).
  3. Añade una variable llamada AH_KEY. Pega la clave secreta de AlertHub como valor. Guarda.
  4. En el JavaScript, léela con process.env.AH_KEY — nunca la interpoles en request.query ni en la URL del endpoint de Polar.

La variable pertenece a esa transformación. Quien pueda editarla la ve; la UI de webhooks de Polar no. Si rotas la clave en el proyecto AlertHub, actualiza solo AH_KEY en Hookdeck — la URL de Polar no cambia.

Los payloads de prueba del editor (Input) no meten las env vars en una captura que compartas. Tras Run, confirma en Output que sale ah_key y que no subes esa pestaña a git.

Copiar y pegar: Polar product.updated → AlertHub

Pega esto como handler de la transformación. Producto archivado → critical; cualquier otra actualización → warning.

addHandler("transform", (request) => {
  const event =
    typeof request.body === "string" ? JSON.parse(request.body) : request.body;
  const product = event.data || {};
  const archived = Boolean(product.is_archived);

  const price = (product.prices && product.prices[0]) || {};
  const priceLabel =
    price.amount_type === "free"
      ? "gratis"
      : price.price_amount != null
        ? `${(price.price_amount / 100).toFixed(2)} ${(price.price_currency || "").toUpperCase()}`
        : "n/d";

  request.body = {
    ah_key: process.env.AH_KEY,
    ah_source: "Polar",
    ah_level: archived ? "critical" : "warning",
    ah_title: archived
      ? `Polar: producto archivado — ${product.name || product.id}`
      : `Polar: producto actualizado — ${product.name || product.id}`,
    ah_description: archived
      ? `${product.name} (${product.id}) ya no está a la venta. Las suscripciones existentes conservan los beneficios.`
      : `ID: ${product.id}\nVisible: ${product.visibility}\nRecurring: ${product.is_recurring} (${product.recurring_interval || "one-time"})\nPrecio: ${priceLabel}`,
  };

  request.headers["content-type"] = "application/json";
  delete request.headers["webhook-signature"];
  delete request.headers["webhook-id"];
  delete request.headers["webhook-timestamp"];

  return request;
});

Borra las cabeceras de firma de Polar al reescribir el body: ya no coinciden, y AlertHub no las verifica.

Input de ejemplo (pégalo en Input de Hookdeck → Run)

{
  "type": "product.updated",
  "timestamp": "2026-09-01T10:12:00.000Z",
  "data": {
    "id": "prod_01ABC",
    "name": "Pro mensual",
    "is_archived": false,
    "is_recurring": true,
    "recurring_interval": "month",
    "visibility": "public",
    "prices": [
      {
        "amount_type": "fixed",
        "price_amount": 1000,
        "price_currency": "eur"
      }
    ]
  }
}

Salida esperada: ah_source: Polar, ah_level: warning, título con Pro mensual, ah_key desde la variable. Pon is_archived a true y Run otra vez — el nivel debe pasar a critical.

Nombra la transformación (p. ej. polar-product-to-alerthub), Confirm, y Save en la connection.

Qué deberías ver

Archiva o renombra un producto en Polar (vale el sandbox). En Hookdeck, Requests muestra el JSON de Polar; Events, el POST transformado. El feed de AlertHub recibe la fila. Los canales conectados solo reciben warning / critical.

Un Source de Hookdeck puede repartir a varios Destinations. Deja una segunda connection sin esta transformación si otro sistema sigue necesitando el body original firmado por Polar.

Ideas para sacarle más partido

  • Mismo patrón con Stripe / GitHub / Shopify. Un Source por proveedor, el mismo Destination AlertHub, una transform que mapee type → ah_title / ah_level. Las URLs con placeholders siguen bien para eventos triviales — constructor de webhooks.
  • subscription.canceled en warning, order.paid en info. Info no pagina; sigue en el dashboard.
  • ntfy o Gotify para guardia, Slack para el equipo. Añade ambos en Connections — guardia híbrida. Polar no necesita formato Discord/Slack; déjalo en Raw.
  • Issues de Hookdeck → AlertHub. Si Hookdeck no puede entregar a ingest, ese Issue puede ser una segunda connection que te pagine. Meta-alerta de la fontanería.

Preguntas frecuentes

¿Polar hace POST a Slack?
No. Polar → Hookdeck → AlertHub → tus Connections.

¿Puedo saltarme la transformación?
Sí: URL del Destination con query (ah_key, ah_title={{type}}) como en la guía de Stripe. Pierdes niveles archivo-vs-cambio y dejas ah_key en una URL. Vale para una prueba, no para cambios de catálogo.

¿Hookdeck reintentará los 429 de AlertHub todo el mes?
Configura 4xx como no reintentables salvo quizá un retraso corto en 429 si esperas subir de plan. No uses el default de «reintentar una semana» en un 401.

¿Sustituye el log de reintentos de Polar?
Hookdeck responde 200 a Polar rápido, así que los reintentos de Polar deberían quedarse quietos. La depuración está en Hookdeck (replay) y en AlertHub (historial).

Siguientes pasos

Crea el Source, pega su URL en Polar para product.updated, añade AH_KEY, pega la transformación y dispara un test desde el sandbox de Polar.

Relacionado: API de ingestión · Webhooks Stripe · Webhooks GitHub · Destino ntfy