---
title: "Triggers (TriggerDecl)"
description: "Generated from the doc schema (CatalogDocSchema, @canonify/kernel-catalog/doc): each field’s type, default and description are the schema’s own, and each example is a bundle file that readBundle…"
order: 14
id: "model/trigger"
---

# Triggers (TriggerDecl)

Generated from the doc schema (`CatalogDocSchema`, @canonify/kernel-catalog/doc): each field’s type, default and description are the schema’s own, and each example is a bundle file that readBundle decodes. A field’s Example is its value in one of those examples.

## TriggerDecl {#triggerdecl}

A trigger: an accountable automatic action (runtime).

The `triggers` slot of the doc holds these, keyed by id. In a bundle each is one file, `triggers/<name>.json`, named by its `name`, which the file then leaves out.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `id` | [TriggerId](shapes.md#triggerid) | yes (propose assigns it; a new declaration leaves it out) | — | — | A trigger id (`tr_…`); renames keep it. |
| `name` | [Ident](shapes.md#ident) | yes | — | `"daily_reminder"` | The trigger name. |
| `declarer` | [DeclarerRef](policy.md#declarerref) | no | — | `{"account":"u-01HZY8Q2:person"}` | Who declared it, recorded by the catalog service at promote; an authored value is replaced (C35). |
| `on` | [TriggerDecl.on](trigger.md#triggerdecl.on) | yes | — | `{"cadence":"daily at 07:00"}` | What fires the trigger: a cadence, a change, entering or leaving a state, or a refused call. |
| `select` | [TriggerDecl.select](trigger.md#triggerdecl.select) | no | — | (see the example) | A cadence trigger runs the action once per selected row. |
| `run` | [TriggerDecl.run](trigger.md#triggerdecl.run) | yes | — | `{"action":"ac_7t9w1y3a5c","input":{"order":"this"}}` | The call it makes. |
| `once` | `"per-row"` \| `"per-row-per-window"` | no | `"per-row-per-window"` | `"per-row"` | A cadence trigger with select fires once per row, or once per row in each cadence window (default). |

`triggers/daily_reminder.json`

```json
{
  "on": { "cadence": "daily at 07:00" },
  "select": {
    "type": "ty_3f8v6h2n0c",
    "where": "status == 'draft'",
    "limit": 100
  },
  "run": { "action": "ac_7t9w1y3a5c", "input": { "order": "this" } },
  "once": "per-row"
}
```

`triggers/on_total_change.json`

```json
{
  "on": {
    "change": "ty_3f8v6h2n0c",
    "ops": ["update"],
    "props": ["pr_8b2f4h6j0k"],
    "when": "total > old.total"
  },
  "run": { "action": "ac_7t9w1y3a5c", "input": { "order": "this" } }
}
```

`triggers/on_placed.json`

```json
{
  "on": { "enter": { "lifecycle": "lc_2k4m6p8r0t", "state": "st_5m7p9r1t3v" } },
  "run": { "action": "ac_7t9w1y3a5c", "input": { "order": "this" } }
}
```

`triggers/on_left_draft.json`

```json
{
  "on": { "exit": { "lifecycle": "lc_2k4m6p8r0t", "state": "st_1a3c5e7g9j" } },
  "run": { "action": "ac_7t9w1y3a5c", "input": { "order": "this" } }
}
```

`triggers/on_refused.json`

```json
{
  "on": { "fail": "ac_3h5k7n9q1s", "preconditions": ["has_lines"] },
  "run": { "action": "ac_7t9w1y3a5c", "input": { "order": "this" } }
}
```

## TriggerDecl.on {#triggerdecl.on}

What fires the trigger: a cadence, a change, entering or leaving a state, or a refused call.

One of:

- [TriggerDecl.on.cadence](trigger.md#triggerdecl.on.cadence)
- [TriggerDecl.on.change](trigger.md#triggerdecl.on.change)
- [TriggerDecl.on.enter](trigger.md#triggerdecl.on.enter)
- [TriggerDecl.on.exit](trigger.md#triggerdecl.on.exit)
- [TriggerDecl.on.fail](trigger.md#triggerdecl.on.fail)

## TriggerDecl.on.cadence {#triggerdecl.on.cadence}

On a cadence.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `cadence` | string | yes | — | `"daily at 07:00"` | When it fires, e.g. daily at 07:00, weekly on mon at 09:00, 15m or a 5-field cron; parsed once at compile. |

## TriggerDecl.on.change {#triggerdecl.on.change}

On a change to a row.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `change` | [TypeId](shapes.md#typeid) | yes | — | `"ty_3f8v6h2n0c"` | The type whose rows it watches. |
| `ops` | (`"create"` \| `"update"` \| `"delete"`)[] | no | — | `["update"]` | The writes that fire it; absent = all three. |
| `props` | [PropId](shapes.md#propid)[] | no | — | `["pr_8b2f4h6j0k"]` | An update fires it only when one of these properties changed. |
| `when` | [Expr](shapes.md#expr) | no | — | `"total > old.total"` | It fires only when this CX condition holds on the changed row. |

## TriggerDecl.on.enter {#triggerdecl.on.enter}

When a row enters a state.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `enter` | [StateRef](shapes.md#stateref) | yes | — | `{"lifecycle":"lc_2k4m6p8r0t","state":"st_5m7p9r1t3v"}` | The state a row enters. |

## TriggerDecl.on.exit {#triggerdecl.on.exit}

When a row leaves a state.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `exit` | [StateRef](shapes.md#stateref) | yes | — | `{"lifecycle":"lc_2k4m6p8r0t","state":"st_1a3c5e7g9j"}` | The state a row leaves. |

## TriggerDecl.on.fail {#triggerdecl.on.fail}

When a call of an action is refused.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `fail` | [ActionId](shapes.md#actionid) | yes | — | `"ac_3h5k7n9q1s"` | The action whose refused call fires it. |
| `preconditions` | [Ident](shapes.md#ident)[] | no | — | `["has_lines"]` | Only a refusal by one of these preconditions fires it; absent = any. |

## TriggerDecl.select {#triggerdecl.select}

A cadence trigger runs the action once per selected row.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `type` | [TypeId](shapes.md#typeid) | yes | — | `"ty_3f8v6h2n0c"` | The type whose rows it selects. |
| `where` | [Expr](shapes.md#expr) | yes | — | `"status == 'draft'"` | The CX condition a row must hold. |
| `limit` | integer (between 1 and 1000) | yes | — | `100` | The most rows one firing selects: 1–1,000. |

## TriggerDecl.run {#triggerdecl.run}

The call it makes.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `action` | [ActionId](shapes.md#actionid) | yes | — | `"ac_7t9w1y3a5c"` | The action it calls. |
| `input` | Record<[Ident](shapes.md#ident), [Expr](shapes.md#expr)> | yes | — | `{"order":"this"}` | The CX value of each input of the call. |
