> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corgea.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Automatiza notificaciones HTTP de Corgea a cualquier sistema externo

## ¿Qué son los webhooks?

Los webhooks permiten que Corgea envíe notificaciones HTTP en tiempo real a sistemas externos cuando se producen determinados eventos. En lugar de consultar continuamente la API para comprobar si hay novedades, el webhook envía los datos del evento al endpoint configurado en cuanto se produce.

### Beneficios clave

* **Notificaciones en tiempo real**: Recibe actualizaciones inmediatas cuando se detectan hallazgos de seguridad, cambia un estado o finaliza un escaneo
* **Automatización**: Inicia flujos de trabajo en herramientas externas como Slack, Zapier o aplicaciones personalizadas
* **Eficiencia**: No necesitas consultar la API; Corgea envía los datos cuando se producen los eventos
* **Flexibilidad**: Suscríbete solo a los eventos que te interesen y filtra por proyecto, estado o escaneo programado
* **Fiabilidad**: Los reintentos y el seguimiento de entregas integrados ayudan a que las notificaciones lleguen a su destino

### Tipos de eventos compatibles

Corgea admite webhooks para los siguientes eventos:

**Eventos de hallazgos:**

* `issue.status_changed` - Se activa cuando se actualiza el estado de un hallazgo (por ejemplo, abierto → corregido)
* `issue.assigned` - Se activa cuando se asigna un hallazgo a un miembro del equipo

**Eventos SLA:**

* `sla.violation` - Se activa cuando la tarea diaria de SLA encuentra uno o varios hallazgos de SAST o SCA que han superado su plazo de remediación o escalado (configurado por SLA en [Gestión de SLA](sla_management))

**Eventos de escaneo:**

