# Triggers

> Triggers are reactions to events on an activity. Each trigger has an event (when it fires), optional conditions (whether it fires), and one or more actions (what it does).

Canonical HTML page: https://conclude.io/doc/reference/triggers/
Last updated: 2026-11-02

---

Triggers are reactions to events on an activity. Each trigger has an
**event** (when it fires), optional **conditions** (whether it fires), and
one or more **actions** (what it does).

## Skeleton

```yaml
triggers:
  - onCreate:
      - alert: ${__channel} ${initiator}
  - onComment:
      - agent: triage
      - script: extract-jira-ids
  - onEdit:
      - attribute: severity
        ifMatch: critical
        alert: "#oncall"
  - onConclude:
      - email:
          to: ${initiator}
          subject: "Resolved: ${title}"
          body: ${conclusion}
```

`triggers:` is a list of trigger groups, each containing one or more
trigger lists keyed by event. (Most apps only need one group.)

## Events

| Event | When it fires |
|---|---|
| `onCreate` | A new activity is created |
| `onComment` | A comment or a file is posted |
| `onEdit` | An attribute, or the severity, changes |
| `onConclude` | The activity is closed |

`onCreate` and `onConclude` fire once per activity. `onComment` and `onEdit`
fire repeatedly. Triggers also fire from clicked buttons,
but those are attached to the attribute, not to
an event list — see [Attributes](https://conclude.io/doc/reference/attributes/#button-attributes).

## Conditions

| YAML key | Purpose |
|---|---|
| `attribute` | Restrict an `onEdit` trigger to changes on this attribute (without it, the trigger fires on every change) |
| `ifMatch` | Regular expression that the attribute (or comment) value must match |
| `ifNotMatch` | Regular expression that must *not* match |

```yaml
- attribute: status
  ifMatch: "^(open|in_progress)$"
  alert: "#team"
```

For an `onComment` trigger, `ifMatch` is checked against the comment text:

```yaml
onComment:
  - ifMatch: "^(?i)help\\s*$"
    agent: helper
```

## Actions

A trigger can carry one or more of these actions, all inlined into the same
trigger:

| YAML key | Action |
|---|---|
| `owner` | Set the activity owner ([role expression](https://conclude.io/doc/reference/macros-and-roles/#role-expressions)) |
| `members` | Add channels/users as activity members |
| `alert` | Post an alert (channel reference) |
| `conclude: true` | Close the activity |
| `setAttribute: {name, value}` | Set an attribute to a (macro-aware) value |
| `message: {to, body}` | Post a chat message |
| `email: {to, subject, body, ...}` | Send an email |
| `script: <name>` | Run a Starlark script by name (must exist in top-level `scripts:`) |
| `agent: <name>` | Engage an AI agent by name |

```yaml
- alert: "#oncall"
  owner: ${initiator}
  setAttribute:
    name: priority
    value: high
  message:
    to: "#announcements"
    body: "${title} acknowledged by ${owner}"
```

### `setAttribute`

```yaml
setAttribute:
  name: status
  value: in_progress      # supports macros
```

### `message` (chat)

```text
message:
  to: "#channel" | "${owner}" | "<user-key>"
  body: "Heads up: ${title}"
```

### `email`

```yaml
email:
  to: "${initiator}"
  cc: "ops@example.com"
  bcc: ""
  from: ""              # optional override
  replyTo: ""           # optional
  subject: "Resolved: ${title}"
  body: ${conclusion}
  log: true             # optional, log the email send
  logBody: true         # optional, log the body too
```

### `script`

```yaml
script: route-by-category
```

`route-by-category` must match a `name:` under top-level `scripts:`. See
[Scripting (Starlark)](https://conclude.io/doc/reference/scripting/).

### `agent`

```yaml
agent: triage
```

`triage` must match a `name:` under top-level `agents:`, or an org-level
agent. It answers in the mode it has in the app's channel, which the app does not set.
See [Agents in YAML](https://conclude.io/doc/reference/agents/#the-mode).

## Per-attribute triggers

Attributes can carry a single `trigger:` that fires when *that* attribute
changes. Equivalent to an `onEdit` trigger with `attribute:` matching, but
co-located with the attribute definition.

```yaml
- name: status
  type: select
  trigger:
    setAttribute:
      name: conclusion
      value: "Status: ${status.label}"
```

Each `Option` can also carry a trigger that fires when *that option* is
selected:

```yaml
- name: status
  type: select
  options:
    - name: resolved
      label: Resolved
      state: closed
      trigger:
        conclude: true
```

## Loop protection

Scripts and agents only fire on **user-originated** events. A trigger that
runs a script and the script posts a comment will *not* re-trigger itself —
the post carries a marker that blocks `onComment` from
dispatching scripts/agents on that comment.

Declarative cascades (`setAttribute`, `message`, `email`, `alert`) still
chain, up to 8 levels deep. After
that, the cascade stops.

**Implication:** if you want a script that re-fires on itself
(e.g. a periodic animation), use `conclude.schedule()` — see
[Scripting (Starlark)](https://conclude.io/doc/reference/scripting/#scheduled-re-entry).

## Trigger dispatch order

When an event fires triggers:

1. Script triggers dispatch sequentially via the notification queue.
2. Agent triggers dispatch concurrently (each runs on its own).
3. `conclude` and `setAttribute` actions are collected from all triggers,
   then applied to the activity as a single change.
4. `message` / `email` actions fire asynchronously.
5. `alert` channels are added to the notification's alert list.

Steps 1 and 2 skip events that a trigger caused.

Multiple triggers on the same event run in YAML order.

## Trigger resolution by event

| Event | Triggers selected |
|---|---|
| Activity created | `onCreate` triggers + any attribute trigger on attributes set during creation |
| Comment or file posted | `onComment` triggers |
| Attribute or severity changed | `onEdit` triggers that match the changed attribute(s), and those without an attribute; plus any attribute-level or option-level trigger on that attribute |
| Activity closed | `onConclude` triggers |
| Button clicked | Trigger attached to the clicked attribute |

