Documentation

1. Publier un événement

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 réponse est renvoyée dès que l'événement est enregistré et mis en file d'attente. Renseignez une dedupeKey pour éviter qu'une nouvelle tentative de votre exécuteur de tâches n'envoie l'événement en double.

2. Ce que votre client reçoit

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. Vérification

Transmettez ceci à vos clients. Les deux erreurs courantes consistent à comparer avec === et à ignorer l'horodatage, et toutes deux échouent silencieusement.

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

Vérifiez par rapport au corps brut, avant tout parsing JSON. La resérialisation modifie les octets et fait échouer chaque signature.

4. Rotation d'un secret

La rotation définit un nouveau secret tout en conservant l'ancien valide pendant la période de votre choix. Nous signons immédiatement avec le nouveau pour encourager la migration de vos clients, mais un destinataire vérifiant encore avec l'ancien secret continue de fonctionner jusqu'à l'expiration du délai.

5. Le journal de livraison

Générez un jeton de portail dans votre backend — au même format qu'une signature de webhook, en désignant l'un de vos clients — et appelez l'API du portail depuis votre propre page de paramètres. Chaque requête est limitée à l'abonné indiqué dans ce jeton, empêchant ainsi vos clients de voir les charges utiles des autres.

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>

Ou intégrez-le

Avec les forfaits Pro et Scale, intégrez directement &lt;hookpulse-log&gt; dans votre page de paramètres plutôt que de concevoir votre propre tableau. Même jeton, même portée — le journal s'affiche directement, dans un shadow root pour éviter tout conflit avec le CSS de votre page.

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

Limites transparentes