Webhooks (gestion des souscriptions)
Endpoints pour gérer vos souscriptions webhook depuis l'API publique. Pour comprendre comment recevoir et vérifier les webhooks émis, voir ../../webhooks/public-api-webhooks.md.
Source : server/publicApi/routes/webhooks.ts. Scope requis : webhooks:manage sur tous les endpoints.
Vue d'ensemble
| Méthode | Path | Description |
|---|---|---|
GET | /webhooks | Liste paginée des souscriptions |
GET | /webhooks/:id | Détail d'une souscription |
POST | /webhooks | Crée une souscription (renvoie le signingSecret une seule fois) |
PATCH | /webhooks/:id | Met à jour (URL, events, headers, isActive) |
DELETE | /webhooks/:id | Supprime |
POST | /webhooks/:id/test | Envoie un test.ping pour valider le endpoint |
GET | /webhooks/:id/deliveries | Historique des livraisons (50 dernières par défaut, max 200) |
GET | /webhooks-events | Catalogue complet des events disponibles |
Catalogue d'événements
Liste complète exposée par GET /webhooks-events :
client.created, client.updated, client.deleted
contact.created, contact.updated, contact.deleted
website.created, website.updated, website.deleted
ticket.created, ticket.updated, ticket.deleted, ticket.status_changed
invoice.created, invoice.updated, invoice.paid, invoice.sent
quote.created, quote.updated, quote.accepted
task.created, task.updated, task.completed, task.deleted
event.created, event.updated, event.deleted
test.pingSource : server/publicApi/webhookEmitter.ts:50-59.
POST /webhooks — créer une souscription
Body
| Champ | Type | Requis | Bornes |
|---|---|---|---|
name | string | ✅ | 1-100 chars |
description | string | null | ❌ | ≤ 500 chars |
url | string (URL HTTPS) | ✅ | URL valide |
events | string[] | ✅ | ≥ 1 événement valide |
maxRetries | int | ❌ | 0-20 (défaut 5) |
timeoutMs | int | ❌ | 1 000-60 000 (défaut 10 000) |
headers | record<string,string> | ❌ | Headers HTTP à ajouter aux requêtes |
{
"name": "Sync ma CRM maison",
"description": "Reçoit les events client.* et invoice.paid",
"url": "https://my-crm.example.com/stormeo/webhook",
"events": ["client.created", "client.updated", "invoice.paid"],
"maxRetries": 5,
"timeoutMs": 10000,
"headers": { "X-My-Custom-Header": "value" }
}Réponse 201
{
"id": 42,
"name": "Sync ma CRM maison",
"url": "https://my-crm.example.com/stormeo/webhook",
"events": ["client.created", "client.updated", "invoice.paid"],
"isActive": true,
"maxRetries": 5,
"timeoutMs": 10000,
"createdAt": "2026-04-27T10:00:00.000Z",
"signingSecret": "whsec_<64 hex>",
"warning": "Copiez ce secret immédiatement. Il sera utilisé pour vérifier les signatures HMAC-SHA256."
}⚠️
signingSecretn'est renvoyé qu'à la création. Stockez-le dans un secret manager. En cas de perte, supprimez la souscription et créez-en une nouvelle.
GET /webhooks / GET /webhooks/:id
Lecture. Le signingSecret n'est jamais retourné par ces endpoints (sécurité).
{
"id": 42,
"agencyId": 7,
"name": "Sync ma CRM maison",
"url": "https://my-crm.example.com/stormeo/webhook",
"events": [...],
"isActive": true,
"lastDeliveryAt": "2026-04-27T10:05:00.000Z",
"lastDeliveryStatus": "success",
"failureCount": 0,
"maxRetries": 5,
"timeoutMs": 10000,
"headers": {},
"createdAt": "2026-04-27T10:00:00.000Z",
"updatedAt": "2026-04-27T10:05:00.000Z"
}PATCH /webhooks/:id
Mise à jour partielle. Champs autorisés : name, description, url, events, maxRetries, timeoutMs, headers, isActive.
Si vous repassez
isActive: trueaprès une désactivation, lefailureCountest remis à0.
Le
signingSecretne peut pas être modifié via PATCH. Pour rotation, supprimez et recréez.
DELETE /webhooks/:id
Suppression définitive de la souscription. Les webhook_deliveries historiques sont conservées (selon politique de rétention DB).
POST /webhooks/:id/test
Insère immédiatement un test.ping dans la queue. Le dispatcher (polling 10s) le pickera dans <15s.
Payload reçu sur votre URL
{
"event": "test.ping",
"data": {
"message": "Test ping from StormeoOS",
"timestamp": "2026-04-27T10:00:00.000Z",
"subscriptionId": 42
},
"deliveryId": 12345,
"timestamp": "2026-04-27T10:00:00.000Z"
}Avec headers signature HMAC — voir ../../webhooks/public-api-webhooks.md.
Réponse 200
{ "ok": true, "message": "Ping enregistré, sera envoyé dans <15s" }GET /webhooks/:id/deliveries
Historique des livraisons (succès + échecs). Triées par createdAt DESC.
Query params
| Param | Type | Description |
|---|---|---|
limit | int | Défaut 50, max 200 |
Réponse 200
[
{
"id": 12345,
"subscriptionId": 42,
"event": "client.created",
"status": "success",
"attempts": 1,
"responseStatus": 200,
"responseBody": "OK",
"createdAt": "2026-04-27T10:05:00.000Z",
"deliveredAt": "2026-04-27T10:05:01.000Z"
},
{
"id": 12344,
"subscriptionId": 42,
"event": "invoice.paid",
"status": "failed",
"attempts": 5,
"responseStatus": 503,
"lastError": "timeout",
"createdAt": "2026-04-27T09:30:00.000Z"
}
]Réponse en array brut, pas dans
{data, total, ...}(incohérence à corriger en v2).
Statuts de livraison
status | Description |
|---|---|
pending | En attente du prochain tick dispatcher |
success | Reçu (HTTP 2xx) |
failed | Échec après tous les retries |
retrying | En cours de retry |
Bonnes pratiques
- Endpoint receiver idempotent : utilisez le
deliveryIdpour dédupliquer côté serveur. StormeoOS peut envoyer le même event 2x en cas de timeout réseau côté retry. - Répondez rapidement (
<10spar défaut) avec un 2xx. Reportez le traitement lourd dans une queue interne. - Vérifiez la signature HMAC systématiquement avant de traiter. Voir ../../webhooks/public-api-webhooks.md.
- Whitelist d'IPs : les webhooks sortants partent toutes du serveur StormeoOS prod (
193.108.54.10). Vous pouvez restreindre votre endpoint à cette IP en plus de la signature.