Documentazione

1. Pubblica 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"
  }'

La chiamata risponde non appena l'evento viene registrato e messo in coda. Se includi una dedupeKey, un eventuale tentativo di re-invio da parte del tuo job runner non invierà l'evento due volte.

2. Cosa riceve il tuo 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. Come verificarlo

Fornisci questo ai tuoi clienti. I due errori che fanno tutti sono confrontare con === e ignorare il timestamp: entrambi passano del tutto inosservati.

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)
  })
}

Effettua la verifica sul corpo grezzo, prima di qualsiasi parsing JSON. Una nuova serializzazione altera i byte e fa fallire ogni firma.

4. Ruotare un secret

La rotazione imposta un nuovo secret mantenendo valido il precedente per un intervallo a tua scelta. Firmiamo subito con quello nuovo per incentivare i tuoi clienti a migrare, ma i destinatari che verificano ancora con il vecchio secret continueranno a funzionare finché la finestra non si chiude.

5. Il log di consegna

Genera un token per il portale nel tuo backend — con la stessa struttura di una firma webhook, indicando uno dei tuoi clienti — e richiama l'API del portale dalla tua pagina delle impostazioni. Ogni richiesta è delimitata all'utente indicato in quel token, così nessun cliente potrà mai vedere i payload di un altro.

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>

Oppure integralo

Sui piani Pro e Scale, inserisci &lt;hookpulse-log&gt; nella pagina delle impostazioni invece di creare una tabella personalizzata. Stesso token, stesso ambito: esegue il rendering del log per te all'interno di una shadow root, evitando qualsiasi conflitto con il CSS della tua pagina.

<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>

Limiti trasparenti