Webhook

Un Webhook es una configuración que ejecuta automáticamente una acción predefinida cuando ocurre algo en un Space (por ejemplo, la creación o publicación de un Content). La acción es una de dos cosas: envía una petición HTTP a una URL externa (url) o ejecuta un Script dentro del Space (script). Se usa para integraciones con sistemas externos o para automatización. Por ejemplo, puede configurarse para llamar a su servidor de notificaciones interno cada vez que se publica un Content de producto, o para ejecutar tareas posteriores con un Script predefinido.

Se especifica exactamente uno de url y script. Especificar ambos, o dejar ambos vacíos, se rechaza. El Webhook es un recurso hijo de Space en CMA, y su ruta se basa en /spaces/{spaceId}/webhooks.

Estructura del recurso

A continuación se muestra la respuesta de consulta individual del Webhook "Notificación de cambio de producto". Junto con sys (propiedades del sistema), tiene campos de configuración como el destino del envío, los eventos suscritos y las condiciones de activación.

{
  "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": "Notificación de cambio de producto",
  "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"
}

Claves principales:

  • sys.id: identificador único del Webhook. Se inserta en {webhookId} de las rutas de consulta individual, modificación y eliminación.
  • url: URL de destino externa a la que se llama cuando ocurre el evento. Se especifica exactamente uno de este y script.
  • script: referencia al Script que se ejecuta en lugar de una llamada externa. Se especifica exactamente uno de este y url. No está presente en el ejemplo anterior. Se explica más abajo en url y script (exactamente uno).
  • runAs: identidad de usuario con la que se ejecuta script. Se explica más abajo en runAs.
  • topics: array que define qué eventos se suscriben. El formato se explica más abajo en topics.
  • filters: condiciones que realmente activan el Webhook entre los eventos suscritos. Se explica más abajo en filters.
  • transformation: configuración que modifica la forma de la petición saliente hacia url (método, cuerpo, etc.). Se explica más abajo en transformation.

Propiedades del sistema (sys)

Todo Webhook incluye sus propiedades de sistema comunes en el objeto sys. space, createdBy y updatedBy se incluyen con la forma Refer ({ "sys": { "id", "type": "Refer", "targetType" } }).

PropiedadTipoDescripción
idstringIdentificador único del recurso.
typestringTipo de recurso. Para Webhook siempre es "Webhook".
spaceRefer<Space>El Space al que pertenece este Webhook.
createdByRefer<User>Usuario que lo creó.
createdAtstring (date-time)Momento de creación.
updatedByRefer<User>Usuario que lo modificó por última vez.
updatedAtstring (date-time)Momento de la última modificación.
versioninteger (≥1)Versión del recurso. Aumenta en 1 con cada modificación.

Como el Webhook es un recurso de configuración, no tiene el concepto de publicación. A diferencia de Content o Content Type, no posee propiedades de estado de publicación como publish, archive o status, sino solo version para el seguimiento de cambios. Su activación y desactivación no se controlan mediante publicación, sino con el campo del cuerpo activate.

Propiedades del cuerpo

El cuerpo del Webhook (los valores de configuración que se envían al crear o modificar y que se devuelven en la respuesta) se compone de los siguientes campos.

CampoTipoObligatorioDescripción
namestring (1~64)Nombre del Webhook.
urlstring (url)URL de destino externa a la que se llama cuando ocurre el evento. Exactamente uno de este y script. Véase url y script (exactamente uno) más abajo.
scriptRefer<Script>Referencia al Script que se ejecuta en lugar de una llamada externa. Exactamente uno de este y url. Véase url y script (exactamente uno) más abajo.
runAsWebhookRunAsIdentidad de usuario con la que se ejecuta script. HookOwner (predeterminado) o EventUser. Véase runAs más abajo.
activatebooleanSi está activado. Si es false, no se ejecuta nada aunque ocurra el evento.
topicsstring[]Array de eventos a suscribir. Véase topics más abajo.
filtersFilter[]Array de condiciones de activación. Si se deja vacío, se activan todos los eventos suscritos. Véase filters más abajo.
headersWebhookHeader[] (0~30)Array de cabeceras HTTP que se incluyen en la llamada a url.
httpBasicUsernamestring (1~32)Nombre de usuario de autenticación HTTP Basic para la llamada a url.
httpBasicPasswordstring (1~32)Contraseña de autenticación HTTP Basic para la llamada a url. Es de solo escritura. No aparece en la respuesta.
transformationTransformationPersonaliza la petición saliente hacia url. Véase transformation más abajo.

