Webhooks

Recevez en push les événements de cycle de vie des tenants et souscriptions.

Configurer un webhook

Lors de la création (ou édition) d'une Application, fournissez :

  • webhookUrl — endpoint HTTPS de votre app qui acceptera les POST.
  • webhookSecret — secret HMAC. Stocké hashé. Sert à signer chaque payload.

Événements émis

ÉvénementDéclencheur
tenant.createdPOST /api/tenants
tenant.status_changedPATCH /api/tenants/{slug}/status
subscription.createdPOST /api/tenants/{slug}/subscriptions
subscription.activatedTransition vers ACTIVE
subscription.trial_endingJob planifié, 3 jours avant trialEndsAt
subscription.trial_expiredJob planifié, après trialEndsAt
subscription.plan_changedPUT subscription avec nouveau planId
subscription.suspendedTransition vers SUSPENDED
subscription.cancelledDELETE subscription
subscription.expiredJob planifié, paiement en retard

Forme du payload

{
  "id": "uuid",
  "event": "subscription.plan_changed",
  "timestamp": "2026-05-06T10:15:00Z",
  "organizationId": "uuid",
  "appSlug": "crs",
  "data": {
    "tenantSlug": "casaride",
    "tenantId": "uuid",
    "subscriptionId": "uuid",
    "previousPlan": { "id": "uuid", "name": "Basic" },
    "newPlan":      { "id": "uuid", "name": "Pro" },
    "effectiveQuotas": { "maxVehicles": 30, "maxUsers": 4 }
  }
}

Le champ datavarie selon l'événement, mais contient toujours tenantSlug et subscriptionId.

Headers envoyés

X-SaaSes-Event:      subscription.plan_changed
X-SaaSes-Delivery:   <uuid>
X-SaaSes-Timestamp:  1746528000
X-SaaSes-Signature:  sha256=<hmac>

Vérifier la signature

Le HMAC est calculé comme :

HMAC-SHA256(webhookSecret, timestamp + '.' + rawBody)

Côté votre app, vérifiez la signature et rejetez les requêtes dont le timestamp diverge de plus de 5 minutes (anti-replay).

Exemple Node.js

import crypto from 'node:crypto'

function verify(req: Request, rawBody: string, secret: string): boolean {
  const signature = req.headers.get('X-SaaSes-Signature') ?? ''
  const timestamp = req.headers.get('X-SaaSes-Timestamp') ?? ''
  const now = Math.floor(Date.now() / 1000)

  if (Math.abs(now - Number(timestamp)) > 300) return false  // anti-replay

  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + rawBody)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  )
}

Livraison et retries

  • Méthode : POST, content-type application/json.
  • Timeout : 10 secondes.
  • SaaSes attend une réponse 2xx. Toute autre réponse déclenche un retry.
  • Backoff exponentiel : 1 min → 5 min → 30 min → 2 h → 8 h. 5 tentatives au total.
  • Après 5 échecs, l'événement est marqué FAILED et visible dans le journal d'audit. Replay manuel possible.

Soyez idempotent

SaaSes peut livrer le même événement plusieurs fois (en cas de timeout réseau). Utilisez le idde l'événement comme clé d'idempotence dans votre handler.