# Auslöser

Ein Auslöser startet eine Sitzung nach einem Zeitplan, über einen Webhook, durch ein App-Ereignis oder durch einen Monitor.

Canonical page: https://beeuni.ai/docs/de/connect/triggers

Ein Trigger startet eine [Sitzung](/docs/de/work/sessions), ohne dass eine Person anwesend ist. Verwende einen Trigger, um wiederkehrende oder ereignisgesteuerte Aufgaben zu automatisieren.

Um die Aufgabe einer Sitzung später weiterzuverfolgen – „prüfe morgen, ob sie geantwortet haben“ – verwende stattdessen eine [Erinnerung](/docs/de/connect/reminders). Eine Erinnerung ist ein auf diese Sitzung beschränkter Trigger. Sie liegt in der Datenbank, nicht in `beeuni.yaml`.

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

## Trigger-Typen

Beeuni unterstützt vier Trigger-Typen.

- **cron** — wird nach einem von dir festgelegten Zeitplan ausgeführt.
- **webhook** — wird ausgeführt, wenn ein externer Dienst eine signierte Anfrage an die Webhook-URL des Projekts sendet.
- **event** — wird ausgeführt, wenn in einer verbundenen App etwas passiert, etwa ein neuer Pull Request oder eine neue E-Mail. Siehe [App-Ereignis-Trigger](#app-event-triggers).
- **monitor** — führt rund um die Uhr einen Befehl aus deinem Repository aus. Jede Zeile, die der Befehl auf stdout ausgibt, löst den Trigger aus. Experimentell: siehe [Monitore](#monitors).

Du definierst Trigger im Projektmanifest `beeuni.yaml`. Jeder Trigger enthält einen Prompt, der als erste Nachricht der ausgelösten Sitzung angezeigt wird. Der Laufzeitstatus – etwa der Zeitpunkt der letzten Auslösung und der Status – befindet sich außerhalb des Manifests in der Datenbank. Das Auslösen eines Triggers erstellt keinen Commit.

Wenn du einen Trigger über das Dashboard erstellst, aktualisierst oder löschst, wird direkt in den Standard-Branch geschrieben. Das läuft nicht über eine [Änderungsanfrage](/docs/de/work/change-requests) (CR). Wenn du `beeuni.yaml` innerhalb einer Sitzung bearbeitest und `beeuni ship` ausführst, gilt stattdessen der normale Branch- und CR-Ablauf.

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

## Cron-Trigger einrichten

### Trigger hinzufügen
```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` ist ein Ausdruck mit 6 Feldern: Sekunde, Minute, Stunde, Tag, Monat, Wochentag. Ein Trigger wird höchstens einmal pro Minute ausgelöst: `0 */30 * * * *` bedeutet alle 30 Minuten, und `*/30 * * * * *` (alle 30 Sekunden) wird abgelehnt. Dieser Befehl bearbeitet nur dein lokales `beeuni.yaml`.

### Veröffentlichen
```sh
beeuni ship
```

`beeuni ship` committet `beeuni.yaml` und pusht es. Der Zeitplan wird aktiv, sobald dies im Standard-Branch deines Projekts landet.

### Ausführung bestätigen
```sh
beeuni triggers ls
```

Die Liste zeigt den Slug und den Status jedes Triggers sowie den Zeitpunkt seiner letzten Auslösung. Um ihn jetzt statt nach Zeitplan auszulösen, führe `beeuni triggers fire daily-digest` aus. Der Befehl wartet auf das Ergebnis der Ausführung und endet bei einem Fehler mit einem von null verschiedenen Exit-Code und dem Fehlertext; `--wait <dur>` begrenzt die Wartezeit (Standardwert `90s`).

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

## Webhook-Trigger einrichten

Ein Webhook-Trigger benötigt ein Secret. Beeuni verwendet es, um die Signatur jeder eingehenden Anfrage zu prüfen.

### Secret hinzufügen
```sh
beeuni secrets set WEBHOOK_SECRET=<a-random-value>
```

Weitere Informationen zu Secrets findest du unter [Secrets](/docs/de/project/secrets).

### Trigger hinzufügen
```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` gibt das Secret an, mit dem Anfragen an diesen Trigger signiert werden.

### Veröffentlichen
```sh
beeuni ship
```

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

### Anfrage senden

Beeuni erstellt die Webhook-URL aus deiner Projekt-ID und dem Slug des Triggers:

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

Sende von einem externen Dienst eine signierte `POST`-Anfrage an diese URL. Beeuni prüft die Signatur anhand von `WEBHOOK_SECRET` und startet dann eine Sitzung, in deren Prompt der Anfrage-Body verfügbar ist. Das genaue Header- und Signaturformat findest du weiter unten unter [Webhook-Signatur](#webhook-signature).

Standardmäßig startet jede Auslösung eine neue Sitzung in einem neuen Branch. Ein Trigger kann stattdessen eine Sitzung wiederverwenden oder festlegen; ein Webhook-Trigger kann außerdem filtern, welche Payloads eine Sitzung starten – siehe weiter unten [Sitzungsstrategie](#session-strategy) und [Payload-Vorlagen](#payload-templating).

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

## App-Ereignis-Trigger

Ein Ereignis-Trigger führt einen Agenten aus, wenn ein App-Ereignis eintritt: ein Pull Request wird geöffnet, eine E-Mail kommt an, der Status eines Issues ändert sich. Du musst weder einen Webhook noch ein Secret oder eine Signatur einrichten. Beeuni abonniert das Ereignis für dich und signiert nichts von Hand.

Ein Ereignis stammt aus einer **Ereignisquelle**: einem Adapter, der die Ereignisse einer App bereitstellt. IDs wie `GITHUB_PULL_REQUEST_CREATED` bezeichnen die von dieser Quelle unterstützten Ereignisse. Wähle im Ereigniskatalog ein Ereignis für deinen Connector aus.

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

### In der Web-App einrichten

1. Öffne das Projekt und dann **Triggers** und **New trigger**.
2. Wähle **App event**.
3. Wähle eine App aus. Wähle darunter einen Connector und eines seiner freigegebenen Konten oder verbinde ein weiteres Konto. Das als **Default** markierte Konto wird verwendet, sofern du kein anderes auswählst.
4. Wähle ein Ereignis aus. Darunter erscheint das Formular für die Einstellungen des Ereignisses, etwa das Repository.
5. Schreibe den Prompt und speichere.

Der Trigger ist aktiv, sobald sein Badge **Live** anzeigt. Die anderen Badges lauten **Activating**, **Needs connection** und **Error**. Sie entsprechen den unten genannten Statuswerten `pending`, `needs_connection` und `error`.

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

### Alle App-Ereignis-Trigger anzeigen

Öffne in der Web-App **Triggers** und wähle **App events**. Die Filterzeile zeigt **All**, **Schedules**, **App events** und **Webhooks**, jeweils mit einer Anzahl. Die Auswahl wird als `?type=event` in der Adresse gespeichert, sodass du direkt darauf verlinken kannst. Die Zeilen sind nach App gruppiert. Darunter listet **Apps with events** alle Apps auf, die einen Agenten starten können; Apps, die dein Projekt bereits verwendet, stehen zuerst. Wähle eine App aus, um das Formular zum Erstellen eines Triggers für diese App zu öffnen.

Das Fenster eines Connectors enthält einen Tab **Triggers** mit den App-Ereignissen dieses Connectors. Der Tab wird nur für Apps mit Ereignissen angezeigt.

Mit der 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` akzeptiert außerdem `cron`, `webhook` und `monitor`. `--connector` und `--type` lassen sich kombinieren.

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

### Mit der CLI einrichten

Ein Agent oder eine Person führt diese Schritte der Reihe nach aus. Jeder Schritt gibt aus, was der nächste Schritt benötigt.

```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` listet die Apps auf, die Ereignisse auslösen können. Die Apps sind nach ihrer Ereignisquelle gruppiert (`source: composio`). Für jede App werden ihre Connectoren (Profile) und deren freigegebene Konten aufgeführt. In der Spalte `STATE` steht `connected` oder `needs account`; Apps ohne Connector erscheinen unter `No connector yet`.
2. `connectors add` fügt den Connector hinzu, wenn die App noch keinen hat. Überspringe diesen Schritt, wenn `CONNECTOR` gesetzt ist.
3. `connectors connect <slug> --owner project` gibt einen Link aus. Eine Person öffnet ihn, um das freigegebene Konto zu verbinden. Das private Konto eines Mitglieds zählt nicht. Führe den Befehl erneut aus, um ein zweites freigegebenes Konto hinzuzufügen, und gib ihm dann mit `connectors rename <id> <label>` eine Bezeichnung.
4. `triggers events --connector <slug>` listet die Ereignisse auf. Ergänze `--event <TYPE>` für die Einstellungen eines Ereignisses und dessen Variablen `{{ event.data.* }}`. Lies die Beschreibung jeder Einstellung: Bei manchen Ereignissen muss der vollständige Name in einer Einstellung angegeben werden, etwa `repo: hekmat/api`, und derselbe Wert wird abgelehnt, wenn er auf `owner` und `repo` aufgeteilt ist.
5. `triggers add --apply` erstellt den Trigger sofort und gibt seinen Status sowie den nächsten Schritt aus. Bei einer fehlenden oder ungültigen Einstellung wird der Befehl mit Code 2 beendet und listet alle Probleme auf. Ohne `--apply` schreibt die CLI den Block in das lokale `beeuni.yaml`; führe `beeuni ship` aus, um ihn anzuwenden.
6. `triggers info <slug>` zeigt den Status an. Wiederhole den Schritt, bis `live` angezeigt wird.

`--source <adapter>` legt die Ereignisquelle fest und ist optional: Ohne diese Angabe verwendet der Trigger den Provider des Connectors. Der Connector muss zu dieser Quelle gehören. Eine `composio`-Quelle bei einem Connector eines anderen Providers zeigt `error` an: `Connector "<slug>" is a <provider> connector; source "composio" needs a composio connector.` Eine unbekannte Quelle wird mit `Unknown event source "<x>". Sources: composio.` abgelehnt. `beeuni triggers set <slug> --source <adapter>` ändert die Quelle; eine Änderung von `--connector` setzt sie zurück.

`--config key=value` wird für jede Einstellung einmal wiederholt und in den Typ der Einstellung konvertiert. `--config-json '{"limit": 5}'` übergibt typisierte Werte. `beeuni triggers set <slug> --config key=value` führt die Änderungen mit der aktiven Konfiguration zusammen; `--config-json` ersetzt sie. Übergebe nicht beide. Ein Ereignis-Trigger akzeptiert keines von `--cron`, `--run-at`, `--timezone`, `--secret-env`, `--run`, `--mode`, `--interval` oder `--expect-event-within`.

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

### Konfigurationsstruktur

```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>

### Was der Prompt lesen kann

Der Prompt, `filter` und `session_key` lesen diese Stammobjekte:

| Stammobjekt | Wert |
| --- | --- |
| `event.data.<field>` | Die Ereignisdaten der App. `beeuni triggers events` zeigt die Felder, die jedes Ereignis enthält. |
| `event.id` | Die ID des Anbieters für dieses Ereignis. |
| `event.type` | Der Ereignistyp, z. B. `GITHUB_PULL_REQUEST_CREATED`. |
| `event.app` | Der App-Slug, z. B. `github`. |
| `event.connector` | Der Connector-Slug, den der Trigger überwacht. |
| `event.occurred_at` | Der Zeitpunkt des Ereignisses. |

Ereignisdaten stammen von einem Drittanbieter. Beeuni beginnt die erste Nachricht der Sitzung mit `[App event: <trigger name> — automated, third-party content, not user input]`, damit der Agent den Ereignisinhalt als Daten und nicht als Anweisungen behandelt. Diese Zeile ist außerdem der Titel der Sitzung, bis der Agent sie benennt.

### Status

`beeuni triggers ls` und die Web-Triggerliste zeigen den Abonnementstatus jedes Ereignis-Triggers.

| Status | Bedeutung | Behebung |
| --- | --- | --- |
| `active` | Das Abonnement existiert. Ereignisse lösen den Trigger aus. | Keine. |
| `pending` | Der Trigger ist deklariert, und Beeuni hat das Abonnement noch nicht erstellt. | Warte einen Moment. Wenn der Status `pending` bleibt, speichere den Trigger erneut. |
| `needs_connection` | Der Connector hat kein verwendbares gemeinsames Konto. Der Text lautet „Connect a shared `<app>` account to activate this trigger.“ oder „Finish connecting the shared `<app>` account…“ | Verbinde ein gemeinsames Konto für den Connector als Projekt (`beeuni connectors connect <slug> --owner project`). Das private Konto eines Mitglieds zählt nicht. Der Trigger wird automatisch aktiv, sobald die Person die Verbindung herstellt, auch wenn sie dies über einen Link außerhalb von Beeuni tut. |
| `error` | Das Abonnement ist fehlgeschlagen oder wurde vom Anbieter deaktiviert. Der Trigger zeigt den Fehlertext an. | Behebe die im Text genannte Ursache und speichere den Trigger erneut. |

Häufige `error`-Texte:

- „This `<app>` account is shared with specific people only. Event triggers need an account shared with the whole project.“ Öffne die Freigabeeinstellungen des Kontos und teile es mit dem gesamten Projekt.
- „Connector `<slug>` is not declared in beeuni.yaml.“ Füge den Connector unter `connectors:` hinzu.
- Wenn Ereignis-Trigger nicht verfügbar sind, wende dich an den Support.

- „The app disabled this subscription: ...“ Der Anbieter hat das Abonnement deaktiviert. Speichere den Trigger erneut, um das Abonnement erneut abzuschließen.
- „The connected account expired: ...“ Die App hat das Konto widerrufen oder es ist abgelaufen. Verbinde die App erneut, um den Trigger fortzusetzen.

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

### Regeln

- **Connector und Konto.** Eine *App* ist der Dienst, z. B. GitHub. Ein *Connector* ist ein Profil in `beeuni.yaml`; mehrere Connectoren können dieselbe App verwenden. Ein *Konto* ist eine verbundene Anmeldung unter einem Connector. Ein Trigger benennt einen Connector. Er verwendet das standardmäßige gemeinsame Konto dieses Connectors, sofern er nicht `account: <label>` setzt. Setze `account` nur, wenn ein Connector mehrere gemeinsame Konten enthält. In der CLI setzt `--account <label>` den Wert, und `--default-account` (auf `triggers set`) entfernt ihn. Ein Label, das zu keinem gemeinsamen Konto passt, erzeugt `needs_connection` mit dem Text „Connect a shared `<app>` account labelled „`<label>`“ on `<connector>`.“
- **Ein einspeisendes Konto.** Nur ein mit dem gesamten Projekt geteiltes Projektkonto kann einen Trigger speisen. Ein Trigger läuft ohne anwesende Person, und Ereignisdaten gelangen in einen Sitzungs-Prompt. Ein Konto, das nur mit bestimmten Personen geteilt ist, oder ein privates Konto speist niemals einen Trigger.
- **Jedes Ereignis löst einmal aus.** Ein Trigger wird einmal pro Anbieterereignis ausgelöst. Ein erneut zugestelltes Ereignis startet keine zweite Sitzung. Zwei Trigger für dasselbe Ereignis werden jeweils einmal ausgelöst.
- **Pausieren stoppt den Trigger.** Ein pausierter Trigger oder ein pausiertes Projekt überspringt Ereignisse. Übersprungene Ereignisse werden nicht in die Warteschlange gestellt.
- **Änderungen schließen das Abonnement erneut ab.** Das Ändern von `connector`, `source`, `account`, `event` oder `config` ersetzt das Abonnement. Durch das Löschen des Triggers wird es entfernt.

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

## Monitore

**Experimentell.** Monitore laufen nur, wenn das Feature-Flag `monitors` aktiviert ist. Das Flag ist standardmäßig deaktiviert. Solange es deaktiviert ist, stellt die Plattform keine Monitor-Box bereit und löst keine Monitorereignisse aus.

Ein Monitor überwacht etwas, das weder Webhooks sendet noch in einen Zeitplan passt: ein Live-Log, eine Warteschlangentiefe, eine sich ändernde Seite oder einen Preis. Beeuni führt deinen Befehl rund um die Uhr in der Monitor-Box des Projekts aus – eine persistente MicroVM pro Projekt mit derselben Isolationsgrenze und denselben Projektgeheimnissen wie in einer Sitzungs-Sandbox. Deterministischer Code überwacht; der Agent wird nur aktiv, wenn der Befehl eine Zeile ausgibt.

Vier Regeln definieren den Vertrag:

- **Stdout-Zeilen sind Ereignisse. Nichts anderes ist ein Ereignis.** Stderr dient der Diagnose: Es ist in den Monitor-Logs sichtbar, löst aber niemals etwas aus.
- Jede Zeile löst den Trigger genau einmal aus, über denselben Pfad wie ein bereits verwendeter Webhook: `filter` → Prompt-Vorlage → `session_mode`.
- **Ein Monitor kann nicht unbemerkt fehlschlagen.** Prozessende, Erschöpfung des Neustartbudgets und eine längere Stille als `expect_event_within` lösen jeweils ein von der Plattform geschriebenes Lebenszyklusereignis im selben Stream aus.
- `session_mode` ist bei einem Monitor standardmäßig auf `reuse` gesetzt, nicht auf `fresh`. Ein Monitor wird absichtlich wiederholt ausgelöst, daher würde `fresh` eine Sitzung pro Ereignis erstellen.

### Monitor hinzufügen
```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` führt den Befehl alle `--interval` erneut aus und erwartet, dass er beendet wird. `--mode stream` führt ihn einmal aus und lässt ihn weiterlaufen; ein Stream benötigt kein `--interval`. Beide Formen erzeugen Zeilen, und nachgelagerte Komponenten können sie nicht unterscheiden. `cron`, `run_at`, `timezone` und `secret_env` werden bei einem Monitor abgelehnt.

### Veröffentlichen
```sh
beeuni ship
```

Die Plattform startet die Monitor-Box, sobald dies im Standard-Branch angekommen ist.

### Ausführung bestätigen
```sh
beeuni triggers ls
beeuni triggers info checkout-errors
```

`ls` zeigt den Modus und das Intervall eines Monitors, während ein Cronjob seinen Zeitplan anzeigt. `info` zeigt `run`, `mode`, `interval` und `expect_event_within`.

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

### Monitorgrenzen

Jede der folgenden Grenzen wird von der Plattform durchgesetzt.

| Grenze | Wert |
| --- | --- |
| Monitore pro Projekt | 10 aktiviert |
| Abfrageintervall | mindestens 30 s |
| `expect_event_within` | mindestens 5 Min. |
| Ereignisrate pro Monitor | 60/Stunde dauerhaft, Burst 30. Bei Überlauf wird der Monitor 10 Minuten unterdrückt; 3 Unterdrückungen innerhalb von 24 Stunden deaktivieren ihn. |
| Zeilenlänge | 8 KiB, gekürzt mit einer `truncated: true`-Markierung |
| Neustartbudget | 5 Neustarts / 10 Minuten, danach ein `restart_budget_exhausted`-Lebenszyklusereignis und 15 Minuten Wartezeit |
| Ereignisaufbewahrung | 30 Tage |
| Monatliches Box-Budget | Standardmäßig 75 $. Danach wird die Box angehalten und ein `budget_exceeded`-Lebenszyklusereignis ausgelöst. |

Die Monitor-Box benötigt einen Anbieter, der eine persistente Sandbox unterstützt. Wenn der Anbieter des Projekts keine solche Sandbox bereitstellen kann, meldet das Flag `monitors`, dass es nicht verfügbar ist.

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

## Konfigurationsstruktur

```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
```

Ein Monitor ersetzt die Zeitplanfelder durch seinen Befehl und seine Form:

```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 }}"
```

Ein Ereignis-Trigger ersetzt die Zeitplanfelder durch die App und das Ereignis, die er überwacht. Siehe [App event triggers](#app-event-triggers).

Legacy-`beeuni.toml` verwendet dieselben Felder in einem anderen Container; siehe [legacy TOML](/docs/de/project/legacy-toml).

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

## Felder

| Feld | Erforderlich | Standardwert | Hinweise |
| --- | --- | --- | --- |
| `slug` | ja | — | `[a-z0-9][a-z0-9_-]{0,127}`, eindeutig pro Projekt. |
| `type` | ja | — | `cron`, `webhook` oder `monitor`. |
| `prompt` | ja | — | Vorlagenstring. Wird als erste Nachricht der Sitzung angezeigt. |
| `name` | nein | `slug` | Bezeichnung für Menschen. |
| `agent` | nein | `default_agent` | Muss einen Schlüssel in `agents:` benennen. Weglassen, um `default_agent` zu verwenden; nicht den Literalwert `default` schreiben. |
| `model` | nein | wird zum Auslösezeitpunkt aufgelöst | Drahtformat `provider/model`, zum Beispiel `anthropic/claude-sonnet-4-5`. |
| `enabled` | nein | `true` | Wenn `false`, überspringen der Scheduler und der Webhook-Empfänger den Eintrag. |
| `session_mode` | nein | `fresh` oder bei einem Monitor `reuse` | Siehe [Sitzungsstrategie](#session-strategy). |
| `session_id` | erforderlich für `pinned` | — | Die genaue Sitzung, für die erneut eine Eingabeaufforderung gesendet werden soll. |
| `session_key` | erforderlich für `keyed` | — | Vorlagenstring. Wenn nur dieses Feld festgelegt ist, wird `session_mode: keyed` angenommen. |
| `filter` | nein | — | Punktierter Pfad → erwarteter String. Eine Webhook-Zustellung, die nicht übereinstimmt, gibt `200` zurück und löst keine Sitzung aus. |
| `cron` | eines von `cron`/`run_at`, bei `type: cron` | — | Ausdruck mit 6 Feldern: Sekunde Minute Stunde Tag Monat Wochentag. Wird höchstens einmal alle 60 Sekunden ausgelöst; ein schnellerer Ausdruck schlägt bei `beeuni validate` und der API fehl. |
| `run_at` | eines von `cron`/`run_at`, bei `type: cron` | — | ISO-8601-Zeitstempel. Wird einmal ausgelöst und bleibt dann inaktiv. |
| `timezone` | nein, nur Cron | `UTC` | IANA-Name. |
| `secret_env` | erforderlich, nur Webhook | — | Name eines Projektschlüssels, der den Signaturschlüssel enthält. Der Schlüssel muss die Zustellung `broker` mit dem Consumer `connector` verwenden. |
| `run` | erforderlich, nur Monitor | — | Ein relativ zum Repository angegebener Befehl, dessen stdout-Zeilen die Ereignisse sind. Eine Zeile, höchstens 1024 Zeichen. |
| `mode` | erforderlich, nur Monitor | — | `poll` führt `run` alle `interval` erneut aus; `stream` führt ihn einmal aus und hält ihn am Laufen. |
| `interval` | erforderlich für `mode: poll` | — | Dauerangabe (`30s`, `5m`, `24h`, `7d`), mindestens `30s`. Ungültig bei `mode: stream`. |
| `expect_event_within` | nein, nur Monitor | — | Dauerangabe, mindestens `5m`. Bei längerer Stille wird ein Lebenszyklusereignis ausgelöst. |

Ein Cron-Trigger benötigt `cron` oder `run_at`, niemals beides. Ein Webhook-Trigger ohne `secret_env` wird abgelehnt – es gibt keine nicht authentifizierten Webhooks. Ein Monitor benötigt `run` und `mode` und lehnt `cron`, `run_at`, `timezone` und `secret_env` ausnahmslos ab – ein Manifest, das einen Zeitplan angibt, den der Monitor-Runner nie liest, ist eine Lüge.

Richten Sie das Signaturgeheimnis ein, bevor Sie einen Webhook-Trigger erstellen oder aktualisieren:

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

Übergeben Sie den Wert dem ersten Befehl über die Standardeingabe. Wenn das Geheimnis zuvor für Sandbox-Zustellungen verwendet wurde, tauschen Sie es nach der Änderung der Zustellung aus, da eine bestehende Sandbox den vorherigen Wert beibehalten kann.

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

## Sitzungsstrategie

`session_mode` bestimmt, für welche Sitzung bei einer Auslösung erneut eine Eingabeaufforderung gesendet wird. Beeuni versucht die folgenden Modi der Reihe nach und geht bei jedem Schritt im Fehlerfall zum nächsten über.

1. **`pinned`** — erneut eine Eingabeaufforderung an genau `session_id` senden. Wenn diese Sitzung nicht mehr vorhanden ist oder fehlgeschlagen ist, fortfahren.
2. **`keyed`** — `session_key` mit der Nutzlast rendern und dann nach der neuesten nicht fehlgeschlagenen Sitzung suchen, die zuvor mit genau diesem Schlüssel markiert wurde. Wenn der Schlüssel leer gerendert wird oder keine Sitzung übereinstimmt, eine neue Sitzung starten. Es wird niemals auf eine Sitzung mit einem anderen Schlüssel zurückgegriffen.
3. **`reuse`** — erneut eine Eingabeaufforderung an die neueste nicht fehlgeschlagene Sitzung senden, die dieser Trigger zuvor erstellt hat. Auch ein fixierter Trigger greift zuerst hierauf zurück, bevor er weiter fortfährt.
4. **`fresh`** — eine neue Sandbox und einen neuen Branch erstellen. Dies ist die Standardeinstellung und der letzte Rückfall für jeden Modus. Die neue Sitzung wird für künftige Auslösungen von `reuse` und `keyed` zur Sitzung des Triggers.

Eine Sitzung vom Typ `reuse` oder `keyed`, deren Verlauf selbst nach automatischer Komprimierung nicht mehr in das Modell passt, wird stillgelegt. Bei der nächsten Auslösung wird eine neue Sitzung gestartet. Bei allen anderen Fehlern bleibt die Sitzung für die nächste Auslösung erhalten.

Ein häufiger Cron-Trigger, der nicht bei jeder Auslösung einen immer länger werdenden Verlauf erneut senden soll, kann seine Sitzung an einem Kalenderwechsel erneuern: `session_key: "merge-{{ cron.scheduled_date }}"` behält eine serialisierte Sitzung pro UTC-Tag und `{{ cron.scheduled_hour }}` eine pro UTC-Stunde.

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

## Fehlgeschlagene Ausführungen

Wenn eine vom Trigger gestartete Ausführung mit einem Fehler endet, zeichnet Beeuni dies beim Trigger auf:

- `last_status` wird zu `failed` und `last_error` enthält den Grund, zum Beispiel `Out of credits: Payment Required: Insufficient credits.`
- Auf der Seite „Schedule“ wird der Trigger mit „Letzter Lauf nicht abgeschlossen“ markiert und der Grund im zugehörigen Bereich angezeigt.
- Der Kontoinhaber erhält bei der ersten fehlgeschlagenen Ausführung eine mobile Push-Benachrichtigung. Bei einer späteren fehlgeschlagenen Ausführung wird der Grund aktualisiert, ohne eine weitere Push-Benachrichtigung zu senden.
- Der Trigger bleibt `failed`, während er weiterhin ausgelöst wird. Eine neue Auslösung beweist nicht, dass die Ausführung funktioniert.

Die nächste abgeschlossene Ausführung hebt den Fehler auf. Eine Ausführung, die Sie selbst stoppen, ein Subagent und ein erneut versuchter Modellaufruf gelten nicht als Ausführungsergebnisse. Ein Fehler bei einer Auslösung, bevor eine Ausführung startet – zum Beispiel, wenn keine Sitzung erstellt werden kann –, wird bei der nächsten erfolgreichen Auslösung aufgehoben.

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

## Sitzungszugriff

Sitzungen, die ein Trigger erstellt, verwenden standardmäßig die private Richtlinie. Sie gehören dem Dienstkonto des Trigger-Agenten – die Identität eines Agenten ist ein `service_account` Principal, kann also wie jeder andere Principal eine Sitzung besitzen und Zuweisungen halten. Ein Projektmanager kann sie jederzeit öffnen. Ein Kontoinhaber und ein Kontoadministrator haben in jedem Projekt einen Zugriff, der dem eines Managers entspricht; für sie gilt daher dasselbe. Die Person, die den Trigger konfiguriert oder manuell ausgelöst hat, erhält durch diese Aktion keinen Zugriff, sofern sie nicht eine dieser Rollen innehat.

In den Triggereinstellungen stehen drei Richtlinien zur Auswahl:

- **Trigger-Agent und Projektmanager** — kein gewöhnliches Projektmitglied kann die Sitzung öffnen.
- **Ausgewählte Teammitglieder** — der Trigger-Agent, Projektmanager und ausgewählte Projektmitglieder oder Kontogruppen.
- **Ganzes Projekt** — alle Projektmitglieder.

Diese Richtlinie ist eine Sichtbarkeitseinstellung pro Ressource, die zusätzlich zum Rollenmodell gilt, aber keine Rolle. Sie legt fest, wer die Sitzungen eines einzelnen Triggers öffnen kann. Sie erteilt keine Berechtigung, die das Rollenmodell verweigert. Siehe [Konten und Zugriff](/docs/de/accounts#per-feature-access-settings).

Die Zugriffsrichtlinie ist ein kontolokaler Laufzeitstatus. Sie wird nicht in das portable Manifest `beeuni.yaml` aufgenommen, da Principal-IDs zu einem einzelnen Konto gehören. Konfigurieren Sie sie über das Dashboard.
Wird nur diese Richtlinie aktualisiert, entsteht kein Git-Commit. Beim Speichern einer Richtlinie werden auch frühere Sitzungen aktualisiert, die von diesem Trigger erstellt wurden.

Eine fixierte Sitzung behält ihre eigenen Freigabeeinstellungen, da der Trigger sie nicht erstellt hat. Wenn eine fixierte Sitzung nicht verfügbar ist und der Trigger eine Ersatzsitzung erstellt, gilt für die neue Sitzung die Trigger-Richtlinie.

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

## Nutzlastvorlagen

`prompt` und `session_key` werden mit derselben Engine gerendert: `{{ token.dotted.path }}`. Ein fehlender Wert wird als leerer String gerendert – ohne Fehler und ohne zurückbleibendes `{{ }}`. Objekte und Arrays werden als JSON gerendert. `session_key` wird gekürzt und auf 512 Zeichen begrenzt.

Bei jeder Auslösung stehen außerdem `{{ trigger.slug }}`, `{{ trigger.type }}` und `{{ trigger.kind }}` zur Verfügung (immer `git`). Welche weiteren Variablen verfügbar sind, hängt davon ab, wie der Trigger ausgelöst wurde.

| Quelle | Variablen |
| --- | --- |
| cron | `{{ cron.schedule }}`, `{{ cron.timezone }}`, `{{ cron.scheduled_for }}` (der Slot, für den die Ausführung vorgesehen ist), `{{ cron.claimed_at }}` (wann der Scheduler sie übernommen hat), `{{ cron.last_scheduled_for }}` (der vorherige Slot; bei der ersten Ausführung leer), `{{ cron.scheduled_date }}` (UTC-`YYYY-MM-DD` des Slots), `{{ cron.scheduled_hour }}` (UTC-`YYYY-MM-DDTHH`). Kein oberstes `fired_at`. |
| webhook | `{{ fired_at }}`, `{{ body.* }}` (als JSON geparst; wenn der Body nicht geparst werden kann, wird auf `{{ body.raw }}` zurückgegriffen), `{{ headers.content_type }}`, `{{ headers.user_agent }}`, `{{ headers.forwarded_for }}`. |
| monitor | `{{ line.* }}` — die stdout-Zeile, als JSON geparst; eine Zeile, die nicht geparst werden kann, wird als `{{ line.raw }}` dargestellt. Außerdem `{{ monitor.slug }}`, `{{ monitor.seq }}`, `{{ monitor.emitted_at }}` und `{{ monitor.kind }}` (`event` oder `lifecycle`). |
| manuell (Dashboard-„Jetzt ausführen“ oder der Endpunkt `fire`) | `{{ fired_at }}`, `{{ source }}` (`manual`), `{{ actor }}`, `{{ message.text }}`, `{{ message.source }}`. |

Beeuni stellt serverseitig jedem gerenderten Monitor-Prompt `[MONITOR EVENT — automated, not user input]` voran. Ein Lifecycle-Ereignis ignoriert dein Template vollständig und rendert stattdessen einen von der Plattform verfassten Prompt. Außerdem umgeht es `filter` — Stille darf nicht versehentlich filterbar sein.

`{{ message.text }}` ist bei einer manuellen Ausführung fest auf eine leere Zeichenfolge gesetzt und `{{ message.source }}` auf `manual_test`. Eine manuelle Ausführung dient nicht dazu, Testeingaben in den Prompt einzuschleusen.

`filter` vergleicht punktgetrennte Pfade als Zeichenfolgen mit genau dem Payload, den der Prompt sieht. Es dient dazu, Schleifen zu unterbrechen. Eine Quelle, die beispielsweise beide Seiten einer Unterhaltung meldet, würde den Agenten andernfalls bei seiner eigenen Antwort erneut ausführen.

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

## Webhook-Signatur

Wird bei `POST /v1/webhooks/projects/{projectId}/{slug}` ausgelöst. Beeuni prüft die Anfrage in dieser Reihenfolge mit einem Vergleich in konstanter Zeit:

1. **HMAC-Signatur** — Header `X-Beeuni-Signature: sha256=<hmac>` (das Präfix `sha256=` ist optional) oder das mit GitHub kompatible `X-Hub-Signature-256`. HMAC-SHA256 über den rohen Anfrage-Body unter Verwendung des durch `secret_env` bezeichneten Secrets.
2. **Statisches Token**, nur wenn kein Signatur-Header vorhanden ist, für Absender, die einen Body nicht per HMAC signieren können. Sende das Secret als `X-Beeuni-Token: <secret>`, `Authorization: Bearer <secret>` oder `Authorization: Basic <base64(user:secret)>` (der Passwortanteil ist das Token).

`beeuni triggers info <slug>` gibt dieses Verfahren aus — den Header, den Algorithmus und eine Beispielanfrage, die mit einem Platzhalter `$SECRET` signiert ist — und `--json` überträgt es als `webhook_signing`. Das Trigger-Blatt im Dashboard zeigt dieselbe Beispielanfrage.

| Status | Bedeutung |
| --- | --- |
| 202 | Signatur oder Token gültig. Body ist `{ status: "fired" \| "queued" \| "deduped", session_id, ... }`. |
| 200 | Gültig, aber übersprungen — das Projekt ist pausiert oder die Zustellung stimmte nicht mit `filter` überein. |
| 400 | Fehlerhafte Projekt-ID oder fehlerhafter Slug in der URL. |
| 401 | Signatur und Token fehlen beide oder sind falsch. |
| 404 | Trigger nicht gefunden, deaktiviert, kein Webhook oder das Projekt ist nicht aktiv. |
| 409 | Das Signier-Secret fehlt, ist inaktiv oder nicht verfügbar oder autorisiert den Consumer `connector` nicht. Die Antwort enthält einen `webhook_secret_*`-Code und eine Fehlerbehebung. |
| 500 | Authentifizierung erfolgreich, aber die Sitzung konnte nicht ausgelöst werden. |

<a id="endpoints"></a>

## Endpunkte

| Methode + Pfad | Erforderlich | Hinweise |
| --- | --- | --- |
| `GET /v1/projects/{projectId}/triggers` | `project.trigger.read` | Listet Trigger, Laufzeitstatus (`last_fired_at`, `last_status`, `last_error`, `last_attempt_at`) und Fehler beim Parsen des Manifests auf. Ein fehlerhafter Eintrag erscheint in `errors[]`; die anderen Trigger werden dadurch nicht beeinträchtigt. |
| `POST /v1/projects/{projectId}/triggers` | `project.trigger.create` | Erstellt einen Trigger. Übernimmt ihn direkt in das Manifest. |
| `PATCH /v1/projects/{projectId}/triggers/{slug}` | `project.trigger.update` | Teilaktualisierung, die mit dem aktuellen Eintrag zusammengeführt wird. |
| `DELETE /v1/projects/{projectId}/triggers/{slug}` | `project.trigger.delete` | Löscht außerdem den Laufzeitstatus des Triggers. |
| `PATCH /v1/projects/{projectId}/triggers/activation` | `project.trigger.update` | Body `{ paused: boolean }`. Siehe [Pausieren und Fortsetzen](#pause-and-resume). |
| `POST /v1/projects/{projectId}/triggers/{slug}/fire` | `project.trigger.fire` | Manuelle Ausführung. Die integrierte Projektrolle `member` besitzt `project.trigger.fire`, sodass ein normales Mitglied einen Trigger ausführen kann. |
| `POST /v1/webhooks/projects/{projectId}/{slug}` | Signatur oder Token | Öffentliche URL, geschützt durch das Webhook-Secret. |

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

## Pausieren und Fortsetzen

Ein Schalter auf Projektebene stoppt alle Trigger des Projekts gleichzeitig, unabhängig vom eigenen Feld `enabled` des jeweiligen Triggers. Während der Pause überspringt der Scheduler das Projekt und eingehende Webhooks geben `200` mit `{ status: "skipped" }` zurück — keine Sitzung wird ausgelöst. Eine manuelle Ausführung funktioniert weiterhin. Verwende dies, wenn dasselbe Repository auf zwei Control Planes läuft (beispielsweise Entwicklung und Produktion), damit cron nicht doppelt ausgeführt wird. CLI: `beeuni triggers pause` und `beeuni triggers resume`. Siehe [CLI](/docs/de/cli) für die vollständige Befehlsgruppe `beeuni triggers`.

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

## Grenzen und Zuverlässigkeit

- Der Scheduler fragt ungefähr jede Sekunde ab (standardmäßig alle 1.000 ms; konfigurierbar über `BEEUNI_TRIGGER_SCHEDULER_INTERVAL_MS`). Eine cron-Ausführung startet nicht exakt zu ihrem Slot: Jeder Trigger erhält einen festen Versatz von bis zu einem Viertel seiner Periode, höchstens jedoch 30 Minuten, sodass viele Trigger derselben Minute nicht gleichzeitig starten. Die Triggerdetails und `GET /triggers` (`next_fire_at`) zeigen den Zeitpunkt, an dem die nächste Ausführung beginnt.
- Eine fehlgeschlagene Ausführung setzt den letzten Status des Triggers auf „fehlgeschlagen“ und enthält den Fehler. Dies gilt für jede Quelle: cron, manuell, Webhook, App-Ereignis und Monitor. Die nächste erfolgreiche Ausführung löscht den Status einer fehlgeschlagenen Ausführung. Eine fehlgeschlagene Ausführung wird gelöscht, wie unter [Fehlgeschlagene Ausführungen](#failed-runs) beschrieben.
- Jedes Projekt erlaubt standardmäßig 3 gleichzeitig bereitgestellte ausgelöste Sitzungen. Eine Ausführung darüber hinaus gibt stattdessen `queued` (`202`) zurück, ohne fehlzuschlagen, und läuft, sobald eine dieser Sitzungen die Bereitstellung abgeschlossen hat. Die Anzahl gleichzeitig laufender Sitzungen ist nicht begrenzt.
- Eine manuelle oder Webhook-Ausführung hat ein Timeout von 45 Sekunden. Das Laden des Manifests hat ein Timeout von 30 Sekunden.
- Eine cron-Ausführung wird anhand des fälligen Zeitplan-Slots identifiziert, sodass eine Ausführung, bei der ein Timeout aufgetreten ist, die aber tatsächlich gestartet wurde, bei einem erneuten Versuch nicht dupliziert wird.
- Eine Webhook-Zustellung wird einmal pro Ereignis ausgeführt. Beeuni liest die Ereignis-ID in dieser Reihenfolge aus `X-Beeuni-Delivery-Id`, `X-GitHub-Delivery`, `X-Gitlab-Event-UUID`, `Linear-Delivery`, `webhook-id` oder `svix-id`; ein erneuter Versuch mit derselben ID antwortet mit `202 { status: "deduped" }` und führt nichts aus. `X-Request-Id` ist keine Ereignis-ID. Ohne Ereignis-ID zählt ein identischer Body mit identischer Signatur innerhalb von 10 Minuten als dasselbe Ereignis; danach wird er erneut ausgeführt. Ein Job, der bei jedem Deployment denselben statischen Body sendet, wird daher bei jedem Deployment ausgeführt. Sende `X-Beeuni-Delivery-Id` mit einem neuen Wert pro Ereignis (die Beispielanfrage verwendet `$(uuidgen)`).
- Eine Zustellung, deren vorheriger Versuch endgültig fehlgeschlagen ist (beispielsweise weil das Konto nicht über genügend Guthaben verfügte) oder deren Sitzung gelöscht wurde, wird bei einer erneuten Zustellung mit derselben ID erneut ausgeführt. Ein Duplikat ändert `last_fired_at` nicht.