Se especifica exactamente uno de los url y script marcados con △. Especificar ambos, o dejar ambos vacíos, se rechaza.

Cada elemento de headers se compone de key (obligatorio), value (obligatorio) y secret (opcional, boolean). Si deja secret en true, ese valor queda oculto en el registro de envío (véase WebhookLog más abajo). Aun así, al consultar este Webhook el valor aparece en claro. Lo único que se omite de la respuesta es httpBasicPassword, así que dé por supuesto que el valor puesto en una cabecera secret es visible para cualquier rol que pueda leer este Webhook y mantenga ese rol acotado.

topics

Cada elemento de topics tiene el formato {recurso}.{acción}. Por ejemplo: Content.Create, Content.Publish, Media.Create.

La acción es una de las siguientes, o *, que significa todas las acciones del recurso (por ejemplo, Content.*).

AcciónSignificado
AllTodas las acciones.
CreateCreación.
ReadConsulta.
EditEdición.
SaveGuardado (modificación). El evento de modificación es Save. No es Update.
DeleteEliminación.
PublishPublicación.
UnpublishAnulación de publicación.
ArchiveArchivado.
UnarchiveDesarchivado.

filters

filters es un array que acota qué condiciones activan realmente el Webhook entre los topics suscritos. Cada filtro tiene la siguiente forma.

{ "doc": "sys.contentType.sys.id", "op": "EQ", "value": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA" }
  • doc: ruta del campo a comparar. Es una de sys.id, sys.contentType.sys.id, sys.createdBy.sys.id o sys.updatedBy.sys.id.
  • op: operador de comparación. Es uno de EQ, NE, IN, NOT_IN, REGEX o NOT_REGEX.
  • value: valor de comparación. Para EQ, NE, REGEX y NOT_REGEX se da una cadena; para IN y NOT_IN, un array de cadenas.

Si se definen varios filtros, todos deben cumplirse para que se active (AND). Si se deja filters vacío, se activan todos los eventos de los topics suscritos.

transformation

transformation modifica la forma de la petición HTTP saliente hacia url (no se aplica a un Webhook que usa script). Si no se especifica, todo el payload del recurso sale tal cual con el POST predeterminado.

ClaveTipoDescripción
methodstringMétodo HTTP. Uno de GET, POST, PUT, DELETE o PATCH.
contentTypestringContent-Type del cuerpo de la petición. El cuerpo se serializa en ese formato (más abajo).
bodyobjectObjeto que compone el cuerpo a enviar mediante plantillas de JSON Pointer.
includeBodybooleanSi se envía también el cuerpo del recurso que dispara el evento.

En qué formato sale el cuerpo

contentType determina el formato de serialización del cuerpo. La comparación no distingue mayúsculas de minúsculas e ignora los parámetros como ;charset=…, y solo tiene en cuenta la parte inicial. Si no se especifica o el valor está vacío, se envía como application/json. Si includeBody es false o method es GET, no se envía cuerpo y en ese caso tampoco se añade Content-Type.

El cuerpo que envía un Webhook es siempre un objeto. La plantilla body es un objeto y, si no se define ninguna plantilla, sale tal cual todo el recurso que dispara el evento.

contentType declaradoContent-Type que sale realmenteCuerpo que sale
(ninguno)application/jsonJSON
application/jsonEl valor declarado tal cualJSON
application/x-www-form-urlencodedEl valor declarado tal cualproduct[sku]=TUMBLER-500&product[price]=24000
text/plainapplication/jsonJSON
Otros (text/xml, etc.)El valor declarado tal cualJSON

En text/plain no cabe un objeto, por lo que se envía corregido a un formato en el que sí cabe. La cabecera nunca indica algo distinto del cuerpo real. Si el destino necesita recibir el cuerpo como texto, contentType no lo resuelve, así que consulte el contrato del receptor.

form-urlencoded despliega los objetos como claves entre corchetes y los arrays como índices.

CuerpoClaves y valores desplegados
{ "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=

Las claves y los valores salen con codificación porcentual en UTF-8. La tabla anterior está en forma decodificada para mostrar la estructura de las claves. Aunque un valor contenga & o +, se transmite tal cual sin que se confunda con un separador de pares ni con un espacio.

La notación que despliega el anidamiento como claves entre corchetes es una convención muy extendida, no una especificación del formato en sí. Compruebe si el receptor restaura product[sku] como un objeto anidado y, si no lo hace, componga la plantilla body con claves planas.

Este es un ejemplo de transformation que envía el cuerpo en formato de formulario.

"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 dispara el evento un Content cuyo sku es TUMBLER-500 y cuyo price es 24000, el cuerpo sale como sku=TUMBLER-500&price=24000.

url y script (exactamente uno)

Cuando se activa un Webhook, realiza una de dos cosas. Si especifica url, envía una petición HTTP a esa URL externa (la forma de la petición se define mediante transformation, headers y httpBasic*). Si especifica script, no sale al exterior, sino que ejecuta un Script dentro del Space.

  • url: URL de destino externa (http/https). Los destinos bloqueados, como redes privadas o loopback, se rechazan.
  • script: el Refer al Script que se ejecuta.

Debe especificar exactamente uno de los dos. Especificar ambos, o dejar ambos vacíos, se rechaza, y el código devuelto difiere según la ruta (véase Errores).

"script": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } }

Cuando se activa, el Script se ejecuta con permisos delegados, y los permisos de recurso por sentencia no se vuelven a verificar en tiempo de ejecución. Lo que está permitido ya se verifica al crear el Script. Para el modelo detallado de ejecución y permisos, consulte Semántica de ejecución, restricciones y seguridad.

runAs

runAs define con qué identidad de usuario se ejecuta script. Esta identidad se convierte en el createdBy/updatedBy de cualquier recurso creado o modificado durante la ejecución, y el filtro createdBy: ":self" dentro de un Script también se resuelve según esta identidad. Es solo atribución, no un límite de permisos. Lo que puede hacer se determina mediante la verificación de permisos realizada al crear el Script.

ValorIdentidad de ejecución
HookOwnerEl usuario que creó el Webhook (sys.createdBy). El valor predeterminado.
EventUserEl usuario que provocó ese evento (cambio), es decir, el sys.updatedBy del recurso activado.

En un Webhook que usa solo url, runAs se ignora. Si no se especifica, es HookOwner.

WebhookLog

Cada vez que un Webhook intenta un envío queda un registro. Es de solo consulta y no tiene endpoints de creación, modificación ni eliminación. Su ruta es /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"
  }
}

Todos los valores están dentro de sys y no hay propiedades de cuerpo. Las claves sin valor se omiten de la respuesta.

Qué Webhook dejó el registro lo indica sys.createdBy. No es un usuario, sino el Refer de ese Webhook, y sys.updatedBy es ese mismo Webhook.

PropiedadTipoDescripción
idstringIdentificador único del registro.
typestringSiempre "WebhookLog".
spaceRefer<Space>El Space al que pertenece este registro.
requestIdstringIdentificador de seguimiento de este intento de envío.
statusCodeintegerCódigo de estado HTTP de la respuesta recibida.
errorsstring[]Lista de motivos de fallo. En los registros de un Webhook que envía a una URL siempre está vacía (es el código de estado el que informa del fallo). Solo los registros de un Webhook que ejecuta un Script mediante script recogen los mensajes de fallo de ese Script.
eventTypestringEs el nombre de la acción que provocó este envío (por ejemplo, Create o Publish). No recoge la forma Content.Create que se escribe en topics, sino solo la acción final.
urlstringURL de destino del envío.
requestAtstring (date-time)Momento en que se envió la petición.
responseAtstring (date-time)Momento en que se recibió la respuesta.
requestobjectLa petición enviada. Su estructura interna está más abajo. Se omite en la consulta de lista.
responseobjectLa respuesta recibida. Su estructura interna está más abajo. Se omite en la consulta de lista.
createdByRefer<Webhook>El Webhook que dejó este registro.
createdAtstring (date-time)Momento de creación del registro.
updatedByRefer<Webhook>Igual que createdBy.
updatedAtstring (date-time)Igual que createdAt.

