Documentación

1. Publicar un evento

curl -X POST https://hookpulse.altixcode.com/api/v1/apps/APP_ID/events \
  -H "Authorization: Bearer hp_…" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "invoice.paid",
    "payload": { "id": "in_123", "amount": 4900 },
    "dedupeKey": "invoice-123-paid"
  }'

Responde tan pronto como el evento se registra y se pone en cola. Pasa un dedupeKey y un reintento de tu ejecutor de tareas no enviará el evento dos veces.

2. Qué recibe tu cliente

POST /their/endpoint
Content-Type: application/json
HookPulse-Signature: t=1700000000,v1=<base64 hmac>
HookPulse-Delivery-Id: dlv_…
HookPulse-Timestamp: 1700000000

{"id":"evt_…","type":"invoice.paid","createdAt":"…","data":{…}}

3. Verificación

Comparte esto con tus clientes. Los dos errores que todo el mundo comete son comparar con === e ignorar la marca de tiempo, y ambos fallan en silencio.

import { createHmac, timingSafeEqual } from 'node:crypto'

function verify(headers, rawBody, secret, toleranceSeconds = 300) {
  const header = headers['hookpulse-signature']
  const deliveryId = headers['hookpulse-delivery-id']
  const timestamp = Number(/t=(\d+)/.exec(header)?.[1])

  // Replay protection. Without this a captured request is valid forever.
  if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false

  const expected = createHmac('sha256', secret)
    .update(`${deliveryId}.${timestamp}.${rawBody}`)
    .digest('base64')

  return [...header.matchAll(/v1=([^,]+)/g)].some(([, presented]) => {
    const a = Buffer.from(presented)
    const b = Buffer.from(expected)
    return a.length === b.length && timingSafeEqual(a, b)
  })
}

Verifica contra el cuerpo raw (en bruto), antes de cualquier parseo de JSON. Volver a serializar altera los bytes e invalida cualquier firma.

4. Rotar un secreto

Rotar establece un nuevo secreto y mantiene válido el anterior durante el margen de tiempo que elijas. Firmamos con el nuevo de inmediato para incentivar la migración de tus clientes, pero un receptor que todavía verifique con el secreto anterior seguirá funcionando hasta que concluya ese margen.

5. El registro de entregas

Genera un token de portal en tu backend —con la misma estructura que la firma de un webhook, identificando a uno de tus clientes— y llama a la API del portal desde tu propia página de configuración. Cada consulta está restringida al suscriptor indicado en ese token, por lo que un cliente jamás podrá ver las cargas útiles de otro.

GET  https://hookpulse.altixcode.com/api/portal/APP_ID/deliveries
POST https://hookpulse.altixcode.com/api/portal/APP_ID/deliveries/DELIVERY_ID/retry
Authorization: Bearer <portal token>

O incrústalo

En los planes Pro y Scale, añade &lt;hookpulse-log&gt; en tu página de configuración en lugar de crear tu propia tabla. Mismo token, mismo alcance: simplemente renderiza el registro por ti en un shadow root para evitar conflictos con el CSS de tu página.

<script src="https://hookpulse.altixcode.com/embed/hookpulse-log.js"></script>
<hookpulse-log app-id="APP_ID" token="PORTAL_TOKEN" api-url="https://hookpulse.altixcode.com"></hookpulse-log>

Límites claros y honestos