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

# Triggering a play

> Declare a play's schedule, change detection, enrolment rules, and filters in code with definePlay

A play watches its [model](/models/overview) and emits a run per changed row. When and how it fires is declared on `definePlay` — schedule, which changes count, how records enrol, and which rows are eligible. All of it deploys with `cargo-ai cdk deploy`.

## Configure triggers in code

```ts plays/onboarding.ts theme={null}
export const onboarding = definePlay("onboarding", {
  model: contacts,
  workflow: onboardRow,
  schedule: { type: "realtime" },        // fire as rows change
  changeKinds: ["added", "updated"],     // which changes create a run
  runCreationRule: "once",               // enrol each record only once
  filter: {
    conjonction: "and",
    groups: [{
      conjonction: "and",
      conditions: [
        { kind: "string", columnSlug: "country", operator: "is", values: ["US"] },
      ],
    }],
  },
});
```

## Schedule

The `schedule` field selects how the play is driven:

| `schedule`                               | Behavior                                  |
| ---------------------------------------- | ----------------------------------------- |
| `{ type: "realtime" }`                   | Fire instantly as the model's rows change |
| `{ type: "cron", cron: "0 * * * *" }`    | Run on a cron expression (UTC)            |
| `{ type: "dependency", play: upstream }` | Run after another play finishes           |
| `{ type: "watch" }`                      | Run on a watched external schedule        |
| `{ type: "dbt", jobId: "…" }`            | Run after a dbt job completes             |

<Info>
  Real-time schedules are currently only available for **HubSpot** and
  **Salesforce**. The minimum interval for scheduled syncs depends on the model
  type (as short as 5 minutes, up to 1 day); a sync is skipped if the model was
  already refreshed within that window.
</Info>

## Change detection

`changeKinds` controls which row changes create a run. Defaults to `["added"]`.

* **Real-time** schedules know exactly what happened — a row was `added` (created in the source) or `updated`.
* **Scheduled** syncs (cron, dependency, watch) compare snapshots, so `added` means "appears in this sync but not the last" — which includes newly created rows, rows that now match the `filter` for the first time, and restored rows.

## Enrolment rules

`runCreationRule` controls what happens when a record triggers repeatedly:

| Value             | UI equivalent                | Behavior                                             |
| ----------------- | ---------------------------- | ---------------------------------------------------- |
| `"always"`        | Always enrol                 | New run every time the trigger fires for that record |
| `"once"`          | Enrol once                   | Each record is enrolled at most once, ever           |
| `"noConcurrency"` | If is not currently enrolled | Skip if the record is already in an active run       |

<Warning>
  **Feedback loops with `"always"` + real-time on `updated`:** if the play
  writes back to the source system (e.g. updating a HubSpot field), that write
  is itself an update event that re-triggers the play on the same record — a
  loop that can multiply executions by orders of magnitude. Prevent it with
  `runCreationRule: "once"` (or `"noConcurrency"`), by restricting
  `changeKinds` to `["added"]`, or by adding a `filter` that excludes
  already-processed rows (e.g. "score is unknown").
</Warning>

## Eligibility filter

`filter` (and optional `sort`, `limit`) restricts which rows are eligible. It uses the same JSON schema as the UI segment builder and the `segment download` CLI command.

<Warning>
  For real-time schedules, SQL, segment, occurrence, and related-model
  conditions are not supported, and neither is OR within groups / AND across
  multiple groups.
</Warning>

## Trigger a batch manually

Beyond the schedule, run a play on demand across a segment with the CLI:

```bash theme={null}
cargo-ai orchestration play list                 # → workflowUuid, segmentUuid
cargo-ai orchestration batch create \
  --workflow-uuid <uuid> \
  --data '{"kind":"segment","segmentUuid":"<uuid>"}' \
  --wait-until-finished
```

## Health monitoring

Set a **batch health threshold** (percent of runs that must succeed) and a Slack **health alert** for when a batch drops below it. See [Monitoring](/tools/monitoring) for querying runs and metrics from the CLI.
