Webhook
Un Webhook est une configuration qui exécute automatiquement une action prédéfinie lorsqu'un événement se produit dans un Space (par exemple, la création ou la publication d'un Content). L'action est l'une des deux suivantes : soit elle envoie une requête HTTP vers une URL externe (url), soit elle exécute un Script à l'intérieur du Space (script). Il sert à intégrer des systèmes externes ou à automatiser des tâches. Par exemple, vous pouvez le configurer pour appeler votre serveur de notification interne chaque fois qu'un Content de produit est publié, ou pour exécuter un traitement ultérieur au moyen d'un Script prédéfini.
Vous spécifiez exactement un parmi url et script. Spécifier les deux, ou laisser les deux vides, est rejeté. Le Webhook est une ressource enfant du Space dans CMA, et son chemin repose sur /spaces/{spaceId}/webhooks.
Structure de la ressource
Voici la réponse d'une consultation unitaire du Webhook « Notification de modification de produit ». Outre sys (propriétés système), il comporte des champs de configuration comme la cible d'envoi, les événements souscrits et les conditions de déclenchement.
{
"sys": {
"id": "3trmXRM3RqbgSnifyg7PWhk01Examp",
"type": "Webhook",
"space": { "sys": { "id": "HnQ32YiH", "type": "Refer", "targetType": "Space" } },
"createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
"createdAt": "2026-06-18T11:30:00.000Z",
"updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
"updatedAt": "2026-06-18T11:30:00.000Z",
"version": 1
},
"name": "Notification de modification de produit",
"filters": [
{ "doc": "sys.contentType.sys.id", "op": "EQ", "value": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA" }
],
"headers": [
{ "key": "X-Source", "value": "weegloo", "secret": false }
],
"httpBasicUsername": "dailywear",
"topics": ["Content.Create", "Content.Publish"],
"transformation": { "method": "POST", "contentType": "application/json", "includeBody": true },
"url": "https://api.dailywear.example/webhooks/products",
"activate": true,
"runAs": "HookOwner"
}Clés principales :
sys.id: identifiant unique du Webhook. Il s'insère dans le{webhookId}des chemins de consultation unitaire, de modification et de suppression.url: URL externe cible à appeler lorsqu'un événement se produit. Spécifiez exactement un parmi celui-ci etscript.script: référence vers le Script à exécuter au lieu d'un appel externe. Spécifiez exactement un parmi celui-ci eturl. Il est absent de l'exemple ci-dessus. Décrit dans url et script (exactement un) ci-dessous.runAs: identité d'utilisateur sous laquellescripts'exécute. Décrit dans runAs ci-dessous.topics: tableau qui détermine les événements à souscrire. La section topics ci-dessous en décrit le format.filters: conditions qui déclenchent réellement parmi les événements souscrits. Décrites dans filters ci-dessous.transformation: configuration qui modifie la forme (méthode, corps, etc.) de la requête sortante versurl. Décrite dans transformation ci-dessous.
Propriétés système (sys)
Chaque Webhook place ses propriétés système communes dans l'objet sys. Les champs space, createdBy et updatedBy se présentent sous la forme Refer ({ "sys": { "id", "type": "Refer", "targetType" } }).
| Propriété | Type | Description |
|---|---|---|
id | string | Identifiant unique de la ressource. |
type | string | Type de ressource. Pour un Webhook, toujours "Webhook". |
space | Refer<Space> | Le Space auquel appartient ce Webhook. |
createdBy | Refer<User> | Utilisateur qui a créé la ressource. |
createdAt | string (date-time) | Date et heure de création. |
updatedBy | Refer<User> | Dernier utilisateur ayant modifié la ressource. |
updatedAt | string (date-time) | Date et heure de la dernière modification. |
version | integer (≥1) | Version de la ressource. Augmente de 1 à chaque modification. |
Le Webhook étant une ressource de configuration, il n'a pas de notion de publication. Contrairement à un Content ou à un Content Type, il ne possède pas de propriétés d'état de publication telles que publish, archive ou status ; il n'a que version pour le suivi des modifications. Son activation et sa désactivation ne se contrôlent pas par la publication, mais par le champ de corps activate.
Propriétés du corps
Le corps du Webhook (les valeurs de configuration envoyées lors de la création ou de la modification et renvoyées dans la réponse) se compose des champs suivants.
| Champ | Type | Requis | Description |
|---|---|---|---|
name | string (1~64) | ✅ | Nom du Webhook. |
url | string (url) | △ | URL externe cible à appeler lorsqu'un événement se produit. Exactement un parmi celui-ci et script. Voir url et script (exactement un) ci-dessous. |
script | Refer<Script> | △ | Une référence vers le Script à exécuter au lieu d'un appel externe. Exactement un parmi celui-ci et url. Voir url et script (exactement un) ci-dessous. |
runAs | WebhookRunAs | Identité d'utilisateur sous laquelle script s'exécute. HookOwner (par défaut) ou EventUser. Voir runAs ci-dessous. | |
activate | boolean | ✅ | Indique s'il est activé. Si false, rien ne s'exécute même lorsqu'un événement se produit. |
topics | string[] | ✅ | Tableau des événements à souscrire. Voir topics ci-dessous. |
filters | Filter[] | ✅ | Tableau des conditions de déclenchement. Si vide, tous les événements souscrits déclenchent. Voir filters ci-dessous. |
headers | WebhookHeader[] (0~30) | ✅ | Tableau des en-têtes HTTP à envoyer avec l'appel url. |
httpBasicUsername | string (1~32) | Nom d'utilisateur pour l'authentification HTTP Basic de l'appel url. | |
httpBasicPassword | string (1~32) | Mot de passe pour l'authentification HTTP Basic de l'appel url. En écriture seule. N'apparaît pas dans la réponse. | |
transformation | Transformation | ✅ | Personnalisation de la requête sortante vers url. Voir transformation ci-dessous. |
Vous spécifiez exactement un des deux champs url et script marqués d'un △. Spécifier les deux, ou laisser les deux vides, est rejeté.
Chaque élément de headers se compose de key (requis), value (requis) et secret (facultatif, boolean). Si vous mettez secret à true, cette valeur reste masquée dans l'enregistrement d'envoi (voir WebhookLog ci-dessous). En revanche, lorsque vous consultez ce Webhook, la valeur apparaît en clair. Le seul champ absent de la réponse étant httpBasicPassword, considérez qu'une valeur placée dans un en-tête secret est visible pour les rôles capables de lire ce Webhook, et restreignez ces rôles.
topics
Chaque élément de topics suit le format {ressource}.{action}. Par exemple : Content.Create, Content.Publish, Media.Create.
L'action est l'une des suivantes, ou bien *, qui désigne toutes les actions de la ressource (par exemple Content.*).
| Action | Signification |
|---|---|
All | Toutes les actions. |
Create | Création. |
Read | Consultation. |
Edit | Édition. |
Save | Enregistrement (modification). L'événement de modification est Save. Ce n'est pas Update. |
Delete | Suppression. |
Publish | Publication. |
Unpublish | Dépublication. |
Archive | Archivage. |
Unarchive | Désarchivage. |
filters
filters est un tableau qui restreint, parmi les topics souscrits, les conditions qui déclencheront réellement le Webhook. Chaque filtre a la forme suivante.
{ "doc": "sys.contentType.sys.id", "op": "EQ", "value": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA" }doc: chemin du champ à comparer. L'une des valeurssys.id,sys.contentType.sys.id,sys.createdBy.sys.idousys.updatedBy.sys.id.op: opérateur de comparaison. L'une des valeursEQ,NE,IN,NOT_IN,REGEXouNOT_REGEX.value: valeur de comparaison. Une chaîne pourEQ,NE,REGEXetNOT_REGEX; un tableau de chaînes pourINetNOT_IN.
Si vous définissez plusieurs filtres, ils doivent tous être satisfaits pour déclencher (AND). Si filters est vide, tous les événements des topics souscrits déclenchent.
transformation
transformation modifie la forme de la requête HTTP sortante vers url (elle ne s'applique pas à un Webhook qui utilise script). Si elle n'est pas spécifiée, l'intégralité de la charge utile de la ressource part telle quelle avec la méthode POST par défaut.
| Clé | Type | Description |
|---|---|---|
method | string | Méthode HTTP. L'une des valeurs GET, POST, PUT, DELETE ou PATCH. |
contentType | string | Content-Type du corps de la requête. Le corps est sérialisé dans ce format (ci-dessous). |
body | object | Objet qui compose le corps à envoyer au moyen de modèles JSON Pointer. |
includeBody | boolean | Indique si le corps de la ressource déclencheuse est envoyé avec la requête. |
Dans quel format le corps est envoyé
contentType détermine le format de sérialisation du corps. La comparaison ignore la casse ainsi que les paramètres tels que ;charset=…, et ne considère que la partie initiale. Si la valeur n'est pas spécifiée ou si elle est vide, l'envoi se fait en application/json. Si includeBody vaut false ou si method est GET, aucun corps n'est envoyé et, dans ce cas, aucun en-tête Content-Type n'est ajouté.
Le corps qu'un Webhook envoie est toujours un objet. En effet, le modèle body est un objet et, si vous ne définissez pas de modèle, l'intégralité de la ressource déclenchée part telle quelle.
contentType déclaré | Content-Type réellement envoyé | Corps envoyé |
|---|---|---|
| (aucun) | application/json | JSON |
application/json | la valeur déclarée telle quelle | JSON |
application/x-www-form-urlencoded | la valeur déclarée telle quelle | product[sku]=TUMBLER-500&product[price]=24000 |
text/plain | application/json | JSON |
Les autres (text/xml, etc.) | la valeur déclarée telle quelle | JSON |
text/plain ne peut pas contenir un objet : l'envoi se fait donc après correction vers un format qui le peut. L'en-tête n'annonce jamais autre chose que le corps réel. Si la cible doit recevoir le corps sous forme de texte, contentType ne résout pas le problème ; vérifiez le contrat de la partie réceptrice.
form-urlencoded développe les objets en clés à crochets et les tableaux en index.
| Corps | Clés et valeurs développées |
|---|---|
{ "product": { "sku": "TUMBLER-500", "price": 24000 } } | product[sku]=TUMBLER-500&product[price]=24000 |
{ "tags": ["kitchen", "insulated"] } | tags[0]=kitchen&tags[1]=insulated |
{ "items": [{ "sku": "TUMBLER-500" }] } | items[0][sku]=TUMBLER-500 |
{ "memo": null } | memo= |
Les clés et les valeurs partent en UTF-8 avec un encodage-pourcent. Le tableau ci-dessus est présenté sous forme décodée afin de montrer la structure des clés. Même si une valeur contient & ou +, elle est transmise telle quelle, sans être interprétée comme un séparateur de paires ou comme une espace.
La notation qui développe l'imbrication en clés à crochets est une convention largement répandue, et non une spécification du format lui-même. Vérifiez si la partie réceptrice restaure product[sku] en objet imbriqué ; si elle ne le fait pas, composez le modèle body avec des clés plates.
Voici un exemple de transformation qui envoie un formulaire.
"transformation": {
"method": "POST",
"contentType": "application/x-www-form-urlencoded",
"includeBody": true,
"body": {
"sku": "{ /payload/fields/sku/ko-KR }",
"price": "{ /payload/fields/price/ko-KR }"
}
}Si le déclenchement provient d'un Content dont sku vaut TUMBLER-500 et price vaut 24000, le corps part sous la forme sku=TUMBLER-500&price=24000.
url et script (exactement un)
Lorsqu'un Webhook est déclenché, il effectue l'une de deux choses. Si vous spécifiez url, il envoie une requête HTTP vers cette URL externe (la forme de la requête est définie par transformation, headers et httpBasic*). Si vous spécifiez script, il ne sort pas vers l'extérieur mais exécute un Script à l'intérieur du Space.
url: URL externe cible (http/https). Les cibles bloquées, comme les réseaux privés ou le bouclage local, sont rejetées.script: leRefervers le Script à exécuter.
Vous devez spécifier exactement un des deux. Spécifier les deux, ou laisser les deux vides, entraîne un rejet, et le code renvoyé diffère selon le chemin (voir Erreurs).
"script": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } }Lorsqu'il est déclenché, le Script s'exécute avec des permissions déléguées, et les permissions de ressource par instruction ne sont pas revérifiées au moment de l'exécution. Ce qui est autorisé est déjà vérifié lors de la rédaction du Script. Pour le modèle détaillé d'exécution et de permissions, consultez Sémantique d'exécution, limites et sécurité de Script.
runAs
runAs détermine sous quelle identité d'utilisateur script s'exécute. Cette identité devient le createdBy/updatedBy de toute ressource créée ou modifiée pendant l'exécution, et le filtre createdBy: ":self" à l'intérieur d'un Script se résout également par rapport à cette identité. Il s'agit uniquement d'une attribution, pas d'une frontière de permissions. Ce qu'il peut faire est déterminé par le contrôle de permissions effectué lors de la rédaction du Script.
| Valeur | Identité d'exécution |
|---|---|
HookOwner | L'utilisateur qui a créé le Webhook (sys.createdBy). Valeur par défaut. |
EventUser | L'utilisateur à l'origine de cet événement (ce changement), c'est-à-dire le sys.updatedBy de la ressource déclenchée. |
Pour un Webhook qui n'utilise que url, runAs est ignoré. S'il n'est pas spécifié, il vaut HookOwner.
WebhookLog
Chaque fois qu'un Webhook tente un envoi, un enregistrement est conservé. Il est en lecture seule et ne dispose d'aucun endpoint de création, de modification ou de suppression. Son chemin est /spaces/{spaceId}/webhooks/{webhookId}/logs.
{
"sys": {
"id": "3trmXRM3RqbgSnifyg7PWhc01Exam",
"type": "WebhookLog",
"space": { "sys": { "id": "HnQ32YiH", "type": "Refer", "targetType": "Space" } },
"requestId": "3trmXRM3qWnLb7Vd1yPcYs04kKrjq",
"statusCode": 200,
"errors": [],
"eventType": "Create",
"url": "https://api.dailywear.example/webhooks/products",
"requestAt": "2026-06-18T11:35:00.100Z",
"responseAt": "2026-06-18T11:35:00.350Z",
"request": {
"url": "https://api.dailywear.example/webhooks/products",
"method": "POST",
"headers": { "Content-Type": "application/json", "X-Source": "weegloo" },
"body": "{\"sys\":{\"type\":\"Content\"}}"
},
"response": {
"url": "https://api.dailywear.example/webhooks/products",
"headers": { "Content-Type": "application/json" },
"body": "{\"ok\":true}",
"statusCode": 200
},
"createdBy": { "sys": { "id": "3trmXRM3RqbgSnifyg7PWhk01Examp", "type": "Refer", "targetType": "Webhook" } },
"createdAt": "2026-06-18T11:35:00.350Z",
"updatedBy": { "sys": { "id": "3trmXRM3RqbgSnifyg7PWhk01Examp", "type": "Refer", "targetType": "Webhook" } },
"updatedAt": "2026-06-18T11:35:00.350Z"
}
}Toutes les valeurs se trouvent dans sys et il n'y a pas de propriété au niveau du corps. Les clés sans valeur sont absentes de la réponse.
C'est sys.createdBy qui indique quel Webhook a laissé l'enregistrement. Ce n'est pas un utilisateur mais le Refer de ce Webhook, et sys.updatedBy désigne le même Webhook.
| Propriété | Type | Description |
|---|---|---|
id | string | Identifiant unique de l'enregistrement. |
type | string | Toujours "WebhookLog". |
space | Refer<Space> | Le Space auquel appartient cet enregistrement. |
requestId | string | Identifiant de suivi de cette tentative d'envoi. |
statusCode | integer | Code de statut HTTP de la réponse reçue. |
errors | string[] | Liste des motifs d'échec. Dans l'enregistrement d'un Webhook qui envoie vers une URL, elle est toujours vide (c'est le code de statut qui dit l'échec). Seul l'enregistrement d'un Webhook qui exécute un Script via script porte le message d'échec de ce Script. |
eventType | string | Le nom de l'action qui a provoqué cet envoi (par exemple Create, Publish). Ce n'est pas la forme Content.Create que l'on écrit dans topics : seule l'action finale y figure. |
url | string | URL cible de l'envoi. |
requestAt | string (date-time) | Date et heure d'envoi de la requête. |
responseAt | string (date-time) | Date et heure de réception de la réponse. |
request | object | La requête envoyée. Sa sous-structure figure ci-dessous. Absente de la consultation de liste. |
response | object | La réponse reçue. Sa sous-structure figure ci-dessous. Absente de la consultation de liste. |
createdBy | Refer<Webhook> | Le Webhook qui a laissé cet enregistrement. |
createdAt | string (date-time) | Date et heure de création de l'enregistrement. |
updatedBy | Refer<Webhook> | Identique à createdBy. |
updatedAt | string (date-time) | Identique à createdAt. |
request et response possèdent chacun les clés suivantes.
request:url(l'URL cible vers laquelle la requête est partie),method(la méthode HTTP),headers(la table des en-têtes envoyés),body(la chaîne du corps envoyé).response:url(l'URL depuis laquelle la réponse a été reçue),headers(la table des en-têtes reçus),body(la chaîne du corps reçu),statusCode(le code de statut reçu).
L'enregistrement d'un Webhook qui exécute un Script via script a une forme différente. Faute d'adresse d'envoi, il n'a pas de url, et le method de request est figé à "SCRIPT". Le body de request porte le payload qui a provoqué cet envoi, et le body de response porte la valeur renvoyée par ce Script (ou son message d'échec).
La valeur d'un en-tête dont secret est activé est stockée masquée. La valeur réelle ne subsiste pas dans l'enregistrement.
Un corps long est stocké abrégé. Le seuil est de 65 536 caractères pour le body de request et de 8 192 caractères pour celui de response. Au-delà, le début et la fin sont conservés et le milieu est omis, et le nombre de caractères omis est inscrit à cet emplacement. Si le corps est du JSON, seules les longues valeurs de chaîne sont abrégées de la même manière afin de ne pas briser la structure : les clés et les valeurs courtes subsistent telles quelles.
Le critère qui distingue le succès de l'échec diffère selon le mode d'intégration. Pour un Webhook qui envoie vers une URL, c'est un succès si la réponse est en 2xx ou en 3xx. Pour un Webhook qui exécute un Script via script, c'est un succès lorsque statusCode est inférieur à 400 et que errors est vide. Ce seul verdict détermine à la fois la durée de conservation ci-dessous et le taux de réussite de l'état d'envoi.
La consultation de liste renvoie l'enregistrement sans request ni response. C'est que la valeur par défaut de select sur l'endpoint de liste est -sys.response,-sys.request. Pour voir aussi le corps de la requête envoyée et de la réponse reçue, utilisez la consultation unitaire ou spécifiez vous-même select pour écraser cette valeur par défaut.
L'enregistrement d'un envoi réussi disparaît au bout d'une heure, celui d'un envoi en échec au bout de trois jours. Aucun champ de la réponse ne porte la date d'expiration : le moment venu, l'enregistrement disparaît de lui-même. Les valeurs qu'il faut conserver plus longtemps, stockez-les séparément sur le serveur destinataire ou consignez-les comme Content depuis le Script exécuté via script.
Erreurs
Ce sont les codes que l'on rencontre lorsque l'on manipule un Webhook. Pour les codes communs à toutes les ressources, consultez Erreurs communes.
| Code | Condition |
|---|---|
WGL400042 | À la création (POST) ou à la modification complète (PUT), url et script ont tous deux été spécifiés, ou tous deux laissés vides. |
WGL422061 | À la modification partielle (PATCH), url et script ont tous deux été spécifiés, ou tous deux laissés vides. |
WGL422050 | url désigne une cible bloquée, comme un réseau privé ou le bouclage local. |
API
L'URL de référence de tous les endpoints ci-dessous est https://cma.weegloo.com/v1, et un jeton Bearer authentifiant auprès de CMA est requis dans l'en-tête Authorization. La modification (PUT) et la modification partielle (PATCH) doivent inclure l'en-tête X-Weegloo-Version (le sys.version actuel de la ressource) pour le contrôle de concurrence optimiste.
Documents liés
- Content : les données de corps qui déclenchent un Webhook.
- Media : la ressource fichier qui peut déclencher un Webhook.
- Script : l'endpoint backend déclaratif à exécuter via
script. Inclut le modèle d'exécution et de permissions. - SpaceRole : le paramètre de rôle qui contient des permissions telles que l'exécution de Script (
Execute).
