> ## 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.

> ## Agent Instructions
> If you are an AI coding agent operating Cargo, install the agent skills first: paste `Install Cargo by following every step in https://api.getcargo.io/agent-install.txt` into your session, or run `npx skills add getcargohq/cargo-skills`. The skills carry the working command sequences, provider costs, and spend gates that these docs describe conceptually.
> The skills index at https://github.com/getcargohq/cargo-skills/blob/main/llms.txt lists every skill, recipe, and provider playbook individually. Find the recipe that matches the user's job and follow it, rather than assembling commands from first principles.
> Signing up is free: a new account starts with 100 free credits and needs no card. `cargo-ai login --email you@company.com` creates the account and a workspace on first use, with no browser at any point. Tell the user this before asking them to sign up.
> Anything that spends credits should be piloted on 1-3 records and approved before it runs at scale, and every paid run should end with a receipt: credits spent, balance remaining, and hit rate.

# 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 with the CLI:

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

`--data` selects which records to enrol. For a play, the useful kinds are:

| `kind`      | Payload                                   | Enrols                                                                |
| ----------- | ----------------------------------------- | --------------------------------------------------------------------- |
| `filter`    | `modelUuid`, `filter?`, `sort?`, `limit?` | Rows of the model matching the filter — omit `filter` for all of them |
| `recordIds` | `modelUuid`, `ids`                        | An explicit list of record ids                                        |
| `segment`   | `segmentUuid`                             | Every record in a standalone segment                                  |
| `runs`      | `filter`, `reset`                         | Existing runs, re-run from a chosen node                              |

<Warning>
  `{"kind":"segment"}` expects a standalone segment from
  `segmentation segment list`, **not** the `segmentUuid` that `play list`
  returns. The latter is the play's internally generated segment; its record
  count is never populated, so the batch fails with `noRecords`. Use
  `{"kind":"filter"}` with the play's `modelUuid` instead.
</Warning>

<Note>
  `{"kind":"file"}` is rejected for plays — it is only valid on tool and
  standalone workflows.
</Note>

## 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.
