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énement | Déclencheur |
|---|---|
tenant.created | POST /api/tenants |
tenant.status_changed | PATCH /api/tenants/{slug}/status |
subscription.created | POST /api/tenants/{slug}/subscriptions |
subscription.activated | Transition vers ACTIVE |
subscription.trial_ending | Job planifié, 3 jours avant trialEndsAt |
subscription.trial_expired | Job planifié, après trialEndsAt |
subscription.plan_changed | PUT subscription avec nouveau planId |
subscription.suspended | Transition vers SUSPENDED |
subscription.cancelled | DELETE subscription |
subscription.expired | Job 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-typeapplication/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é
FAILEDet 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.