request y response tienen cada uno las siguientes claves.

  • request: url (la URL de destino a la que se envió la petición) · method (el método HTTP) · headers (el mapa de cabeceras enviadas) · body (la cadena del cuerpo enviado).
  • response: url (la URL de la que se recibió la respuesta) · headers (el mapa de cabeceras recibidas) · body (la cadena del cuerpo recibido) · statusCode (el código de estado recibido).

Los registros de un Webhook que ejecuta un Script mediante script tienen otra forma. Como no hay dirección a la que enviar, no hay url, y el method de request queda fijado en "SCRIPT". El body de request contiene el payload que provocó ese envío, y el body de response, el valor que devolvió ese Script (o el mensaje de fallo).

El valor de una cabecera con secret activado se guarda oculto. El valor real no queda en el registro.

Los cuerpos largos se guardan recortados. La referencia es de 65.536 caracteres para el body de request y de 8.192 caracteres para el body de response. Si son más largos, se conservan el principio y el final y se omite la parte central, y en ese lugar se anota el número de caracteres omitidos. Si el cuerpo es JSON, solo se recortan de la misma manera los valores de cadena largos para no romper la estructura, así que las claves y los valores cortos se conservan tal cual.

El criterio que separa el éxito del fallo es distinto según la forma de integración. Un Webhook que envía a una URL tiene éxito cuando la respuesta es 2xx o 3xx. Un Webhook que ejecuta un Script mediante script tiene éxito cuando statusCode es menor que 400 y errors está vacío. Ese único veredicto determina a la vez el periodo de conservación de más abajo y la tasa de éxito del estado de envío.

La consulta de lista devuelve los registros sin request ni response. Es porque el valor predeterminado de select del endpoint de lista es -sys.response,-sys.request. Para ver también los cuerpos de la petición enviada y de la respuesta recibida, use la consulta individual o indique select usted mismo para sobrescribir ese valor predeterminado.

Los registros de los envíos correctos desaparecen al cabo de 1 hora y los de los envíos fallidos, al cabo de 3 días. Ningún campo de la respuesta contiene la hora de expiración: cuando llega el momento, el registro desaparece por sí solo. Los valores que haya que conservar más tiempo, guárdelos por separado en el servidor receptor o déjelos como Content desde el Script que se ejecuta mediante script.

Errores

Códigos que se encuentran al trabajar con Webhook. Para los códigos comunes a todos los recursos, consulte Errores comunes.

CódigoCondición
WGL400042La petición de creación (POST) o de modificación completa (PUT) indica url y script a la vez, o deja ambos vacíos.
WGL422061La petición de modificación parcial (PATCH) indica url y script a la vez, o deja ambos vacíos.
WGL422050El valor de url apunta a un destino bloqueado, como una red privada o loopback.

API

La URL base de todos los endpoints siguientes es https://cma.weegloo.com/v1, y se requiere un token Bearer que autentique en CMA en la cabecera Authorization. La modificación (PUT) y la modificación parcial (PATCH) deben enviar además la cabecera X-Weegloo-Version (el sys.version del recurso actual) para el control de concurrencia optimista.

  • Content: los datos del cuerpo que activan el Webhook.
  • Media: el recurso de archivo que puede activar el Webhook.
  • Script: el endpoint de backend declarativo que se ejecuta mediante script. Incluye el modelo de ejecución y permisos.
  • SpaceRole: la configuración de rol que contiene permisos como la ejecución de Script (Execute).