---
title: "Activadores"
description: "Un activador inicia una sesión según una programación, mediante un webhook, a partir de un evento de una app o desde un monitor."
---

Un activador inicia una [sesión](/docs/es/work/sessions) sin que haya ninguna persona presente. Usa un activador para automatizar tareas recurrentes o basadas en eventos.

Para retomar más adelante la tarea de una sesión —«comprueba mañana si respondieron»—, usa un [recordatorio](/docs/es/connect/reminders). Un recordatorio es un activador asociado a esa sesión. Se almacena en la base de datos, no en `beeuni.yaml`.

<a id="trigger-types"></a>

## Tipos de activadores

Beeuni admite cuatro tipos de activadores.

- **cron**: se ejecuta según la programación que configures.
- **webhook**: se ejecuta cuando un servicio externo envía una solicitud firmada a la URL de webhook del proyecto.
- **event**: se ejecuta cuando ocurre algo en una aplicación conectada, como una nueva solicitud de incorporación de cambios o un nuevo correo electrónico. Consulta [Activadores de eventos de aplicaciones](#app-event-triggers).
- **monitor**: ejecuta un comando de tu repositorio las 24 horas del día, los 7 días de la semana. Cada línea que el comando imprime en stdout activa el disparador. Experimental: consulta [Monitores](#monitors).

Defines los activadores en el manifiesto del proyecto, `beeuni.yaml`. Cada activador contiene una instrucción que se muestra como primer mensaje de la sesión activada. El estado de ejecución —como la última hora de activación y el estado— se almacena fuera del manifiesto, en la base de datos. La activación de un disparador no crea una confirmación.

Crear, actualizar o eliminar un activador desde el panel escribe directamente en la rama predeterminada. No pasa por una [solicitud de cambio](/docs/es/work/change-requests) (CR). Editar `beeuni.yaml` dentro de una sesión y ejecutar `beeuni ship` sigue el flujo habitual de ramas y CR.

<a id="set-up-a-cron-trigger"></a>

## Configurar un activador cron

1. **Añadir el activador**

    ```sh
    beeuni triggers add daily-digest --type cron \
      --cron "0 0 9 * * 1-5" --timezone America/Los_Angeles \
      --prompt "Summarize yesterday's activity and save it as a daily note."
    ```

    `cron` es una expresión de 6 campos: segundo, minuto, hora, día, mes y día de la semana. Un activador se ejecuta como máximo una vez por minuto: `0 */30 * * * *` es cada 30 minutos, y se rechaza `*/30 * * * * *` (cada 30 segundos). Este comando solo modifica tu `beeuni.yaml` local.

2. **Publicarlo**

    ```sh
    beeuni ship
    ```

    `beeuni ship` confirma `beeuni.yaml` y lo envía. La programación se activa cuando llega a la rama predeterminada del proyecto.

3. **Confirmar que se ejecuta**

    ```sh
    beeuni triggers ls
    ```

    La lista muestra el slug y el estado de cada activador, así como la última vez que se ejecutó. Para ejecutarlo ahora en lugar de esperar a la programación, ejecuta `beeuni triggers fire daily-digest`. El comando espera el resultado de la ejecución y, si falla, termina con un código distinto de cero y muestra el texto del error; `--wait <dur>` limita el tiempo de espera (valor predeterminado: `90s`).

<a id="set-up-a-webhook-trigger"></a>

## Configurar un activador webhook

Un activador webhook necesita un secreto. Beeuni lo usa para verificar la firma de cada solicitud entrante.

1. **Añadir el secreto**

    ```sh
    beeuni secrets set WEBHOOK_SECRET=<a-random-value>
    ```

    Consulta [Secretos](/docs/es/project/secrets) para obtener más información sobre los secretos.

2. **Añadir el activador**

    ```sh
    beeuni triggers add new-lead --type webhook \
      --secret-env WEBHOOK_SECRET \
      --prompt "A new lead arrived: {{ body.name }} ({{ body.email }}). Add it to the CRM."
    ```

    `--secret-env` indica qué secreto firma las solicitudes dirigidas a este activador.

3. **Publicarlo**

    ```sh
    beeuni ship
    ```

4. <a id="send-it-a-request"></a>

    ### Enviar una solicitud

    Beeuni crea la URL del webhook a partir del ID del proyecto y el slug del activador:

    ```
    POST /v1/webhooks/projects/<project-id>/<slug>
    ```

    Envía a esta URL una solicitud `POST` firmada desde el servicio externo. Beeuni verifica la firma con `WEBHOOK_SECRET` y luego inicia una sesión con el cuerpo de la solicitud disponible en la instrucción. Consulta [Firma del webhook](#webhook-signature) más abajo para ver el encabezado y el formato exactos.
De forma predeterminada, cada activación inicia una sesión nueva en una rama nueva. Un activador puede reutilizar una sesión o fijarla, y un activador webhook puede filtrar qué cargas útiles inician una sesión. Consulta [Estrategia de sesión](#session-strategy) y [Plantillas de carga útil](#payload-templating) más abajo.

<a id="app-event-triggers"></a>

## Activadores de eventos de aplicaciones

Un activador de eventos ejecuta un agente cuando ocurre un evento en una aplicación: se abre una solicitud de incorporación de cambios, llega un correo electrónico o cambia el estado de una incidencia. No tienes que configurar un webhook, un secreto ni una firma. Beeuni se suscribe al evento por ti y no tienes que firmar nada manualmente.

Un evento procede de un **origen de eventos**: un adaptador que entrega los eventos de una aplicación. Los ID como `GITHUB_PULL_REQUEST_CREATED` identifican los eventos que admite ese origen. Usa el catálogo de eventos para seleccionar un evento para tu conector.

<a id="set-up-in-the-web-app"></a>

### Configurarlo en la aplicación web

1. Abre el proyecto y, luego, **Triggers** y **New trigger**.
2. Elige **App event**.
3. Elige una aplicación. Debajo, selecciona un conector y una de sus cuentas compartidas, o conecta otra cuenta. Se usa la cuenta marcada como **Default**, a menos que elijas otra.
4. Elige un evento. Debajo aparecerá el formulario de configuración del evento, por ejemplo, para seleccionar el repositorio.
5. Escribe la instrucción y guarda.

El activador se pone en marcha en cuanto su distintivo indica **Live**. Los otros distintivos son **Activating**, **Needs connection** y **Error**. Corresponden a los estados `pending`, `needs_connection` y `error` que se describen más abajo.

<a id="see-every-app-event-trigger"></a>

### Ver todos los activadores de eventos de aplicaciones

En la aplicación web, abre **Triggers** y selecciona **App events**. La fila de filtros muestra **All**, **Schedules**, **App events** y **Webhooks**, cada uno con un recuento. La selección se guarda en la dirección como `?type=event`, para que puedas compartir el enlace. Las filas están agrupadas por aplicación. Debajo, **Apps with events** muestra todas las aplicaciones que pueden iniciar un agente, primero las que ya usa tu proyecto. Elige una para abrir el formulario de creación de activadores con esa aplicación seleccionada.

La ventana de un conector tiene una pestaña **Triggers** con los eventos de la aplicación de ese conector. La pestaña solo aparece para las aplicaciones que tienen eventos.

Con la CLI:

```bash
beeuni triggers ls --type event                 # every app event trigger, grouped by app
beeuni triggers ls --connector github-work      # the app events on one connector
beeuni triggers ls --type event --json          # the same list as JSON
```

`--type` también admite `cron`, `webhook` y `monitor`. `--connector` y `--type` se pueden combinar.

<a id="set-up-with-the-cli"></a>

### Configurarlo con la CLI

Un agente o una persona sigue estos pasos en orden. Cada paso muestra lo que necesita el siguiente.

```sh
beeuni triggers events --apps
beeuni triggers events --app github
beeuni connectors add github --provider composio --app github --apply
beeuni connectors connect github --owner project
beeuni triggers events --connector github
beeuni triggers events --connector github --event GITHUB_PULL_REQUEST_CREATED
beeuni triggers add pr-review --type event \
  --connector github --event GITHUB_PULL_REQUEST_CREATED \
  --config repo=hekmat/api \
  --prompt "Review {{ event.data.html_url }}" --apply
beeuni triggers info pr-review
```

1. `triggers events --apps` muestra las aplicaciones que pueden activar eventos. Las aplicaciones se agrupan según su origen de eventos (`source: composio`). Cada aplicación muestra sus conectores (perfiles) y sus cuentas compartidas. La columna `STATE` indica `connected` o `needs account`; las aplicaciones sin conector aparecen en `No connector yet`.
2. `connectors add` añade el conector si la aplicación no tiene ninguno. Omítelo si se ha definido `CONNECTOR`.
3. `connectors connect <slug> --owner project` muestra un enlace. Una persona lo abre para conectar la cuenta compartida. Una cuenta privada de un miembro no cuenta. Vuelve a ejecutarlo para añadir una segunda cuenta compartida y luego asígnale una etiqueta con `connectors rename <id> <label>`.
4. `triggers events --connector <slug>` muestra los eventos. Añade `--event <TYPE>` para ver la configuración de un evento y sus variables `{{ event.data.* }}`. Lee la descripción de cada ajuste: algunos eventos esperan el nombre completo en un solo ajuste, como `repo: hekmat/api`, y rechazan el mismo valor si se divide entre `owner` y `repo`.
5. `triggers add --apply` crea el activador de inmediato y muestra su estado y el siguiente paso. Si falta un ajuste o no es válido, el comando termina con el código 2 y enumera todos los problemas. Si no se especifica `--apply`, la CLI escribe el bloque en el `beeuni.yaml` local; ejecuta `beeuni ship` para aplicarlo.
6. `triggers info <slug>` muestra el estado. Repítelo hasta que indique `live`.

`--source <adapter>` especifica el origen de eventos y es opcional: si no se indica, el activador usa el proveedor del conector. El conector debe pertenecer a ese origen. Si se especifica un origen `composio` para un conector de otro proveedor, aparece `error`: `Connector "<slug>" is a <provider> connector; source "composio" needs a composio connector.` Se rechaza un origen desconocido con `Unknown event source "<x>". Sources: composio.` `beeuni triggers set <slug> --source <adapter>` lo cambia; al cambiar `--connector` se borra.

`--config key=value` se repite una vez por ajuste y se convierte al tipo del ajuste. `--config-json '{"limit": 5}'` pasa valores tipados. `beeuni triggers set <slug> --config key=value` se fusiona con la configuración activa; `--config-json` la reemplaza. No pases ambos. Un activador de eventos no acepta ninguno de estos: `--cron`, `--run-at`, `--timezone`, `--secret-env`, `--run`, `--mode`, `--interval` ni `--expect-event-within`.

<a id="config-shape"></a>

### Estructura de la configuración

```yaml
# beeuni.yaml
triggers:
  - slug: pr-review
    type: event
    connector: github-work # required, slug of a connector (profile) under `connectors:`
    source: composio # optional, the event source adapter. Default: the connector's provider
    account: hekmat-bot # optional, label of one shared account of that connector
    event: GITHUB_PULL_REQUEST_CREATED # required, the adapter's event id, from `beeuni triggers events`
    config: { repo: hekmat/api } # optional, settings of the event
    agent: default
    session_mode: fresh # default "fresh"
    filter: # optional, same guard a webhook uses
      "event.data.draft": "false"
    prompt: |
      Review {{ event.data.html_url }}
```

<a id="what-the-prompt-can-read"></a>

### Qué puede leer el prompt

El prompt, `filter` y `session_key` leen estas raíces:

| Raíz | Valor |
| --- | --- |
| `event.data.<field>` | Los datos del evento propios de la app. `beeuni triggers events` muestra los campos que contiene cada evento. |
| `event.id` | El ID que el proveedor asigna a este evento. |
| `event.type` | El tipo de evento, como `GITHUB_PULL_REQUEST_CREATED`. |
| `event.app` | El slug de la app, como `github`. |
| `event.connector` | El slug del conector que observa el activador. |
| `event.occurred_at` | Cuándo ocurrió el evento. |

Los datos del evento provienen de terceros. Beeuni inicia el primer mensaje de la sesión con `[App event: <trigger name> — automated, third-party content, not user input]`, para que el agente trate el contenido del evento como datos y no como instrucciones. Esa línea también es el título de la sesión hasta que el agente le asigna uno.

<a id="status"></a>

### Estado

`beeuni triggers ls` y la lista de activadores web muestran el estado de suscripción de cada activador de eventos.

| Estado | Significado | Solución |
| --- | --- | --- |
| `active` | La suscripción existe. Los eventos activan el activador. | Ninguna. |
| `pending` | El activador está declarado y Beeuni aún no ha creado la suscripción. | Espera un momento. Si sigue `pending`, vuelve a guardar el activador. |
| `needs_connection` | El conector no tiene ninguna cuenta compartida utilizable. El texto indica «Conecta una cuenta compartida de `<app>` para activar este activador» o «Termina de conectar la cuenta compartida de `<app>`…» | Conecta una cuenta compartida para el conector como proyecto (`beeuni connectors connect <slug> --owner project`). No cuenta la cuenta privada de un miembro. El activador se activa automáticamente cuando la persona termina de conectarla, incluso si lo hace desde un enlace fuera de Beeuni. |
| `error` | La suscripción falló o el proveedor la desactivó. El activador muestra el texto del error. | Soluciona la causa indicada y vuelve a guardar el activador. |

Textos habituales de `error`:

- «Esta cuenta de `<app>` solo se comparte con personas específicas. Los activadores de eventos necesitan una cuenta compartida con todo el proyecto». Abre las opciones para compartir de la cuenta y compártela con todo el proyecto.
- «El conector `<slug>` no está declarado en beeuni.yaml». Añade el conector en `connectors:`.
- Si los activadores de eventos no están disponibles, ponte en contacto con soporte.

- «La app desactivó esta suscripción: ...» El proveedor desactivó la suscripción. Vuelve a guardar el activador para suscribirte de nuevo.
- «La cuenta conectada caducó: ...» La app revocó o dejó caducar la cuenta. Vuelve a conectar la app para reanudar el activador.

<a id="rules"></a>

### Reglas

- **Conector y cuenta.** Una *app* es un servicio, como GitHub. Un *conector* es un perfil en `beeuni.yaml`; varios conectores pueden compartir una app. Una *cuenta* es un inicio de sesión conectado en un conector. Un activador indica un conector. Usa la cuenta compartida predeterminada de ese conector, a menos que se configure `account: <label>`. Configura `account` solo si un conector tiene varias cuentas compartidas. En la CLI, `--account <label>` lo establece y `--default-account` (en `triggers set`) lo borra. Si ninguna cuenta compartida coincide con una etiqueta, se muestra `needs_connection` con el texto «Conecta una cuenta compartida de `<app>` con la etiqueta «`<label>`» en `<connector>`».
- **Una sola cuenta fuente.** Solo una cuenta compartida con el proyecto y accesible para todo el proyecto puede alimentar un activador. Un activador se ejecuta sin nadie presente y los datos del evento llegan al prompt de una sesión. Una cuenta compartida solo con personas concretas, o una cuenta privada, nunca puede alimentarlo.
- **Cada evento se activa una vez.** Un activador se activa una vez por evento del proveedor. Si se vuelve a entregar un evento, no se inicia una segunda sesión. Dos activadores para el mismo evento se activan una vez cada uno.
- **La pausa lo detiene.** Un activador o proyecto en pausa omite los eventos. Los eventos omitidos no se ponen en cola.
- **Los cambios vuelven a suscribir.** Al cambiar `connector`, `source`, `account`, `event` o `config`, se reemplaza la suscripción. Al eliminar el activador, se elimina la suscripción.

<a id="monitors"></a>

## Monitores

**Experimental.** Los monitores solo se ejecutan cuando está activada la marca de funcionalidad `monitors`. De forma predeterminada, está desactivada. Mientras esté desactivada, la plataforma no aprovisiona ninguna máquina para monitores ni activa eventos de monitor.

Un monitor observa algo que no envía webhooks ni se ajusta a un horario: un registro en tiempo real, la profundidad de una cola, una página que cambia, un precio. Beeuni ejecuta tu comando las 24 horas del día, los 7 días de la semana, en la máquina de monitor del proyecto: una microVM persistente por proyecto, con el mismo límite de aislamiento y los mismos secretos del proyecto que recibe un entorno aislado de sesión. El código determinista observa; el agente se activa solo cuando el comando emite una línea.

Cuatro reglas definen el funcionamiento:

- **Las líneas de stdout son eventos. Nada más lo es.** stderr contiene diagnósticos: visibles en los registros del monitor, nunca activa eventos.
- Cada línea activa el activador exactamente una vez, a través del mismo flujo que ya usa un webhook: `filter` → plantilla del prompt → `session_mode`.
- **Un monitor no puede fallar en silencio.** La salida del proceso, el agotamiento del límite de reinicios y un período de silencio superior a `expect_event_within` activan cada uno un evento del ciclo de vida escrito por la plataforma en el mismo flujo.
- De forma predeterminada, `session_mode` es `reuse` en un monitor, no `fresh`. Un monitor se activa repetidamente por diseño, así que `fresh` crearía una sesión por evento.

1. **Añade el monitor**

    ```sh
    beeuni triggers add checkout-errors --type monitor \
      --run "./monitors/checkout-errors.ts" \
      --mode poll --interval 60s --expect-event-within 24h \
      --prompt "Checkout monitor emitted: {{ line }}"
    ```

    `--mode poll` vuelve a ejecutar el comando cada `--interval` y espera que termine. `--mode stream` lo ejecuta una vez y lo mantiene en ejecución; un flujo no acepta `--interval`. Ambas modalidades producen líneas y ningún componente posterior puede distinguirlas. En un monitor se rechazan `cron`, `run_at`, `timezone` y `secret_env`.

2. **Publícalo**

    ```sh
    beeuni ship
    ```

    La plataforma inicia la máquina de monitor cuando esto llega a la rama predeterminada.

3. **Confirma que se ejecuta**

    ```sh
    beeuni triggers ls
    beeuni triggers info checkout-errors
    ```

    `ls` muestra el modo y el intervalo de un monitor, mientras que un cron muestra su programación. `info` muestra `run`, `mode`, `interval` y `expect_event_within`.

<a id="monitor-limits"></a>

### Límites de los monitores

La plataforma aplica todos los límites que aparecen a continuación.

| Límite | Valor |
| --- | --- |
| Monitores por proyecto | 10 activados |
| Intervalo de sondeo | al menos 30 s |
| `expect_event_within` | al menos 5 min |
| Frecuencia de eventos por monitor | 60 por hora de forma sostenida, ráfaga de 30. Si se supera el límite, el monitor se suprime durante 10 minutos; 3 supresiones en 24 horas lo desactivan. |
| Longitud de línea | 8 KiB; se trunca con un marcador `truncated: true` |
| Límite de reinicios | 5 reinicios / 10 minutos; después, un evento de ciclo de vida `restart_budget_exhausted` y una espera de 15 minutos |
| Retención de eventos | 30 días |
| Presupuesto mensual de la máquina | $75 de forma predeterminada. Al superarlo, la máquina se detiene y se activa un evento de ciclo de vida `budget_exceeded`. |

La máquina de monitor necesita un proveedor compatible con un entorno aislado persistente. Si el proveedor del proyecto no puede mantenerlo, la marca `monitors` se indica como no disponible.

<a id="config-shape-1"></a>

## Estructura de la configuración

```yaml
# beeuni.yaml
triggers:
  - slug: daily-digest # required, lowercase + dashes, unique per project
    name: Daily digest # optional, defaults to slug
    type: cron # "cron" | "webhook" | "event" | "monitor", required
    agent: beeuni # optional, defaults to "default"
    model: anthropic/claude-sonnet-4-5 # optional, resolves at fire time if unset
    enabled: true # optional, default true
    cron: "0 0 9 * * 1-5" # 6-field expression, mutually exclusive with run_at
    timezone: America/Los_Angeles # IANA name, default UTC
    session_mode: reuse # "fresh" | "reuse" | "pinned" | "keyed", default "fresh"
    filter: # optional, webhook payload guard
      "body.data.direction": "inbound"
    prompt: "Summarize {{ body.text }}" # required, template string
```

Un monitor sustituye los campos de programación por su comando y modalidad:

```yaml
# beeuni.yaml
triggers:
  - slug: checkout-errors
    type: monitor
    run: ./monitors/checkout-errors.ts # required, repo-relative command
    mode: poll # "poll" | "stream", required
    interval: 60s # required on poll, invalid on stream
    expect_event_within: 24h # optional silence watchdog
    agent: oncall
    session_mode: reuse # the monitor default
    filter: # optional, same guard a webhook uses
      "line.severity": "error"
    prompt: "Checkout monitor emitted: {{ line }}"
```

Un activador de eventos sustituye los campos de programación por la app y el evento que observa. Consulta [Activadores de eventos de apps](#app-event-triggers).

La configuración heredada `beeuni.toml` usa los mismos campos en un contenedor distinto; consulta [TOML heredado](/docs/es/project/legacy-toml).

<a id="fields"></a>

## Campos

| Campo | Obligatorio | Valor predeterminado | Notas |
| --- | --- | --- | --- |
| `slug` | sí | — | `[a-z0-9][a-z0-9_-]{0,127}`, único por proyecto. |
| `type` | sí | — | `cron`, `webhook` o `monitor`. |
| `prompt` | sí | — | Cadena de plantilla. Se muestra como el primer mensaje de la sesión. |
| `name` | no | `slug` | Etiqueta descriptiva. |
| `agent` | no | `default_agent` | Debe ser el nombre de una clave de `agents:`. Omítelo para usar `default_agent`; no escribas el literal `default`. |
| `model` | no | se resuelve al activarse | Formato de cable `provider/model`, por ejemplo `anthropic/claude-sonnet-4-5`. |
| `enabled` | no | `true` | Si `false`, el programador y el receptor del webhook omiten la entrada. |
| `session_mode` | no | `fresh` o `reuse` en un monitor | Consulta [Estrategia de sesión](#session-strategy). |
| `session_id` | obligatorio para `pinned` | — | Sesión exacta a la que se volverá a enviar el mensaje. |
| `session_key` | obligatorio para `keyed` | — | Cadena de plantilla. Si se configura por sí sola, implica `session_mode: keyed`. |
| `filter` | no | — | Ruta con puntos → cadena esperada. Si una entrega del webhook no coincide, devuelve `200` y no activa ninguna sesión. |
| `cron` | uno de `cron`/`run_at`, en `type: cron` | — | Expresión de 6 campos: segundo, minuto, hora, día, mes y día de la semana. Se activa como máximo una vez cada 60 segundos; una expresión más frecuente produce un error en `beeuni validate` y en la API. |
| `run_at` | uno de `cron`/`run_at`, en `type: cron` | — | Marca de tiempo ISO-8601. Se activa una vez y luego queda inactivo. |
| `timezone` | no, solo cron | `UTC` | Nombre de IANA. |
| `secret_env` | obligatorio, solo webhook | — | Nombre de un secreto del proyecto que contiene la clave de firma. El secreto debe usar la entrega `broker` con el consumidor `connector`. |
| `run` | obligatorio, solo monitor | — | Comando relativo al repositorio cuyas líneas de salida estándar son los eventos. Una línea, de 1024 caracteres como máximo. |
| `mode` | obligatorio, solo monitor | — | `poll` vuelve a ejecutar `run` cada `interval`; `stream` lo ejecuta una vez y lo mantiene activo. |
| `interval` | obligatorio para `mode: poll` | — | Literal de duración (`30s`, `5m`, `24h`, `7d`), mínimo `30s`. No válido en `mode: stream`. |
| `expect_event_within` | no, solo monitor | — | Literal de duración, mínimo `5m`. Un silencio superior a este valor activa un evento del ciclo de vida. |

Un activador cron necesita `cron` o `run_at`, nunca ambos. Se rechaza un activador de webhook sin `secret_env`: no hay webhooks sin autenticación. Un monitor necesita `run` y `mode`, y rechaza directamente `cron`, `run_at`, `timezone` y `secret_env`: un manifiesto que declara una programación que el ejecutor del monitor nunca lee es una mentira.

Configura el secreto de firma antes de crear o actualizar un activador de webhook:

```bash
beeuni secrets set WEBHOOK_SECRET=-
beeuni secrets delivery WEBHOOK_SECRET broker --consumer connector
```

Pasa el valor al primer comando mediante la entrada estándar. Si el secreto se usó anteriormente para la entrega en sandbox, rótalo después de cambiar el método de entrega, ya que un sandbox existente puede conservar el valor anterior.

<a id="session-strategy"></a>

## Estrategia de sesión

`session_mode` controla a qué sesión se vuelve a enviar el mensaje al activarse. Beeuni prueba los modos siguientes en orden y pasa al siguiente si falla cada paso.

1. **`pinned`** — volver a enviar el mensaje a la `session_id` exacta. Si esa sesión ya no existe o falló, pasar al siguiente modo.
2. **`keyed`** — procesar `session_key` con la carga útil y, luego, buscar la sesión más reciente que no haya fallado y que tenga esa clave exacta. Si la clave se procesa como vacía o no hay ninguna sesión coincidente, pasar a una sesión nueva. Nunca pasa a la sesión de otra clave.
3. **`reuse`** — volver a enviar el mensaje a la sesión más reciente que no haya fallado y que este activador haya creado anteriormente. Un activador con sesión fijada también recurre a este modo antes de pasar al siguiente.
4. **`fresh`** — crear un sandbox y una rama nuevos. Es el valor predeterminado y el recurso final para todos los modos. La sesión nueva pasa a ser la sesión del activador para futuras activaciones `reuse` y `keyed`.

Una sesión `reuse` o `keyed` cuyo historial ya no cabe en el modelo, ni siquiera después de la compactación automática, se retira. La siguiente activación inicia una sesión nueva. Cualquier otro error conserva la sesión para la siguiente activación.

Un activador cron frecuente que no deba volver a enviar un historial que crece sin parar puede renovar la sesión al cambiar el período del calendario: `session_key: "merge-{{ cron.scheduled_date }}"` mantiene una sesión serializada por día UTC y `{{ cron.scheduled_hour }}`, una por hora UTC.

<a id="failed-runs"></a>

## Ejecuciones fallidas

Cuando una ejecución iniciada por un activador termina con un error, Beeuni lo registra en el activador:

- `last_status` pasa a ser `failed` y `last_error` contiene el motivo, por ejemplo `Out of credits: Payment Required: Insufficient credits.`
- La página Programación marca el activador como «La última ejecución no terminó» y muestra el motivo en su panel.
- El propietario de la cuenta recibe una notificación push móvil cuando falla la primera ejecución. Si falla una ejecución posterior, se actualiza el motivo sin enviar otra notificación push.
- El activador sigue `failed` mientras continúa activándose. Una nueva activación no demuestra que la ejecución funcione.

El siguiente intento que finalice borra el error. Una ejecución que detienes tú, un subagente y una llamada al modelo reintentada no cuentan como resultados de ejecución. Un error que se produce antes de que empiece una ejecución, por ejemplo, cuando no se puede crear una sesión, se borra con la siguiente activación que tenga éxito.

<a id="session-access"></a>

## Acceso a las sesiones

Las sesiones que crea un activador usan la política privada de forma predeterminada. Pertenecen a la cuenta de servicio del agente del activador: la identidad de un agente es un principal `service_account`, por lo que puede ser propietaria de una sesión y tener asignaciones como cualquier otro principal. Un administrador del proyecto siempre puede abrirlas. El propietario y el administrador de una cuenta tienen acceso equivalente al de un administrador en todos los proyectos, así que lo mismo se aplica a ellos. La persona que configuró o activó manualmente el activador no obtiene acceso por esa acción, a menos que tenga uno de esos roles.

La configuración de los activadores ofrece tres políticas:

- **Agente del activador y administradores del proyecto** — ningún miembro ordinario del proyecto puede abrir la sesión.
- **Compañeros seleccionados** — el agente del activador, los administradores del proyecto y los miembros del proyecto o grupos de cuentas seleccionados.
- **Todo el proyecto** — todos los miembros del proyecto.

Esta política es una opción de visibilidad por recurso que se superpone al modelo de roles; no es un rol. Decide quién puede abrir las sesiones de un activador. No concede ningún permiso que deniegue el resultado de los roles. Consulta [Cuentas y acceso](/docs/es/accounts#per-feature-access-settings).

La política de acceso es un estado de ejecución local a la cuenta. No se incluye en el manifiesto `beeuni.yaml` portátil porque los ID de principal pertenecen a una cuenta. Configúrala desde el panel.
Actualizar solo esta política no crea ningún commit de Git. Al guardar una política, también se actualizan las sesiones anteriores creadas por ese activador.

Una sesión fijada conserva su propia configuración para compartirla, ya que el activador no la creó. Si una sesión fijada no está disponible y el activador crea una sesión alternativa, la política del activador se aplica a esa sesión nueva.

<a id="payload-templating"></a>

## Plantillas de carga útil

`prompt` y `session_key` se procesan con el mismo motor: `{{ token.dotted.path }}`. Los valores que faltan se procesan como cadenas vacías: sin errores ni `{{ }}` sobrantes. Los objetos y las matrices se procesan como JSON. `session_key` se recorta y se trunca a 512 caracteres.

Cada activación también recibe `{{ trigger.slug }}`, `{{ trigger.type }}` y `{{ trigger.kind }}` (siempre `git`). El resto del conjunto de variables depende de cómo se haya activado el activador.

| Origen | Variables |
| --- | --- |
| cron | `{{ cron.schedule }}`, `{{ cron.timezone }}`, `{{ cron.scheduled_for }}` (la franja para la que se activa), `{{ cron.claimed_at }}` (cuándo la recogió el programador), `{{ cron.last_scheduled_for }}` (la franja anterior; vacío en la primera activación), `{{ cron.scheduled_date }}` (`YYYY-MM-DD` UTC de la franja), `{{ cron.scheduled_hour }}` (`YYYY-MM-DDTHH` UTC). No hay `fired_at` de nivel superior. |
| webhook | `{{ fired_at }}`, `{{ body.* }}` (analizado como JSON; si el cuerpo no se puede analizar, se usa `{{ body.raw }}`), `{{ headers.content_type }}`, `{{ headers.user_agent }}`, `{{ headers.forwarded_for }}`. |
| monitor | `{{ line.* }}`: la línea de stdout, analizada como JSON; si no se puede analizar, se muestra como `{{ line.raw }}`. También `{{ monitor.slug }}`, `{{ monitor.seq }}`, `{{ monitor.emitted_at }}` y `{{ monitor.kind }}` (`event` o `lifecycle`). |
| manual (botón «fire now» del panel o endpoint `fire`) | `{{ fired_at }}`, `{{ source }}` (`manual`), `{{ actor }}`, `{{ message.text }}`, `{{ message.source }}`. |

Beeuni antepone `[MONITOR EVENT — automated, not user input]` a todos los prompts de monitor renderizados, en el servidor. Un evento de ciclo de vida ignora por completo tu plantilla y renderiza un prompt escrito por la plataforma; además, omite `filter`: el silencio no debe poder filtrarse por accidente.

`{{ message.text }}` se fija en una cadena vacía al activarse manualmente, y `{{ message.source }}` en `manual_test`. La activación manual no sirve para inyectar datos de prueba en el prompt.

`filter` compara rutas con puntos como cadenas con la misma carga útil que recibe el prompt. Sirve para romper bucles. Por ejemplo, un origen que informa de ambos lados de una conversación volvería a activar el agente con su propia respuesta.

<a id="webhook-signature"></a>

## Firma del webhook

Se activa con `POST /v1/webhooks/projects/{projectId}/{slug}`. Beeuni verifica la solicitud en este orden, mediante una comparación de tiempo constante:

1. **Firma HMAC**: encabezado `X-Beeuni-Signature: sha256=<hmac>` (el prefijo `sha256=` es opcional) o `X-Hub-Signature-256`, compatible con GitHub. HMAC-SHA256 sobre el cuerpo sin procesar de la solicitud, usando el secreto indicado por `secret_env`.
2. **Token estático**, solo si no hay un encabezado de firma, para remitentes que no pueden firmar cuerpos con HMAC. Envía el secreto como `X-Beeuni-Token: <secret>`, `Authorization: Bearer <secret>` o `Authorization: Basic <base64(user:secret)>` (la parte de contraseña es el token).

`beeuni triggers info <slug>` muestra este esquema —el encabezado, el algoritmo y una solicitud de ejemplo firmada con el marcador de posición `$SECRET`— y `--json` lo incluye como `webhook_signing`. La hoja del activador del panel muestra la misma solicitud de ejemplo.

| Estado | Significado |
| --- | --- |
| 202 | La firma o el token son válidos. El cuerpo es `{ status: "fired" \| "queued" \| "deduped", session_id, ... }`. |
| 200 | Válida, pero omitida: el proyecto está pausado o la entrega no coincide con `filter`. |
| 400 | El ID o el slug del proyecto en la URL tienen un formato incorrecto. |
| 401 | Faltan tanto la firma como el token, o son incorrectos. |
| 404 | No se encuentra el activador, está deshabilitado, no es un webhook o el proyecto no está activo. |
| 409 | Falta el secreto de firma, está inactivo o no disponible, o no autoriza al consumidor `connector`. La respuesta incluye un código `webhook_secret_*` y cómo resolverlo. |
| 500 | La autenticación se completó, pero no se pudo activar la sesión. |

## Endpoints

| Método + ruta | Requisitos | Notas |
| --- | --- | --- |
| `GET /v1/projects/{projectId}/triggers` | `project.trigger.read` | Enumera los activadores, el estado de ejecución (`last_fired_at`, `last_status`, `last_error`, `last_attempt_at`) y los errores de análisis del manifiesto. Una entrada incorrecta aparece en `errors[]`; no afecta a los demás activadores. |
| `POST /v1/projects/{projectId}/triggers` | `project.trigger.create` | Crea un activador. Lo confirma directamente en el manifiesto. |
| `PATCH /v1/projects/{projectId}/triggers/{slug}` | `project.trigger.update` | Actualización parcial que se combina con la entrada actual. |
| `DELETE /v1/projects/{projectId}/triggers/{slug}` | `project.trigger.delete` | También borra el estado de ejecución del activador. |
| `PATCH /v1/projects/{projectId}/triggers/activation` | `project.trigger.update` | Cuerpo `{ paused: boolean }`. Consulta [Pausar y reanudar](#pause-and-resume). |
| `POST /v1/projects/{projectId}/triggers/{slug}/fire` | `project.trigger.fire` | Activación manual. El rol integrado `member` del proyecto tiene `project.trigger.fire`, por lo que un miembro normal puede activar un disparador. |
| `POST /v1/webhooks/projects/{projectId}/{slug}` | firma o token | URL pública protegida por el secreto del webhook. |

<a id="pause-and-resume"></a>

## Pausar y reanudar

Un interruptor a nivel de proyecto detiene todos sus activadores a la vez, independientemente del campo `enabled` de cada uno. Mientras el proyecto está pausado, el programador lo omite y los webhooks entrantes devuelven `200` con `{ status: "skipped" }`: no se activa ninguna sesión. La activación manual sigue funcionando. Úsalo cuando el mismo repositorio se ejecute en dos planos de control (por ejemplo, desarrollo y producción) para evitar que cron se active dos veces. CLI: `beeuni triggers pause` y `beeuni triggers resume`. Consulta [CLI](/docs/es/cli) para ver el grupo completo de comandos `beeuni triggers`.

<a id="limits-and-reliability"></a>

## Límites y fiabilidad

- El programador consulta aproximadamente cada segundo (1.000 ms de forma predeterminada; configurable mediante `BEEUNI_TRIGGER_SCHEDULER_INTERVAL_MS`). Una ejecución de cron no empieza exactamente en su franja: cada activador recibe un desfase fijo de hasta una cuarta parte de su período, con un máximo de 30 minutos, para que muchos activadores programados para el mismo minuto no empiecen a la vez. El detalle del activador y `GET /triggers` (`next_fire_at`) muestran cuándo empieza la siguiente ejecución.
- Si una activación falla, el último estado del activador pasa a ser fallido y se muestra el error. Esto se aplica a todos los orígenes: cron, manual, webhook, evento de aplicación y monitor. La siguiente activación que se complete correctamente borra el estado de fallo. Una ejecución fallida se borra según se describe en [Ejecuciones fallidas](#failed-runs).
- Cada proyecto permite aprovisionar 3 sesiones activadas a la vez de forma predeterminada. Si se supera ese límite, la activación devuelve `queued` (`202`) en lugar de fallar y se ejecuta cuando termine el aprovisionamiento de una de ellas. No hay límite en el número de sesiones que pueden ejecutarse a la vez.
- Las activaciones manuales y de webhook tienen un tiempo de espera de 45 segundos. La carga del manifiesto tiene un tiempo de espera de 30 segundos.
- Las activaciones de cron se identifican por la franja programada correspondiente; así, si una activación agota el tiempo de espera pero se ejecuta, no se duplica al reintentarse.
- Cada entrega de webhook se ejecuta una vez por evento. Beeuni lee el ID del evento de `X-Beeuni-Delivery-Id`, `X-GitHub-Delivery`, `X-Gitlab-Event-UUID`, `Linear-Delivery`, `webhook-id` o `svix-id`, en ese orden. Un reintento con el mismo ID devuelve `202 { status: "deduped" }` y no ejecuta nada. `X-Request-Id` no es un ID de evento. Si no hay ID de evento, un cuerpo y una firma idénticos en un intervalo de 10 minutos se consideran el mismo evento; pasado ese plazo, se vuelve a ejecutar. Por tanto, un trabajo que envía el mismo cuerpo estático en cada implementación se ejecuta en cada una. Envía `X-Beeuni-Delivery-Id` con un valor nuevo por evento (la solicitud de ejemplo usa `$(uuidgen)`).
- Una entrega cuyo intento anterior falló definitivamente (por ejemplo, porque la cuenta se quedó sin créditos) o cuya sesión se eliminó vuelve a ejecutarse si se reenvía con el mismo ID. Un duplicado no modifica `last_fired_at`.
