Webhooks d'événements
Recevez les événements de cycle de vie du tenant sur vos endpoints HTTPS, vérifiez la signature HMAC-SHA256, et construisez des consommateurs idempotents.
Les webhooks poussent les événements du tenant vers vos propres systèmes au fil de l'eau : Obexal envoie un POST JSON signé à chaque endpoint actif abonné à l'événement. Les endpoints se gèrent par tenant avec la permission tenant:manage, depuis la console ou l'API d'administration. Les exemples utilisent https://accounts.obexal.com ; remplacez-le par votre domaine personnalisé le cas échéant.
Événements disponibles
| Événement | Émis quand |
|---|---|
user.created | Un compte utilisateur est créé (inscription, activation d'invitation, provisioning) |
user.login | Un utilisateur se connecte avec succès |
user.deleted | Un compte utilisateur est supprimé |
S'abonner à tout autre nom d'événement est refusé à la création. Le catalogue est aussi renvoyé par GET /v1/admin/webhooks dans le champ knownEvents.
Créer un endpoint
curl -X POST https://accounts.obexal.com/v1/admin/webhooks \
-H "Authorization: Bearer $OBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.eu/obexal","events":["user.created","user.deleted"]}'{
"id": "wh_01hzx...",
"url": "https://hooks.example.eu/obexal",
"events": ["user.created", "user.deleted"],
"enabled": true,
"createdAt": "2026-07-02T09:15:00Z",
"secret": "f3a1c9...64 caractères hexadécimaux...b7d2"
}Le secret (une clé de signature de 256 bits) n'est renvoyé qu'une seule fois. Obexal le stocke chiffré et ne le réexpose jamais ; si vous le perdez, supprimez l'endpoint et créez-en un nouveau.
GET /v1/admin/webhooks liste vos endpoints (sans les secrets) ; DELETE /v1/admin/webhooks/{id} en supprime un.
La requête de livraison
Chaque livraison est un POST HTTP portant ces en-têtes :
| En-tête | Contenu |
|---|---|
Content-Type | application/json |
User-Agent | Obexal-Webhooks/1 |
X-Obexal-Event | Le nom de l'événement, par exemple user.created |
X-Obexal-Delivery | Un identifiant de livraison unique (hexadécimal) |
X-Obexal-Signature | sha256=<HMAC-SHA256 hexadécimal du corps brut> |
Le corps est une enveloppe signée :
{
"id": "9f2c4e8a1b7d3f6c9e0a5b2d4f8c1e7a",
"event": "user.created",
"tenantId": "ten_01hzx...",
"createdAt": "2026-07-02T09:15:00Z",
"data": {"userId": "usr_01hzx...", "email": "alice@example.eu"}
}Le contenu de data dépend de l'événement : user.created porte userId et email, user.login porte userId et tenantId, user.deleted porte userId.
Vérifier la signature
Calculez un HMAC-SHA256 du corps brut de la requête (octet par octet, avant tout parsing) avec le secret de votre endpoint, encodez-le en hexadécimal, et comparez-le à la valeur de l'en-tête après le préfixe sha256=. Rejetez la requête si les deux diffèrent.
# body.json = le corps brut de la requête, exactement tel que reçu
expected="sha256=$(openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" < body.json | awk '{print $NF}')"
received="$HTTP_X_OBEXAL_SIGNATURE"
[ "$expected" = "$received" ] && echo "signature OK" || echo "REJET"Dans votre code applicatif, utilisez une fonction de comparaison à temps constant pour comparer les deux signatures, et vérifiez avant de parser le JSON.
Restrictions sur les URL cibles (anti-SSRF)
Les cibles de webhook sont choisies par les admins du tenant : Obexal refuse donc de servir de relais contre une infrastructure interne.
- L'URL doit être en
https://. Lehttp://en clair,localhostet les IP privées littérales sont refusés à la création. - À la livraison, la connexion est recontrôlée après résolution DNS, juste avant le connect : les adresses de bouclage, privées, link-local, ULA, non spécifiées et multicast sont refusées. Cela ferme la fenêtre de DNS rebinding.
Garanties de livraison, honnêtement
La livraison est best-effort, au plus une fois par endpoint :
- Toute réponse
2xxacquitte la livraison. Tout le reste (ou un délai dépassé) déclenche une nouvelle tentative : 3 essais au total, avec un court backoff, puis la livraison est abandonnée et journalisée côté serveur. - Les livraisons passent par une file en mémoire, hors du chemin de requête. Si la file sature, l'événement est abandonné (et journalisé).
- La file n'est pas durable : les livraisons en attente lors d'un redémarrage du service sont perdues.
Traitez les webhooks comme des signaux, pas comme une source de vérité. Pour des garanties, réconciliez périodiquement avec l'API (par exemple GET /v1/admin/users) et utilisez l'export du journal d'audit.
Bonnes pratiques côté consommateur
- Soyez idempotent : dédupliquez sur
X-Obexal-Delivery(ou le champidde l'enveloppe). Une nouvelle tentative après un2xxperdu livrerait deux fois le même événement. - Acquittez vite : répondez
2xxen quelques secondes et traitez en asynchrone ; un handler lent consomme le budget de tentatives. - Tolérez l'inconnu : ignorez les types d'événements que vous ne traitez pas, pour que de futurs événements ne cassent pas votre consommateur.
- Tournez par remplacement : pour changer de secret, créez un nouvel endpoint, basculez votre vérification, puis supprimez l'ancien.