* `scan.started` - Se activa cuando comienza un escaneo de seguridad
* `scan.completed` - Se activa cuando un escaneo termina con éxito
* `scan.failed` - Se activa cuando un escaneo detecta un error
* `scheduled_scan.daily_report` - Se activa cada día cuando finalizan los escaneos programados y resume los hallazgos nuevos de todas las ejecuciones de las últimas 24 horas. Consulta [Notificaciones](notifications#daily-scan-report) para ver el esquema del correo y del payload.

<Note>
  Los eventos del ciclo de vida del escaneo (`scan.started`, `scan.completed`, `scan.failed`) incluyen `data.message`, un breve resumen en texto sin formato que puedes utilizar en Slack, Zapier u otras herramientas de mensajería. También incluyen los campos de triaje `pull_request_id`, `scan_url`, `true_positive_count`, `project_name` y `status`.

  En Slack Workflow Builder (`Type = Slack` y `hooks.slack.com/triggers/...`), Corgea aplana **solo** `scan.started`, `scan.completed`, `scan.failed` y `webhook.test` como claves de nivel superior. No asignes campos `data.*` anidados en Slack, ya que provocan un error HTTP 400. Los demás tipos de eventos del mismo webhook conservan el sobre anidado. Consulta [Slack](slack).
</Note>

**Eventos de autenticación de usuarios:**

* `user.login` - Se activa cuando un usuario inicia sesión con éxito
* `user.login_failed` - Se activa cuando falla un intento de inicio de sesión de usuario

***

## Cómo funcionan los webhooks

### Ciclo de vida de un webhook

<Steps>
  <Step title="Ocurre un evento">
    Ocurre algo en Corgea (por ejemplo, finaliza un escaneo o cambia el estado de un hallazgo)
  </Step>

  <Step title="Se activa el webhook">
    El sistema identifica todos los webhooks suscritos a ese tipo de evento
  </Step>

  <Step title="Filtrado aplicado">
    Los filtros de proyecto, estado, escaneo programado, solo pull requests y filtros de finalización con hallazgos determinan si el webhook debe activarse
  </Step>

  <Step title="Se construye el payload">
    De forma predeterminada, se construye un sobre JSON estándar con los detalles del evento, o se utiliza la plantilla de cuerpo personalizada si está configurada. En Slack Workflow Builder (`Type = Slack` y una URL con `triggers/`), Corgea aplana el cuerpo HTTP solo para `scan.*` y `webhook.test`; los demás eventos permanecen anidados.
  </Step>

  <Step title="Solicitud HTTP POST">
    El payload se envía a la URL del webhook con cabeceras de seguridad
  </Step>

  <Step title="Lógica de reintentos">
    Si la petición falla, se producen intentos automáticos con retroceso exponencial
  </Step>

  <Step title="Entrega registrada">
    Todos los intentos se registran en el historial de entregas para la resolución de problemas
  </Step>
</Steps>

### Estructura del payload

De forma predeterminada, los payloads de webhook siguen un sobre anidado estándar (Zapier y Other). Los eventos del ciclo de vida del escaneo incluyen `data.message`, además de los campos de triaje:

```json webhook-payload.json theme={null}
{
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_type": "scan.completed",
  "timestamp": "2025-01-15T14:30:00.000Z",
  "data": {
    "company": "company-uuid",
    "scan_id": "scan-uuid",
    "run_id": "run-12345",
    "project": {
      "id": "project-uuid",
      "name": "My Application"
    },
    "project_name": "My Application",
    "branch": "feature/auth",
    "engine": "corgea-blast",
    "scan_type": "full",
    "status": "completed",
    "pull_request_id": "42",
    "scan_url": "https://app.corgea.app/project/project-uuid/?scan_id=scan-uuid",
    "true_positive_count": 5,
    "summary": {
      "total_issues": 12,
      "issues_with_fixes": 7,
      "issues_without_fixes": 5,
      "true_positive_count": 5,
      "true_positive_issues_with_fixes": 3
    },
    "message": "Scan completed for My Application (PR #42): 5 true-positive findings (3 with fixes). View: https://app.corgea.app/project/project-uuid/?scan_id=scan-uuid",
    "scheduled_scan_ids": ["scheduled-scan-uuid"],
    "processed_at": "2025-01-15T14:30:00.000Z",
    "created_at": "2025-01-15T14:00:00.000Z"
  }
}
```

`summary.total_issues` cuenta todos los hallazgos no eliminados, incluidos los falsos positivos y los de calidad del código. `true_positive_count` es el recuento de triaje utilizado en `message` y en los filtros de eventos de escaneo.

| Evento           | Ejemplo `data.message`                                                            |
| ---------------- | --------------------------------------------------------------------------------- |
| `scan.started`   | `Scan started for My Application (PR #42).`                                       |
| `scan.completed` | `Scan completed for My Application (PR #42): 5 true-positive findings. View: ...` |
| `scan.failed`    | `Scan failed for My Application (PR #42): Scanner timed out. View: ...`           |

`true_positive_count` cuenta los hallazgos de seguridad no eliminados que no son falsos positivos. Utiliza la misma lógica que la interfaz de escaneo: excluye `status=false_positive`, `hold_reason=false_positive` y `detected_by=code-quality`. El sufijo opcional `(N with fixes)` de los mensajes `scan.completed` solo cuenta las correcciones de esos hallazgos verdaderos positivos, no las de todos los hallazgos del escaneo.

<Tip>
  **Generador de flujos de trabajo de Slack:** cuando `Type = Slack` y la URL es `hooks.slack.com/triggers/...`, Corgea envía mediante POST un cuerpo **plano** para `scan.*` y `webhook.test` (`message`, `pull_request_id`, `scan_url`, `true_positive_count`, `company`, etc. en el nivel superior). Slack no admite asignar campos `data.*` anidados y devuelve HTTP 400. Los eventos que no son de escaneo en ese webhook **no** se aplanan. Consulta [Slack](slack).
</Tip>

**Ejemplo de payload `sla.violation`** (generado por la tarea diaria de Gestión de SLA):

```json sla-violation-payload.json theme={null}
{
  "event_id": "550e8400-e29b-41d4-a716-446655440001",
  "event_type": "sla.violation",
  "timestamp": "2025-01-15T14:30:00.000Z",
  "data": {
    "company": "1",
    "sla": {
      "id": 3,
      "rule_type": "code",
      "urgency": ["CR", "HI"],
      "remediation_days": 7,
      "escalation_days": 3
    },
    "notification_type": "remediation",
    "issue_kind": "SAST",
    "issue_count": 5,
    "summary_message": "Plain-text summary (same style as email body)...",
    "projects": [
      {
        "id": "project-uuid",
        "name": "My Application",
        "url": "https://app.corgea.com/project/project-uuid",
        "issue_count": 5,
        "urgency_counts": {
          "Critical": 2,
          "High": 3
        }
      }
    ]
  }
}
```

| Campo               | Descripción                                                                               |
| ------------------- | ----------------------------------------------------------------------------------------- |
| `notification_type` | `remediation` o `escalation`                                                              |
| `issue_kind`        | `SAST` o `SCA`                                                                            |
| `sla.rule_type`     | `code` (SAST) o `sca` (dependencia)                                                       |
| `projects`          | Una entrada por cada proyecto afectado; se utiliza en los filtros de proyecto del webhook |

Suscríbete a `sla.violation` en **Integraciones → Webhooks** o adjunta un webhook desde el formulario de [Gestión de SLA](sla_management); los webhooks existentes se suscriben automáticamente.

**Ejemplo de payload de `scheduled_scan.daily_report`**:

```json scheduled-scan-daily-report-payload.json theme={null}
{
  "event_id": "550e8400-e29b-41d4-a716-446655440002",
  "event_type": "scheduled_scan.daily_report",
  "timestamp": "2025-01-15T14:30:00.000Z",
  "data": {
    "title": "Corgea Daily Scan Report",
    "company": "1",
    "company_name": "Acme Security",
    "total_new_issues": 7,
    "message": "Daily scan report for Acme Security: 7 new issues detected across 2 scan runs.",
    "scan_runs": [
      {
        "scheduled_scan_name": "Weekly Production Scan",
        "project": "Payments API",
        "new_issue_count": 4,
        "scan_url": "https://www.corgea.app/scans/scan-uuid"
      }
    ]
  }
}
```

Los administradores de la empresa pueden controlar si este evento se envía a webhooks desde **Ajustes → Notificaciones → Predeterminados de la empresa**.

Si configuras **Custom Body** durante la configuración del webhook, Corgea envía tu objeto JSON renderizado en lugar de la estructura de payload predeterminada.

<Note>El evento `scheduled_scan.daily_report` utiliza el mismo sobre, pero tiene su propio esquema de `data`. Consulta [Ejemplos de payloads](#payload-examples) para ver la referencia completa.</Note>

### Características de seguridad

<AccordionGroup>
  <Accordion title="Verificación de firma HMAC" icon="shield-check">
    * Corgea genera automáticamente una clave secreta cuando creas un webhook
    * Cada solicitud incluye una cabecera `X-Corgea-Signature` con un hash HMAC-SHA256 del payload
    * Verifica la firma con tu clave secreta para asegurarte de que el webhook procede de Corgea
  </Accordion>

  <Accordion title="Cabeceras personalizadas" icon="key">
    * Incluir cabeceras requeridas por tu endpoint (por ejemplo, tokens de autenticación)
    * Configura cabeceras personalizadas al crear el webhook
  </Accordion>

  <Accordion title="HTTPS Obligatorio" icon="lock">
    * Todas las URLs de webhooks deben usar HTTPS (solo en los puertos 443 u 80)
    * Las URLs no deben incrustar credenciales en la URL
    * Se rechazan los destinos que resuelven a direcciones privadas, loopback, link-local, reservadas o multicast (protección SSRF)
    * Los operadores pueden restringir los hosts mediante la opción `WEBHOOK_ALLOWED_HOSTS`
  </Accordion>
</AccordionGroup>

### Lógica de reintento automático

<Info>Si la entrega por webhook falla, Corgea lo intenta automáticamente con la siguiente estrategia:</Info>

* **Intento inicial** + **2 intentos** = 3 intentos en total
* **Retroceso exponencial**: 2 segundos, 4 segundos entre intentos
* **Tiempo fuera**: 10 segundos por petición
* **Pausa automática**: Tras 10 fallos consecutivos, el webhook se pausa automáticamente

### Cabeceras enviadas con cada webhook

```text webhook-headers.txt theme={null}
Content-Type: application/json
X-Corgea-Event: issue.status_changed
X-Corgea-Delivery: <delivery-uuid>
X-Corgea-Timestamp: <unix-timestamp>
X-Corgea-Signature: <hmac-signature>
User-Agent: Corgea-Webhooks/1.0
```

***

## Configurar un webhook

<Frame>
  <img src="https://mintcdn.com/corgea/JgeRsjW5cn2yU52a/images/webhooks/webhooks_table.png?fit=max&auto=format&n=JgeRsjW5cn2yU52a&q=85&s=23c5fe597820ac3bdb57560ef29c3b9d" alt="Interfaz de gestión de webhooks que muestra la lista de webhooks configurados" style={{ borderRadius: '0.5rem' }} width="3108" height="798" data-path="images/webhooks/webhooks_table.png" />
</Frame>

### Requisitos previos

<Check>Permisos de administración o gestión de integración en tu cuenta de Corgea</Check>
<Check>Una URL de endpoint de webhook que acepta solicitudes POST</Check>
<Check>Endpoint HTTPS (necesario para la seguridad)</Check>

### Configuración paso a paso

<Steps>
  <Step title="Ve a Integraciones">
    * Abre **Integraciones** en Corgea
    * En **Integraciones de automatización**, abre **Webhooks**

    <Frame>
      <img src="https://mintcdn.com/corgea/bUShOerLaxHbdmO7/images/webhooks/webhooks_integrations.png?fit=max&auto=format&n=bUShOerLaxHbdmO7&q=85&s=e84b9d7d75569f68d644ada376ea16bf" alt="Integraciones de automatización mostrando Webhooks activos, con Slack y Zapier marcados como obsoletos" style={{ borderRadius: '0.5rem' }} width="1641" height="240" data-path="images/webhooks/webhooks_integrations.png" />
    </Frame>

    <Warning>
      Las filas independientes de **Slack** y **Zapier** están obsoletas. Usa **Webhooks** para nuevas configuraciones. Las integraciones existentes de Slack/Zapier siguen funcionando — abre **Ver todo** para probarlas o eliminarlas.
    </Warning>
  </Step>

  <Step title="Configurar ajustes básicos">
    <Frame>
      <img src="https://mintcdn.com/corgea/bUShOerLaxHbdmO7/images/webhooks/create_webhook_basic.png?fit=max&auto=format&n=bUShOerLaxHbdmO7&q=85&s=64b04b74bc95600f1c4e58c9a8b94398" alt="Crear un formulario de webhook mostrando los campos Nombre, URL del Webhook y Tipo" style={{ borderRadius: '0.5rem' }} width="1107" height="433" data-path="images/webhooks/create_webhook_basic.png" />
    </Frame>

    * **Nombre**: Una etiqueta clara (por ejemplo, "Notificaciones de Slack" o "Alertas de escaneo de producción")
    * **URL de Webhook**: Tu endpoint HTTPS
    * **Tipo**:
      * `Slack` — Slack Workflow Builder o Incoming Webhooks
      * `Zapier` — Zapier Catch Hooks
      * `Other` — endpoints personalizados

    <Tip>
      Para Slack, utiliza preferentemente una URL de Workflow Builder (`hooks.slack.com/triggers/...`). Corgea aplana `scan.*` y `webhook.test` en esos destinos: asigna el campo `message` del nivel superior, no `data.*`. Los eventos que no son de escaneo permanecen anidados. Los Incoming Webhooks (`hooks.slack.com/services/...`) se rechazan salvo que configures un Custom Body cuyo JSON renderizado tenga una cadena `text` no vacía en el nivel superior. `{"text": "{{message}}"}` solo se admite en eventos del ciclo de vida del escaneo y `scheduled_scan.daily_report`. Consulta [Slack](slack).
    </Tip>
  </Step>

  <Step title="Suscríbete a los eventos">
    <Frame>
      <img src="https://mintcdn.com/corgea/bUShOerLaxHbdmO7/images/webhooks/create_webhook_events.png?fit=max&auto=format&n=bUShOerLaxHbdmO7&q=85&s=dffd5f8c779ea28405555be193aae289" alt="Formulario de suscripciones a eventos con opciones para hallazgos, SLA, escaneos, usuarios y escaneos programados" style={{ borderRadius: '0.5rem' }} width="1089" height="761" data-path="images/webhooks/create_webhook_events.png" />
    </Frame>

    Activa los eventos que te interesan. Puedes seleccionar más de uno:

    * Estado del hallazgo modificado
    * Hallazgo asignado
    * Violación del SLA (`sla.violation`)
    * Escaneo iniciado / completado / fallido
    * Inicio de sesión de usuario / Inicio de sesión fallido
    * Informe diario de escaneos programados (`scheduled_scan.daily_report`)
  </Step>

  <Step title="Configurar filtros (opcional)">
    <Frame>
      <img src="https://mintcdn.com/corgea/bUShOerLaxHbdmO7/images/webhooks/create_webhook_scopes.png?fit=max&auto=format&n=bUShOerLaxHbdmO7&q=85&s=b77a4be9aeb754e6b411ca55714f84f6" alt="Alcance del proyecto, Ámbito de escaneo programado, cabeceras personalizadas y secciones de cuerpo personalizadas" style={{ borderRadius: '0.5rem' }} width="1089" height="594" data-path="images/webhooks/create_webhook_scopes.png" />
    </Frame>

    **Filtros de eventos de escaneo** (para eventos `scan.*`)

    * **Solo escaneos de pull request / merge request** — omite los escaneos sin `pull_request_id`
    * **Solo escaneos completados con hallazgos verdaderos positivos** — omite `scan.completed` cuando `true_positive_count` es 0 (`scan.failed` y `scan.started` no se ven afectados)

    **Alcance del proyecto**

    * Déjalo deshabilitado para recibir eventos de todos los proyectos
    * Habilita el filtro para limitar el webhook a los proyectos seleccionados
    * En `sla.violation`, el webhook se activa si coincide **algún** proyecto del payload

    **Filtro de cambio de estado** (para `issue.status_changed`)

    * Dejar vacío para todos los cambios de estado
    * O limitar a estados como `fixed` o `false_positive`

    **Alcance de escaneo programado** (para `scan.*` eventos)

    * Déjalo deshabilitado para recibir todos los escaneos, tanto manuales como programados
    * Habilítalo para que solo se active con los escaneos programados seleccionados
  </Step>

  <Step title="Añadir cabeceras y cuerpo personalizados (opcional)">
    **Cabeceras personalizadas**

    Añade cabeceras requeridas por el destino de tu webhook. Cada servicio tiene requisitos diferentes:

    <AccordionGroup>
      <Accordion title="Jira Automation" icon="jira">
        **Cabecera requerida:**

        ```text theme={null}
        X-Automation-Webhook-Token: <your-jira-webhook-secret>
        ```

        **Cómo conseguir tu token:**

        1. En Jira, crea una regla de automatización con un disparador de "Webhook entrante"
        2. Copia el token secreto proporcionado por Jira
        3. Añádelo como valor de la cabecera en Corgea

        [Documentación de Jira Webhook](https://support.atlassian.com/cloud-automation/docs/configure-the-incoming-webhook-trigger-in-atlassian-automation/)
      </Accordion>

      <Accordion title="Slack" icon="slack">
        **No se necesitan cabeceras personalizadas** — la autenticación está en la URL de Slack.

        Utiliza preferentemente una URL de Workflow Builder (`https://hooks.slack.com/triggers/...`). Con `Type = Slack`, Corgea aplana `scan.*` y `webhook.test`: asigna `message`, `pull_request_id`, `scan_url`, `true_positive_count` y `company`. **No** asignes campos `data.*` anidados (Slack devuelve HTTP 400). Los demás tipos de eventos no se aplanan. Consulta [Slack](slack).

        Los Incoming Webhooks (`https://hooks.slack.com/services/...`) necesitan un Custom Body cuyo JSON renderizado tenga una cadena `text` no vacía en el nivel superior. Utiliza `{"text": "{{message}}"}` solo con eventos del ciclo de vida del escaneo y `scheduled_scan.daily_report`.
      </Accordion>

      <Accordion title="Microsoft Teams" icon="microsoft">
        **No se necesitan cabeceras personalizadas** - Las URL de webhook de Teams incluyen la autenticación.

        Pega la URL del webhook de Teams con este formato: `https://xxx.webhook.office.com/webhookb2/xxx/IncomingWebhook/xxx`

        <Note>Los Incoming Webhooks de Teams no validan cabeceras personalizadas. Para mantener la seguridad, no reveles la URL del webhook.</Note>

        [Documentación de Teams Webhook](https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook)
      </Accordion>

      <Accordion title="API personalizada con Bearer Token" icon="key">
        **Cabecera:**

        ```text theme={null}
        Authorization: Bearer <your-api-token>
        ```

        Esta cabecera es habitual en API REST que utilizan tokens JWT u OAuth.
      </Accordion>

      <Accordion title="Splunk HEC" icon="bolt">
        **Cabecera:**

        ```text theme={null}
        Authorization: Splunk <your-hec-token>
        ```

        Úsalo al enviar eventos webhook a un endpoint Splunk HTTP Event Collector (HEC).
      </Accordion>

      <Accordion title="API personalizada con clave de API" icon="lock">
        **Cabeceras (elige una):**

        ```text theme={null}
        X-API-Key: <your-api-key>
        ```

        o

        ```text theme={null}
        Authorization: ApiKey <your-api-key>
        ```

        Es una opción habitual para autenticar mediante claves de API.
      </Accordion>

      <Accordion title="PagerDuty" icon="bell">
        **No se necesitan cabeceras personalizadas** - La API de eventos de PagerDuty v2 utiliza `routing_key` en el payload JSON para la autenticación.

        Utiliza el endpoint de la API de PagerDuty Events: `https://events.pagerduty.com/v2/enqueue`

        <Note>PagerDuty no valida cabeceras personalizadas. La autenticación se gestiona mediante routing\_key en el cuerpo de la solicitud.</Note>

        [Documentación de Webhook de PagerDuty](https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgw-events-api-v2-overview)
      </Accordion>

      <Accordion title="Zapier" icon="bolt">
        **No se necesitan cabeceras personalizadas** - Las URL de webhook de Zapier incluyen la autenticación.

        Crea un disparador "Webhooks by Zapier" y usa la URL proporcionada.

        <Note>Zapier Catch Hook no valida cabeceras personalizadas de forma predeterminada. Para mantener la seguridad, no reveles la URL del webhook. Si es necesario, puedes añadir al Zap la lógica de validación de cabeceras.</Note>

        [Documentación de Zapier Webhook](https://zapier.com/help/create/code-webhooks/trigger-zaps-from-webhooks)
      </Accordion>
    </AccordionGroup>

    <Tip>
      Si tu servicio no aparece aquí, consulta su documentación sobre webhooks o Incoming Webhooks para saber si requiere alguna cabecera.
    </Tip>

    <Warning>
      **Importante:** Aunque Corgea envía todas las cabeceras personalizadas que configures, no todos los destinos las validan. Servicios como Slack, Teams y Zapier utilizan URL secretas en lugar de validar cabeceras. Añade cabeceras personalizadas solo si el servicio de destino las necesita o las valida, como ocurre con Jira y algunas API personalizadas.
    </Warning>

    **Cuerpo personalizado**

    * Puedes proporcionar una plantilla de objeto JSON para el cuerpo de la solicitud del webhook
    * Déjala en blanco para utilizar la estructura de payload predeterminada de Corgea
    * Variables compatibles:
      * `{{payload}}` (objeto completo del payload predeterminado)
      * `{{time}}` (segundos Unix)
      * `{{timestamp}}` (marca de tiempo ISO 8601)
      * `{{event_type}}`
      * `{{event_id}}`
      * `{{message}}` (de `data.message` cuando esté presente — ciclo de vida del escaneo e informe diario)
    * Si la plantilla renderizada es JSON inválida, la entrega falla y el error aparece en el historial del webhook
  </Step>

  <Step title="Guardar y activar">
    * Haz clic en **Crear Webhook**
    * Copia la clave secreta desde la ventana emergente — Corgea solo la muestra una vez

    <Frame>
      <img src="https://mintcdn.com/corgea/bUShOerLaxHbdmO7/images/webhooks/webhook_secret_key.png?fit=max&auto=format&n=bUShOerLaxHbdmO7&q=85&s=e219c00690cd7873f987954a632fec23" alt="Ventana emergente de la clave secreta de Webhook mostrada una vez después de crear un webhook" style={{ borderRadius: '0.5rem' }} width="506" height="610" data-path="images/webhooks/webhook_secret_key.png" />
    </Frame>

    <Warning>
      Guarda la clave secreta antes de cerrar el cuadro de diálogo. No podrás volver a verla. Contacta con soporte si necesitas rotarla.
    </Warning>

    * Haz clic en **He guardado la clave secreta**
    * El webhook está activo y empezará a recibir eventos
  </Step>

  <Step title="Prueba tu Webhook">
    * Abre el webhook y haz clic en **Probar Webhook**
    * Confirma que tu endpoint devuelve una respuesta 2xx
    * En Slack Workflow Builder, recibirás un cuerpo plano con un campo `message` no vacío en el nivel superior y claves de triaje de ejemplo (`pull_request_id`, `scan_url`, `true_positive_count`, etc.), que podrás asignar durante la prueba. No se admiten configuraciones exclusivas de Workflow Builder.
  </Step>
</Steps>

### Verificar firmas de webhooks

<Note>Utiliza la clave secreta que se muestra al crear el webhook para verificar la cabecera `X-Corgea-Signature` de cada solicitud.</Note>

<Tabs>
  <Tab title="Python">
    ```python verify-signature.py theme={null}
    import hmac
    import hashlib

    def verify_webhook_signature(payload, signature, secret):
        """Verify Corgea webhook signature"""
        expected_signature = hmac.new(
            secret.encode('utf-8'),
            payload.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()

        return hmac.compare_digest(expected_signature, signature)

    # In your webhook handler:
    if not verify_webhook_signature(request.body, request.headers['X-Corgea-Signature'], SECRET_KEY):
        return HttpResponse(status=403)  # Reject invalid signatures
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript verify-signature.js theme={null}
    const crypto = require('crypto');

    function verifyWebhookSignature(payload, signature, secret) {
      const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(payload)
        .digest('hex');

      return crypto.timingSafeEqual(
        Buffer.from(expectedSignature),
        Buffer.from(signature)
      );
    }
    ```
  </Tab>
</Tabs>

***

## Casos de uso

### 1. Notificaciones de Slack en tiempo real

**Escenario**: Notificar al equipo de seguridad en Slack cuando se detecten hallazgos de gravedad alta

**Preparación**:

* Crea una URL de webhook entrante de Slack en tu espacio de trabajo de Slack
* En Corgea, crea un webhook con:
  * Tipo: `Slack`
  * URL: Tu URL de webhook de Slack
  * Eventos: `scan.completed`
  * Filtro de Proyectos: Proyectos críticos de producción

**Resultado**: Tu canal de #security recibe notificaciones instantáneas cuando terminan los escaneos

***

### 2. Creación automática de tickets para hallazgos críticos

**Escenario**: Crear automáticamente tickets en Jira o Linear cuando se detectan hallazgos críticos

**Preparación**:

* Crea un Zapier zap o un endpoint personalizado que genere tickets
* En Corgea, crea un webhook con:
  * Eventos: `scan.completed`, `issue.status_changed`
  * Filtro de estado: solo el estado `open` (para evitar tickets duplicados)
  * Filtro de Proyectos: Proyectos de producción

**Resultado**: Los hallazgos de gravedad alta o crítica se convierten automáticamente en tickets en la herramienta de gestión de proyectos

***

### 3. Integración del flujo de trabajo de aceptación de riesgos

**Escenario**: Documentar automáticamente en Jira o Linear los riesgos aceptados cuando los hallazgos de seguridad se marcan como "Riesgo Aceptado"

**Preparación**:

* Crea un endpoint o una integración con Zapier que genere tickets de documentación
* En Corgea, crea un webhook con:
  * Eventos: `issue.status_changed`
  * Filtro de estado: solo el estado `accepted_risk`
  * Filtro de Proyectos: Todos los proyectos o proyectos específicos de alta conformidad
* Configura la integración para:
  * Crea un ticket que documente la aceptación del riesgo
  * Incluye los detalles del hallazgo (clasificación, ruta del archivo y urgencia)
  * Añade la etiqueta "aceptación de riesgos"
  * Asigna el ticket al responsable de seguridad para que lo revise

**Resultado**: Cada riesgo aceptado se registra automáticamente en tu sistema de gestión de proyectos con todo el contexto, creando una pista de auditoría para revisiones de cumplimiento y gestión de riesgos

***

### 4. Integración personalizada de paneles

**Escenario**: Muestra métricas de seguridad en tiempo real en tu panel interno

**Preparación**:

* Crea un endpoint que reciba datos de webhooks y actualice tu panel de control
* En Corgea, crea un webhook con:
  * Eventos: Todos los eventos de escaneos y hallazgos
  * Sin filtros (recibe todo)

**Resultado**: El panel muestra en tiempo real los resultados de los escaneos de seguridad y las tendencias de los hallazgos

***

### 5. Enrutamiento multiequipo

**Escenario**: Redirigir diferentes notificaciones de proyectos a distintos equipos

**Preparación**:

* Crea webhooks separados para cada equipo:
  * **Backend Team Webhook**: Filtro de proyectos = proyectos de backend, canal de Slack #backend-seguridad
  * **Frontend Team Webhook**: Filtro de proyectos = proyectos frontend, canal de Slack #frontend-seguridad
  * **DevOps Team Webhook**: Filtro de proyectos = proyectos de infraestructura, canal de Slack #devops-seguridad

**Resultado**: Cada equipo solo ve los hallazgos de seguridad pertinentes para sus proyectos

***

### 6. Informes de cumplimiento

**Escenario**: Registrar automáticamente todos los hallazgos de seguridad en un sistema de cumplimiento

**Preparación**:

* Crea un endpoint que escriba en tu base de datos de cumplimiento
* En Corgea, crea un webhook con:
  * Eventos: `scan.completed`
  * Todos los proyectos
  * Almacena el historial de entregas de webhooks para las auditorías

**Resultado**: Seguimiento completo de auditoría de todos los escaneos de seguridad para fines de cumplimiento

***

## Resolución de problemas

### Consultar el historial de entregas de webhooks

<Steps>
  <Step title="Ve al historial de webhooks">
    Ve a **Integraciones** → **Webhooks**

    <Frame>
      <img src="https://mintcdn.com/corgea/JgeRsjW5cn2yU52a/images/webhooks/webhook_history.png?fit=max&auto=format&n=JgeRsjW5cn2yU52a&q=85&s=e0c209104347995d1bde95b867a6bb7d" alt="Historial de entregas de webhooks que muestra intentos recientes de webhook" style={{ borderRadius: '0.5rem' }} width="3112" height="1466" data-path="images/webhooks/webhook_history.png" />
    </Frame>
  </Step>

  <Step title="Abre el registro de entregas">
    Haz clic en **Historial** o **Registro de entregas**
  </Step>

  <Step title="Revisa los detalles de la entrega">
    <Frame>
      <img src="https://mintcdn.com/corgea/JgeRsjW5cn2yU52a/images/webhooks/webhook_history_details.png?fit=max&auto=format&n=JgeRsjW5cn2yU52a&q=85&s=14c49d0e334a55ee79a2bdb7f8b5126b" alt="Información detallada sobre la entrega de webhooks, incluyendo solicitudes y respuestas" style={{ borderRadius: '0.5rem' }} width="2806" height="2094" data-path="images/webhooks/webhook_history_details.png" />
    </Frame>

    Consulta todos los intentos de entrega de webhook con:

    * Tipo de evento y marca temporal
    * Código de estado HTTP
    * Detalles de solicitudes/respuestas
    * Mensajes de error (si los hay)
    * Intentos de reintento
  </Step>
</Steps>

### Problemas habituales y soluciones

<AccordionGroup>
  <Accordion title="Webhook no recibe eventos" icon="circle-xmark">
    **Posibles causas:**

    * El webhook está pausado o inactivo
    * No se han configurado suscripciones a eventos
    * Los filtros de proyecto, estado o escaneo programado excluyen los eventos
    * El endpoint no devuelve códigos de estado 2xx

    **Soluciones:**

    1. Comprueba que el webhook esté activo y no se encuentre en pausa
    2. Comprueba que se hayan seleccionado las suscripciones a los eventos
    3. Revisa los filtros; para probar, elimina temporalmente los de proyecto, estado o escaneo programado
    4. Revisa los logs del endpoint para comprobar si recibe solicitudes
    5. Prueba el webhook usando el botón "Probar Webhook"
  </Accordion>

  <Accordion title="Webhook se pausó automáticamente" icon="pause">
    **Causa:** 10 fallos consecutivos en la entrega

    **Soluciones:**

    1. Revisa los detalles de los errores en el historial de entregas
    2. Verifica que la URL de tu endpoint sea correcta y accesible
    3. Asegúrate de que tu endpoint devuelva códigos de estado 2xx
    4. Comprueba si alguna regla del firewall o de seguridad bloquea las solicitudes de Corgea
    5. Soluciona el problema subyacente y luego **reactiva manualmente** el webhook
    6. Utiliza "Test Webhook" para verificar que funciona antes de volver a activar
  </Accordion>

  <Accordion title="El webhook recibe demasiadas llamadas" icon="volume-high">
    **Soluciones:**

    1. **Usar filtros de estado**: Para `issue.status_changed`, filtra a solo estados que te interesan (por ejemplo, solo `fixed` y `false_positive`)
    2. **Utilizar filtros de proyectos**: Solo suscríbete a proyectos críticos específicos
    3. **Utilizar filtros de escaneos programados**: En los eventos de escaneo, selecciona los escaneos programados que deben activar el webhook
    4. **Reducir las suscripciones a eventos**: Cancela la suscripción a los eventos que no necesites
    5. **Limitar la tasa**: Implementa rate limiting o una cola en el endpoint
  </Accordion>

  <Accordion title="Fallo en la verificación de firma" icon="shield-xmark">
    **Posibles causas:**

    * Clave secreta incorrecta
    * Lógica incorrecta de verificación de firma
    * Problemas de codificación de caracteres

    **Soluciones:**

    1. Verifica que estás usando la clave secreta exacta de Corgea
    2. Asegúrate de usar el algoritmo HMAC-SHA256
    3. Utiliza el cuerpo sin procesar de la solicitud, no el JSON parseado, para la verificación
    4. Comprueba la codificación UTF-8 en ambos lados
    5. Utiliza `hmac.compare_digest()` (Python) o `crypto.timingSafeEqual()` (Node.js) para realizar una comparación resistente a ataques de temporización

    <Tip>Registra tanto la firma recibida como tu firma calculada para comparar</Tip>
  </Accordion>

  <Accordion title="Timeout del endpoint" icon="clock">
    **Causa:** Tu endpoint tarda más de 10 segundos en responder

    **Soluciones:**

    1. **Confirma la recepción de inmediato**: Devuelve 200 OK inmediatamente y después procesa la solicitud de forma asíncrona
    2. **Usa una cola**: Añade los payloads de webhook a una cola para procesarlos en segundo plano
    3. **Optimizar el procesamiento**: Acelera la lógica de tu manejador de webhook
    4. **Aumenta los recursos**: Escala la infraestructura del endpoint

    **Patrón de mejores prácticas:**

    ```python webhook-handler.py theme={null}
    @app.route('/webhook', methods=['POST'])
    def handle_webhook():
        payload = request.json

        # Immediately acknowledge receipt
        queue.add(process_webhook, payload)

        # Return quickly
        return '', 200

    def process_webhook(payload):
        # Do time-consuming work here
        ...
    ```
  </Accordion>

  <Accordion title="Eventos duplicados" icon="copy">
    **Posibles causas:**

    * Múltiples webhooks suscritos al mismo evento
    * Lógica de reintento activándose tras un éxito retardado

    **Soluciones:**

    1. Comprueba si hay configuraciones de webhook duplicadas
    2. Utiliza el campo `event_id` como clave de idempotencia: almacena los ID de los eventos procesados y omite los duplicados
    3. Implementa claves de idempotencia en tu endpoint

    **Patrón de Idempotencia:**

    ```python idempotency.py theme={null}
    processed_events = set()  # Or use Redis/database

    @app.route('/webhook', methods=['POST'])
    def handle_webhook():
        event_id = request.json['event_id']

        if event_id in processed_events:
            return '', 200  # Already processed

        # Process event...
        processed_events.add(event_id)
        return '', 200
    ```
  </Accordion>

  <Accordion title="Faltan datos en el payload" icon="question">
    **Soluciones:**

    1. Consulta el payload completo en el historial de entregas del webhook
    2. Algunos campos pueden ser `null` si no hay datos, por ejemplo en hallazgos sin asignar
    3. Implementa comprobaciones de valores nulos en el código del handler
    4. Consulta en el historial de entregas la estructura del payload de cada evento
  </Accordion>
</AccordionGroup>

### Consultar estadísticas de webhooks

Consulta métricas de rendimiento para tus webhooks:

1. Navega a **Integraciones** → **Webhooks**
2. Consulta las estadísticas de cada webhook:
   * **Total de entregas**: Número total de llamadas del webhook
   * **Entregas exitosas**: Llamadas que devolvieron 2xx
   * **Entregas fallidas**: Llamadas que fallaron o se agotaron
   * **Tasa de éxito**: Porcentaje de entregas exitosas
   * **Fallos consecutivos**: Racha actual de fallos
   * **Última activación**: Momento en que el webhook se activó por última vez

***

### Reintento manual

Si una entrega por webhook falló, puedes intentarlo manualmente:

1. Ve a **Integraciones** → **Webhooks** → **Historial**
2. Encuentra la entrega fallida
3. Haz clic **Reintentar**
4. Se creará y enviará un nuevo intento de entrega inmediatamente

***

### Exportar el historial de entregas

Para cumplimiento o depuración, exporta el historial de entrega de webhooks:

1. Ve a **Integraciones** → **Webhooks** → **Historial**
2. Aplica los filtros necesarios (intervalo de fechas, tipo de evento, estado y webhook)
3. Haz clic en **Exportar** para descargar CSV
4. Utiliza la exportación para:
   * Auditorías de cumplimiento
   * Análisis de rendimiento
   * Patrones de depuración
   * Seguimiento de la resolución de problemas

***

### Consejos para pruebas

<Accordion title="Antes de salir en directo">
  1. Utiliza [webhook.site](https://webhook.site) o [RequestBin](https://requestbin.com) para inspeccionar los payloads
  2. Prueba primero con proyectos de bajo volumen
  3. Supervisa la tasa de éxito de las entregas durante los primeros días
  4. Configura alertas en tu sistema para los fallos del webhook
</Accordion>

<Accordion title="Lista de verificación de depuración">
  * La URL del webhook es correcta y accesible
  * El endpoint devuelve el código de estado 2xx en 10 segundos
  * El cortafuegos permite las solicitudes de Corgea
  * Se seleccionan las suscripciones a los eventos
  * Los filtros se configuran correctamente (o se eliminan para pruebas)
  * La verificación de firma funciona (si se usa secreto)
  * El webhook está activo y no está en pausa
</Accordion>

***

## Ejemplos de cargas útiles

<Tabs>
  <Tab title="Cambio de estado del hallazgo">
    ```json issue-status-changed.json theme={null}
    {
      "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "event_type": "issue.status_changed",
      "timestamp": "2025-01-15T14:30:00.000Z",
      "data": {
        "company": "comp-uuid-1234",
        "issue_id": "issue-uuid-5678",
        "classification": "SQL Injection",
        "urgency": "HI",
        "status": "fixed",
        "previous_status": "open",
        "file_path": "src/controllers/user.py",
        "line_num": 45,
        "project": {
          "id": "proj-uuid-9012",
          "name": "Production API"
        },
        "scan_id": "scan-uuid-3456",
        "url": "https://app.corgea.com/issue/issue-uuid-5678/"
      }
    }
    ```
  </Tab>

  <Tab title="Escaneo iniciado">
    ```json scan-started.json theme={null}
    {
      "event_id": "a0b1c2d3-e4f5-6789-abcd-ef0123456789",
      "event_type": "scan.started",
      "timestamp": "2025-01-15T15:40:00.000Z",
      "data": {
        "company": "comp-uuid-1234",
        "scan_id": "scan-uuid-7890",
        "run_id": "run-12345",
        "project": {
          "id": "proj-uuid-9012",
          "name": "Production API"
        },
        "project_name": "Production API",
        "branch": "main",
        "engine": "corgea-blast",
        "scan_type": "full",
        "status": "started",
        "pull_request_id": "42",
        "scan_url": "https://app.corgea.app/project/proj-uuid-9012/?scan_id=scan-uuid-7890",
        "true_positive_count": 0,
        "message": "Scan started for Production API (PR #42).",
        "scheduled_scan_ids": ["scheduled-scan-uuid"],
        "created_at": "2025-01-15T15:40:00.000Z"
      }
    }
    ```
  </Tab>

  <Tab title="Escaneo completado">
    ```json scan-completed.json theme={null}
    {
      "event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "event_type": "scan.completed",
      "timestamp": "2025-01-15T15:45:00.000Z",
      "data": {
        "company": "comp-uuid-1234",
        "scan_id": "scan-uuid-7890",
        "run_id": "run-12345",
        "project": {
          "id": "proj-uuid-9012",
          "name": "Production API"
        },
        "project_name": "Production API",
        "branch": "main",
        "engine": "corgea-blast",
        "scan_type": "full",
        "status": "completed",
        "pull_request_id": "42",
        "scan_url": "https://app.corgea.app/project/proj-uuid-9012/?scan_id=scan-uuid-7890",
        "true_positive_count": 5,
        "summary": {
          "total_issues": 8,
          "issues_with_fixes": 5,
          "issues_without_fixes": 3,
          "severity_breakdown": {
            "HI": {
              "name": "High",
              "count": 2
            },
            "ME": {
              "name": "Medium",
              "count": 6
            }
          },
          "classification_breakdown": [
            {
              "classification": "SQL Injection",
              "count": 2
            },
            {
              "classification": "Cross-Site Scripting (XSS)",
              "count": 1
            }
          ]
        },
        "message": "Scan completed for Production API (PR #42): 5 true-positive findings (5 with fixes). View: https://app.corgea.app/project/proj-uuid-9012/?scan_id=scan-uuid-7890",
        "scheduled_scan_ids": ["scheduled-scan-uuid"],
        "processed_at": "2025-01-15T15:45:00.000Z",
        "created_at": "2025-01-15T15:40:00.000Z"
      }
    }
    ```

    El sobre anidado anterior es el que reciben Zapier y Other, y el que Corgea almacena en el historial de entregas. Solo en `scan.*` y `webhook.test`, Slack Workflow Builder recibe los campos de triaje como claves **de nivel superior** en el cuerpo HTTP (`message`, `scan_id`, `scan_url`, `pull_request_id`, `true_positive_count`, `status`, `branch`, `project_name`, `company`, `run_id`, `engine`, `scan_type`; también `error` en `scan.failed` cuando está disponible, y `company_id`, `test` y `webhook_name` en `webhook.test`). No recibe anidados `summary`, `project`, `scheduled_scan_ids`, `scan_errors`, `created_at` ni `processed_at`. `data.message`, o `message` en el nivel superior, es un resumen de texto sin formato; el sufijo "(N con correcciones)" solo cuenta las correcciones de hallazgos verdaderos positivos, no las de todos los hallazgos del escaneo.
  </Tab>

  <Tab title="Escaneo fallido">
    ```json scan-failed.json theme={null}
    {
      "event_id": "c1d2e3f4-a5b6-7890-cdef-1234567890ab",
      "event_type": "scan.failed",
      "timestamp": "2025-01-15T15:42:00.000Z",
      "data": {
        "company": "comp-uuid-1234",
        "scan_id": "scan-uuid-7890",
        "run_id": "run-12345",
        "project": {
          "id": "proj-uuid-9012",
          "name": "Production API"
        },
        "project_name": "Production API",
        "branch": "main",
        "engine": "corgea-blast",
        "scan_type": "full",
        "status": "failed",
        "pull_request_id": "42",
        "scan_url": "https://app.corgea.app/project/proj-uuid-9012/?scan_id=scan-uuid-7890",
        "true_positive_count": 0,
        "error": "Scanner timed out",
        "scan_errors": null,
        "message": "Scan failed for Production API (PR #42): Scanner timed out. View: https://app.corgea.app/project/proj-uuid-9012/?scan_id=scan-uuid-7890",
        "scheduled_scan_ids": ["scheduled-scan-uuid"],
        "created_at": "2025-01-15T15:40:00.000Z"
      }
    }
    ```

    `data.message` incluye el motivo del fallo (truncado si supera los 200 caracteres) y la URL del escaneo cuando está disponible. Utiliza `data.error` o `data.scan_errors` para consultar los detalles sin procesar. En Slack Workflow Builder, `error` también está disponible como campo plano de nivel superior.
  </Tab>

  <Tab title="Hallazgo asignado">
    ```json issue-assigned.json theme={null}
    {
      "event_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
      "event_type": "issue.assigned",
      "timestamp": "2025-01-15T16:00:00.000Z",
      "data": {
        "company": "comp-uuid-1234",
        "issue_id": "issue-uuid-5678",
        "classification": "Cross-Site Scripting (XSS)",
        "urgency": "ME",
        "status": "open",
        "assigned_to": {
          "id": "user-uuid-1111",
          "email": "jane.doe@company.com",
          "name": "Jane Doe"
        },
        "previously_assigned_to": {
          "id": "user-uuid-2222",
          "email": "john.smith@company.com",
          "name": "John Smith"
        },
        "project": {
          "id": "proj-uuid-9012",
          "name": "Production API"
        },
        "url": "https://app.corgea.com/issue/issue-uuid-5678/"
      }
    }
    ```
  </Tab>

  <Tab title="Violación del SLA">
    ```json sla-violation.json theme={null}
    {
      "event_id": "f6a7b8c9-d0e1-2345-f012-345678901234",
      "event_type": "sla.violation",
      "timestamp": "2025-01-15T16:20:00.000Z",
      "data": {
        "company": "comp-uuid-1234",
        "sla": {
          "id": 3,
          "rule_type": "sca",
          "urgency": ["CR", "HI"],
          "remediation_days": 14,
          "escalation_days": 7
        },
        "notification_type": "escalation",
        "issue_kind": "SCA",
        "issue_count": 12,
        "summary_message": "Plain-text summary of breached issues...",
        "projects": [
          {
            "id": "proj-uuid-9012",
            "name": "Production API",
            "url": "https://app.corgea.com/project/proj-uuid-9012",
            "issue_count": 12,
            "urgency_counts": {
              "Critical": 4,
              "High": 8
            }
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="Inicio de sesión de usuario">
    ```json user-login.json theme={null}
    {
      "event_id": "d4e5f6a7-b8c9-0123-def0-123456789012",
      "event_type": "user.login",
      "timestamp": "2025-01-15T16:10:00.000Z",
      "data": {
        "company": "comp-uuid-1234",
        "user_id": 123,
        "username": "jane.doe",
        "email": "jane.doe@company.com",
        "first_name": "Jane",
        "last_name": "Doe",
        "user_agent": "Mozilla/5.0",
        "path": "/login/"
      }
    }
    ```
  </Tab>

  <Tab title="Inicio de sesión de usuario fallido">
    ```json user-login-failed.json theme={null}
    {
      "event_id": "e5f6a7b8-c9d0-1234-ef01-234567890123",
      "event_type": "user.login_failed",
      "timestamp": "2025-01-15T16:12:00.000Z",
      "data": {
        "company": "comp-uuid-1234",
        "username": "jane.doe",
        "user_agent": "Mozilla/5.0",
        "path": "/login/"
      }
    }
    ```
  </Tab>

  <Tab title="Informe Diario de Escaneo Programado">
    ```json scheduled-scan-daily-report.json theme={null}
    {
      "event_id": "3282dbbb-7392-476b-8ce2-19fc1070342c",
      "event_type": "scheduled_scan.daily_report",
      "timestamp": "2026-05-13T12:29:53.083616+00:00",
      "data": {
        "title": "🐕 Corgea Daily Scan Report",
        "company": "123",
        "company_name": "Acme Corp",
        "total_new_issues": 7,
        "message": "Daily scan report for Acme Corp: 7 new issues detected across 2 scan runs.",
        "scan_runs": [
          {
            "scheduled_scan_name": "Nightly API Scan",
            "project": "api-service",
            "new_issue_count": 5,
            "scan_url": "https://app.corgea.com/scans/abc123"
          },
          {
            "scheduled_scan_name": "Nightly Frontend Scan",
            "project": "frontend",
            "new_issue_count": 2,
            "scan_url": "https://app.corgea.com/scans/def456"
          }
        ]
      }
    }
    ```

    El sobre exterior (`event_id`, `event_type`, `timestamp`, `data`) sigue la [estructura estándar del payload](#payload-structure). El objeto `data` contiene:

    | Campo                             | Tipo           | Descripción                                                                            |
    | --------------------------------- | -------------- | -------------------------------------------------------------------------------------- |
    | `title`                           | cadena         | Título visible del informe                                                             |
    | `company`                         | cadena         | ID de la empresa                                                                       |
    | `company_name`                    | cadena         | Nombre visible de la empresa                                                           |
    | `total_new_issues`                | entero         | Suma de los nuevos hallazgos de todas las ejecuciones incluidas en el payload          |
    | `message`                         | cadena         | Resumen legible adecuado para notificaciones de texto sin formato (por ejemplo, Slack) |
    | `scan_runs`                       | array          | Una entrada por cada ejecución de escaneo que produjo al menos un hallazgo nuevo       |
    | `scan_runs[].scheduled_scan_name` | cadena         | Nombre de la configuración del escaneo programado                                      |
    | `scan_runs[].project`             | cadena \| null | Nombre del proyecto, o `null` si no hay ningún proyecto vinculado                      |
    | `scan_runs[].new_issue_count`     | entero         | Número de hallazgos nuevos detectados en esta ejecución                                |
    | `scan_runs[].scan_url`            | cadena \| null | Enlace directo a los resultados del escaneo en Corgea, o `null` si no está disponible  |
  </Tab>
</Tabs>

***

## Preguntas frecuentes

<AccordionGroup>
  <Accordion title="¿Puedo usar la misma URL de webhook para varios tipos de eventos?">
    Sí. Tu endpoint recibirá la cabecera `X-Corgea-Event` y el campo `event_type` para identificar el evento.
  </Accordion>

  <Accordion title="¿Cuántos webhooks puedo crear?">
    No hay un límite estricto, pero recomendamos organizarlos por finalidad, por ejemplo, uno por equipo o herramienta.
  </Accordion>

  <Accordion title="¿Qué pasa si mi endpoint está caído?">
    Corgea lo intentará 3 veces con retroceso exponencial. Tras 10 fallos consecutivos, el webhook se pausa automáticamente.
  </Accordion>

  <Accordion title="¿Puedo probar los webhooks sin activar eventos reales?">
    Sí. Utiliza el botón "Test Webhook" para enviar un payload de muestra sin esperar a que ocurran eventos reales. Para Slack Workflow Builder, la muestra es plana e incluye un campo `message` no vacío, además de las claves de triaje (`pull_request_id`, `scan_url`, `true_positive_count`, `company`). También incluye `company_id`, con el mismo valor, para los consumidores de Test antiguos.
  </Accordion>

  <Accordion title="¿Puedo notificar solo en casos de fallos en el escaneo de PR o en escaneos completados con resultados?">
    Sí. En **Filtros de eventos de escaneo**, activa **Solo escaneos de pull request / merge request** y **Solo escaneos completados con hallazgos verdaderos positivos**. Suscríbete a `scan.failed` y `scan.completed`. El filtro de hallazgos no se aplica a `scan.failed`.
  </Accordion>

  <Accordion title="¿Los webhooks admiten autenticación?">
    Sí. Corgea genera automáticamente una clave secreta para verificar la firma HMAC (`X-Corgea-Signature`). También puedes añadir cabeceras personalizadas para los tokens de autenticación del destino.
  </Accordion>

  <Accordion title="¿Puedo filtrar los webhooks para ramas específicas?">
    No directamente, pero puedes filtrar por proyecto. También puedes filtrar en el endpoint el payload recibido.
  </Accordion>

  <Accordion title="¿Están cifradas las cargas útiles de webhook?">
    Los payloads se envían mediante HTTPS (TLS), que los cifra en tránsito. Utiliza las firmas HMAC para verificar su autenticidad.
  </Accordion>

  <Accordion title="¿Cuánto tiempo se conservan los registros de entrega de webhook?">
    Los registros de entrega se conservan para cumplimiento y depuración. Consulta con tu plan los periodos específicos de retención.
  </Accordion>

  <Accordion title="¿Puedo volver a intentar un webhook manualmente?">
    Sí. Ve al historial de entregas del webhook y haz clic en "Reintentar" en cualquier entrega fallida.
  </Accordion>

  <Accordion title="¿Desde qué direcciones IP envía Corgea los webhooks?">
    Contacta con soporte para obtener la lista actual de direcciones IP que debes incluir en la allowlist del firewall.
  </Accordion>
</AccordionGroup>

***

**¿Tienes alguna pregunta o problema?** Contacta con el [soporte de Corgea](mailto:support@corgea.com).
