---
title: "Gestión de webhooks"
description: "API de gestión de endpoints de webhook: crear, listar, actualizar, activar o desactivar, rotar el secreto y eliminar."
url: "https://docs.edugoverna.com/gestion/webhooks"
---
# Gestión de webhooks

Esta página cubre la API con que administras los endpoints de webhook de tu organización: crearlos, listarlos con su historial de entregas, actualizarlos, activarlos o desactivarlos, rotar su secreto y eliminarlos. El formato de las entregas, la firma y el catálogo de eventos están en la [guía de webhooks](/webhooks). {{ className: 'lead' }}

<Note>
  Estas rutas las llama **la organización dueña del endpoint**, con su sesión de
  consola o su API key en la cabecera `x-api-key`; una credencial de partner no
  sirve aquí. Las lecturas exigen el permiso `integrations.read`; las
  escrituras, `integrations.write`. Todo lo descrito también está disponible en
  la Consola, en **Integraciones → Webhooks**. El formato de las entregas, que
  es lo que consume tu plataforma, está en la [guía de webhooks](/webhooks).
</Note>

## El modelo de endpoint

Las respuestas devuelven el endpoint en su forma segura: nunca incluyen el secreto almacenado (solo el indicador `hasSecret`); el secreto en texto plano aparece únicamente en las respuestas de creación y de rotación.

### Propiedades

<Properties>
  <Property name="id" type="string">
    Identificador único del endpoint.
  </Property>
  <Property name="name" type="string">
    Nombre del endpoint, único dentro de la organización.
  </Property>
  <Property name="endpointUrl" type="string">
    URL de destino. Solo `https://`; se rechazan hosts locales, direcciones
    privadas, link-local y la IP de metadatos de nube
    (`OUTBOUND_WEBHOOK_URL_BLOCKED`).
  </Property>
  <Property name="enabledEvents" type="array de strings">
    Eventos suscritos, o `["*"]` para todos (incluidos los futuros). El comodín
    debe ir solo en el arreglo.
  </Property>
  <Property name="signatureHeader" type="string">
    Nombre de la cabecera donde viaja la firma. Se almacena en minúsculas; por
    defecto `x-edugoverna-signature`.
  </Property>
  <Property name="authType" type="string">
    Esquema de autenticación de las entregas. Por defecto `hmac_sha256`.
  </Property>
  <Property name="status" type="string">
    `active` o `disabled`. Un endpoint desactivado no recibe entregas.
  </Property>
  <Property name="organizationPartnerConnectionId" type="string | null">
    Conexión de partner a la que está asociado el endpoint, si corresponde.
  </Property>
  <Property name="hasSecret" type="boolean">
    Indica que el endpoint tiene un secreto configurado.
  </Property>
  <Property name="lastSuccessAt" type="timestamp">
    Última entrega exitosa.
  </Property>
  <Property name="lastFailureAt" type="timestamp">
    Última entrega fallida.
  </Property>
  <Property name="metadata" type="object | null">
    Metadatos libres definidos al crear el endpoint.
  </Property>
</Properties>

---

## Listar los endpoints {{ tag: 'GET', label: '/api/integrations/webhook-endpoints' }}

<Row>
  <Col>

    Devuelve todos los endpoints de webhook de la organización, en su forma
    segura.

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="GET" label="/api/integrations/webhook-endpoints">

    ```bash {{ title: 'cURL' }}
    curl https://app.edugoverna.com/api/integrations/webhook-endpoints \
      -H "x-api-key: {api_key}"
    ```

    ```js
    const { webhookEndpoints } = await fetch(
      'https://app.edugoverna.com/api/integrations/webhook-endpoints',
      { headers: { 'x-api-key': apiKey } },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta' }}
    {
      "webhookEndpoints": [
        {
          "id": "886313e1-3b8a-5372-9b90-0c9aee199e5d",
          "name": "erp-colegio",
          "endpointUrl": "https://erp.micolegio.cl/webhooks/edugoverna",
          "enabledEvents": ["*"],
          "signatureHeader": "x-edugoverna-signature",
          "authType": "hmac_sha256",
          "status": "active",
          "organizationPartnerConnectionId": null,
          "hasSecret": true,
          "lastSuccessAt": "2026-08-25T14:03:22.511Z",
          "lastFailureAt": null,
          "metadata": null
        }
      ]
    }
    ```

  </Col>
