LoonaDocs

Webhooks

Eventos y firmas

Reaccioná a cambios en el workspace sin hacer polling.

Crear un endpoint

Desde Configuración → Developers, un admin u owner puede registrar una URL y elegir a qué eventos suscribirse. Al crearlo, Loona genera un secreto (whsec_...) que se muestra una sola vez — se usa para verificar la firma de cada entrega.

Requiere el mismo entitlement que la API pública (plan Pro, Business o Custom). Ver Acceso por plan.

Eventos disponibles

EventoGrupo
client.createdClientes
client.updatedClientes
client.status_changedClientes
contact.createdContactos
deal.createdPipeline
deal.stage_changedPipeline
deal.wonPipeline
deal.lostPipeline
billing.createdFacturación
billing.paidFacturación
billing.overdueFacturación
task.createdTareas
task.completedTareas
work.createdProyectos
member.invitedEquipo

Headers de cada entrega

HeaderContenido
x-synera-signatureHMAC-SHA256 del payload, en hex.
x-synera-timestampUnix timestamp (segundos) usado en la firma. Rechazá entregas con un timestamp fuera de una ventana razonable (por ejemplo, 5 minutos) para evitar ataques de replay.
x-synera-delivery-idId estable de la entrega, igual en todos los reintentos. Usalo para deduplicar.
idempotency-keyEl mismo valor que x-synera-delivery-id, con el nombre que la mayoría de los frameworks ya reconoce.
x-synera-attemptNúmero de intento (1-based). Sólo diagnóstico.
x-synera-eventEl tipo de evento, para rutear sin parsear el body.

Verificar la firma

La firma es un HMAC-SHA256 sobre ${timestamp}.${body} (el timestamp entra en la firma, no sólo en un header, para que una entrega interceptada no se pueda reproducir más tarde con otro timestamp).

verify-webhook.js
const crypto = require("node:crypto")

function verifyLoonaWebhook(rawBody, signatureHeader, timestampHeader, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestampHeader}.${rawBody}`, "utf8")
    .digest("hex")

  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(signatureHeader, "hex"),
  )
}

Usá el body crudo

Calculá la firma sobre el body tal como llegó por la red, antes de parsearlo como JSON. Re-serializar el objeto parseado puede cambiar el orden de las claves o el espaciado y romper la comparación.

Reintentos

Hasta 5 intentos por entrega, con backoff de 1, 5, 15 y 45 minutos entre cada uno (~2 horas en total). Alcanza para cubrir un deploy de tu endpoint sin quedar reintentando contra algo que ya no existe.

Rotar el secreto

Rotar el secreto de un endpoint invalida las firmas del secreto anterior de inmediato — no hay un período de gracia con los dos secretos válidos a la vez. Es lo correcto si el secreto se filtró, pero significa que tenés que actualizar tu verificador en el mismo momento en que rotás.