</Row>

---

## Crear un endpoint {{ tag: 'POST', label: '/api/integrations/webhook-endpoints' }}

<Row>
  <Col>

    Registra un endpoint nuevo. La respuesta `201` incluye el campo `secret` —
    el secreto en texto plano — **una única vez**; después de esta respuesta
    solo podrás [rotarlo](#rotar-el-secreto).

    Requiere el módulo `integrations` en el plan de la organización: sin él, la
    creación (y la [activación](#activar-o-desactivar)) responde `409
    INTEGRATIONS_MODULE_REQUIRED` en vez de registrar un endpoint al que nunca
    llegaría nada.

    ### Atributos obligatorios

    <Properties>
      <Property name="name" type="string">
        Nombre del endpoint (2–120 caracteres). Debe ser único en la
        organización; un nombre repetido responde `409` con
        `OUTBOUND_WEBHOOK_NAME_TAKEN`.
      </Property>
      <Property name="endpointUrl" type="string">
        URL de destino, solo `https://`.
      </Property>
    </Properties>

    ### Atributos opcionales

    <Properties>
      <Property name="secret" type="string">
        Secreto propio (10–500 caracteres). Si lo omites, el servidor genera
        uno con prefijo `whsec_` y lo devuelve en esta respuesta.
      </Property>
      <Property name="enabledEvents" type="array de strings">
        Eventos a suscribir. `["*"]` suscribe todos los eventos, actuales y
        futuros, y debe ir solo en el arreglo — mezclar el comodín con tipos
        explícitos es un error de validación. Si omites el campo — o envías una
        lista vacía — el valor almacenado es `["*"]`.
      </Property>
      <Property name="signatureHeader" type="string">
        Nombre de la cabecera de firma (2–120 caracteres). Se normaliza a
        minúsculas. Por defecto `x-edugoverna-signature`.
      </Property>
      <Property name="organizationPartnerConnectionId" type="string">
        UUID de una [conexión de partner](/gestion/conexiones) de tu
        organización, para asociar el endpoint a esa relación. Una conexión
        inexistente responde `404` con
        `OUTBOUND_WEBHOOK_CONNECTION_NOT_FOUND`.
      </Property>
      <Property name="authType" type="string">
        Esquema de autenticación. Por defecto `hmac_sha256`.
      </Property>
      <Property name="status" type="string">
        Estado inicial. Por defecto `active`.
      </Property>
      <Property name="metadata" type="object">
        Metadatos libres.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="POST" label="/api/integrations/webhook-endpoints">

    ```bash {{ title: 'cURL' }}
    curl https://app.edugoverna.com/api/integrations/webhook-endpoints \
      -H "x-api-key: {api_key}" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "erp-colegio",
        "endpointUrl": "https://erp.micolegio.cl/webhooks/edugoverna",
        "enabledEvents": [
          "consent.decision.recorded",
          "consent.revoked",
          "rights_request.created"
        ]
      }'
    ```

    ```js
    const endpoint = await fetch(
      'https://app.edugoverna.com/api/integrations/webhook-endpoints',
      {
        method: 'POST',
        headers: {
          'x-api-key': apiKey,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          name: 'erp-colegio',
          endpointUrl: 'https://erp.micolegio.cl/webhooks/edugoverna',
          enabledEvents: [
            'consent.decision.recorded',
            'consent.revoked',
            'rights_request.created',
          ],
        }),
      },
    ).then((r) => r.json())

    // Guarda endpoint.secret ahora: no vuelve a mostrarse.
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta (201)' }}
    {
      "id": "886313e1-3b8a-5372-9b90-0c9aee199e5d",
      "name": "erp-colegio",
      "endpointUrl": "https://erp.micolegio.cl/webhooks/edugoverna",
      "enabledEvents": [
        "consent.decision.recorded",
        "consent.revoked",
        "rights_request.created"
      ],
      "signatureHeader": "x-edugoverna-signature",
      "authType": "hmac_sha256",
      "status": "active",
      "hasSecret": true,
      "secret": "whsec_f47ac10b58cc4372a5670e02b2c3d479"
    }
    ```

  </Col>
</Row>

---

## Consultar un endpoint {{ tag: 'GET', label: '/api/integrations/webhook-endpoints/:id' }}

<Row>
  <Col>

    Devuelve un endpoint junto con sus intentos de entrega más recientes
    (hasta 50, del más nuevo al más antiguo) y estadísticas agregadas. Es la
    herramienta principal para diagnosticar entregas fallidas.

    Cada entrega del historial trae:

    <Properties>
      <Property name="eventType" type="string">
        El evento entregado.
      </Property>
      <Property name="sourceType" type="string">
        Tipo del recurso de origen.
      </Property>
      <Property name="sourceId" type="string">
        Identificador del recurso de origen.
      </Property>
      <Property name="attemptNumber" type="integer">
        Número del intento (los reintentos incrementan este valor).
      </Property>
      <Property name="status" type="string">
        `delivered` o `failed`.
      </Property>
      <Property name="responseCode" type="integer | null">
        Código HTTP con que respondió tu servidor, si quedó registrado; hoy
        llega `null` y, en los intentos fallidos, el código viaja dentro de
        `errorSummary`.
      </Property>
      <Property name="deliveredAt" type="timestamp | null">
        Cuándo se confirmó la entrega.
      </Property>
      <Property name="errorSummary" type="string | null">
        Resumen del error del intento fallido, por ejemplo
        `Webhook responded 500`.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="GET" label="/api/integrations/webhook-endpoints/:id">

    ```bash {{ title: 'cURL' }}
    curl https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d \
      -H "x-api-key: {api_key}"
    ```

    ```js
    const { endpoint, deliveries, stats } = await fetch(
      'https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d',
      { headers: { 'x-api-key': apiKey } },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta' }}
    {
      "endpoint": {
        "id": "886313e1-3b8a-5372-9b90-0c9aee199e5d",
        "name": "erp-colegio",
        "status": "active"
        // ... resto del endpoint
      },
      "deliveries": [
        {
          "id": "b1946ac9-2d5a-4a3c-8f2e-0d9c7e6f5a4b",
          "eventType": "consent.decision.recorded",
          "sourceType": "consent_request",
          "sourceId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
          "attemptNumber": 1,
          "status": "delivered",
          "responseCode": null,
          "deliveredAt": "2026-08-25T14:03:22.511Z",
          "nextRetryAt": null,
          "errorSummary": null,
          "createdAt": "2026-08-25T14:03:22.300Z"
        }
      ],
      "stats": { "total": 1, "ok": 1, "failed": 0 }
    }
    ```

  </Col>
</Row>

---

## Actualizar un endpoint {{ tag: 'PATCH', label: '/api/integrations/webhook-endpoints/:id' }}

<Row>
  <Col>

    Actualiza el nombre, la URL o la lista de eventos suscritos. Debes enviar
    al menos uno de los tres campos; los que omitas no cambian. La URL nueva
    pasa por la misma validación `https`/anti-SSRF que en la creación, y el
    nombre nuevo por la misma regla de unicidad.

    ### Atributos opcionales

    <Properties>
      <Property name="name" type="string">
        Nuevo nombre (2–120 caracteres).
      </Property>
      <Property name="endpointUrl" type="string">
        Nueva URL de destino, solo `https://`.
      </Property>
      <Property name="enabledEvents" type="array de strings">
        Nueva lista de eventos; reemplaza la lista completa. Rige la misma
        regla del comodín `["*"]`, y una lista vacía vuelve al comodín.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="PATCH" label="/api/integrations/webhook-endpoints/:id">

    ```bash {{ title: 'cURL' }}
    curl -X PATCH https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d \
      -H "x-api-key: {api_key}" \
      -H "Content-Type: application/json" \
      -d '{ "enabledEvents": ["*"] }'
    ```

    ```js
    await fetch(
      'https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d',
      {
        method: 'PATCH',
        headers: {
          'x-api-key': apiKey,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ enabledEvents: ['*'] }),
      },
    )
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta' }}
    {
      "webhookEndpoint": {
        "id": "886313e1-3b8a-5372-9b90-0c9aee199e5d",
        "enabledEvents": ["*"]
        // ... resto del endpoint
      }
    }
    ```

  </Col>
</Row>

---

## Activar o desactivar {{ tag: 'POST', label: '/api/integrations/webhook-endpoints/:id/status' }}

<Row>
  <Col>

    Cambia el estado del endpoint. Un endpoint `disabled` conserva su
    configuración y su historial, pero deja de recibir entregas hasta que lo
    reactives.

    ### Atributos obligatorios

    <Properties>
      <Property name="status" type="string">
        `active` o `disabled`.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="POST" label="/api/integrations/webhook-endpoints/:id/status">

    ```bash {{ title: 'cURL' }}
    curl -X POST https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d/status \
      -H "x-api-key: {api_key}" \
      -H "Content-Type: application/json" \
      -d '{ "status": "disabled" }'
    ```

    ```js
    await fetch(
      'https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d/status',
      {
        method: 'POST',
        headers: {
          'x-api-key': apiKey,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ status: 'disabled' }),
      },
    )
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta' }}
    {
      "webhookEndpoint": {
        "id": "886313e1-3b8a-5372-9b90-0c9aee199e5d",
        "status": "disabled"
        // ... resto del endpoint
      }
    }
    ```

  </Col>
</Row>

---

## Rotar el secreto {{ tag: 'POST', label: '/api/integrations/webhook-endpoints/:id/rotate-secret' }}

<Row>
  <Col>

    Genera un secreto nuevo (`whsec_…`) y lo devuelve **una única vez** en el
    campo `secret` de la respuesta. El secreto anterior deja de ser válido de
    inmediato: las entregas siguientes se firman solo con el nuevo, así que
    actualiza tu verificador antes o inmediatamente después de rotar.

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="POST" label="/api/integrations/webhook-endpoints/:id/rotate-secret">

    ```bash {{ title: 'cURL' }}
    curl -X POST https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d/rotate-secret \
      -H "x-api-key: {api_key}"
    ```

    ```js
    const { webhookEndpoint } = await fetch(
      'https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d/rotate-secret',
      { method: 'POST', headers: { 'x-api-key': apiKey } },
    ).then((r) => r.json())

    // Guarda webhookEndpoint.secret ahora: no vuelve a mostrarse.
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta' }}
    {
      "webhookEndpoint": {
        "id": "886313e1-3b8a-5372-9b90-0c9aee199e5d",
        "hasSecret": true,
        "secret": "whsec_a3bb189e8bf938889912ace4e6543002"
        // ... resto del endpoint
      }
    }
    ```

  </Col>
</Row>

---

## Eliminar un endpoint {{ tag: 'DELETE', label: '/api/integrations/webhook-endpoints/:id' }}

<Row>
  <Col>

    Elimina el endpoint y todo su historial de entregas. La operación es
    permanente; si solo quieres pausar las entregas, usa
    [Activar o desactivar](#activar-o-desactivar).

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="DELETE" label="/api/integrations/webhook-endpoints/:id">

    ```bash {{ title: 'cURL' }}
    curl -X DELETE https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d \
      -H "x-api-key: {api_key}"
    ```

    ```js
    await fetch(
      'https://app.edugoverna.com/api/integrations/webhook-endpoints/886313e1-3b8a-5372-9b90-0c9aee199e5d',
      { method: 'DELETE', headers: { 'x-api-key': apiKey } },
    )
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta' }}
    {
      "deleted": true
    }
    ```

  </Col>
</Row>
