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

# Schedule a workflow and set goals

> Put a deployed workflow on a cadence, give it a goal a judge checks after each run, and know what the agent may do with workflows from chat

After this guide, a [workflow](/concepts/workflows) runs on a cron cadence with a fixed input, a goal runs it repeatedly until a judge says the objective is met, and you know what the agent may do with workflows from chat. A schedule is the workflow's own cadence job and every fire is a recorded run; for a background function with no run record, use a [job](/concepts/jobs).

*Verified against lua-cli 3.33.0.*

**Before you begin**

* A workflow pushed and deployed (`lua workflows deploy <name> -v latest`), with `concurrencyPolicy: 'forbid'` if two fires must never overlap.
* A user session (`lua auth configure`).

<Steps>
  <Step title="Create a schedule from the CLI">
    `schedules create` attaches a cadence to the workflow, or replaces the one it has. Each fire starts a run with the `--input` you give.

    ```bash theme={null}
    lua workflows schedules create lead-outreach --cadence '0 9 * * 1-5' --timezone Europe/London \
      --input '{"leadId":"lead_abc123"}' --tag nightly --budget-credits 10 --ci
    ```

    The reply is `✅ schedule <jobId> on "lead-outreach" · cron 0 9 * * 1-5 (Europe/London) · next <time>`. `--cadence` repeats up to 5 times; `--every 15m` is the interval form (whole minutes). `--notify emailApp|email|app|off` chooses where each fire notifies, `-v` pins the version each fire runs, and `--backfill-on-enable <n>` replays up to `n` missed fires on re-enable. `schedules pause <jobId>`, `resume <jobId> [--backfill-now]`, `patch <jobId> --paused true|false`, and `delete <jobId> --yes` manage it.
  </Step>

  <Step title="Declare the schedule in code">
    A schedule in the definition deploys with the workflow and is activated with `lua workflows activate <name>`.

    ```ts src/workflows/weekly-digest.ts theme={null}
    import { z } from 'zod';
    import { createStep, createWorkflow, Data } from 'lua-cli';

    const countOpen = createStep({
      id: 'countOpen',
      inputSchema: z.object({ team: z.string() }),
      outputSchema: z.object({ open: z.number() }),
      async execute({ inputData }) {
        const page = await Data.get('tickets', { team: inputData.team, status: 'open' }, 1, 1);
        return { open: page.pagination.totalCount };
      },
    });

    export const weeklyDigest = createWorkflow({
      name: 'weekly-digest',
      description: 'Count the team\'s open tickets every Monday morning.',
      inputSchema: z.object({ team: z.string() }),
      outputSchema: z.object({ open: z.number() }),
      schedule: { type: 'cron', expression: '0 9 * * 1', timezone: 'Europe/London' },
      scheduleInput: { team: 'support' },
      concurrencyPolicy: 'forbid',
      backfillOnEnable: { maxOccurrences: 1 },
    })
      .then(countOpen)
      .commit();
    ```

    `schedule` takes the same shape as a [`LuaJob`](/reference/sdk/luajob) schedule; `scheduleInput` must satisfy `inputSchema`, or `lua compile` warns `schedule-input-required` or `schedule-input-invalid`. `deactivate` pauses the schedule without touching the live version.
  </Step>

  <Step title="Create a goal">
    A goal runs the workflow on a cadence until a judge says the objective is met, `--max-runs` is used up, or you close it.

    ```bash theme={null}
    lua workflows goals create lead-outreach --objective "Every lead created this month has received an intro email" \
      --judge-predicate 'output.sent truthy' --cadence '0 9 * * 1-5' --timezone Europe/London \
      --max-runs 10 --input '{"leadId":"lead_abc123"}' --ci
    ```

    The reply is `✅ goal <goalId> created on "lead-outreach" · active · runs 0/10 · cron 0 9 * * 1-5 (Europe/London) · schedule <jobId>`. A predicate judge is deterministic: `'<path> <op> [value]'`, with `path` under `output.`, `state.`, or `steps.<stepId>.`, or one of `iteration` and `runStatus`, and `op` one of `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `exists`, `truthy`. An agent judge is `--judge-agent <agentId|'$self'>` with `--schema @schema.json`, a JSON Schema whose root declares a boolean `done`; a `'$self'` judge also needs `--judge-role @role.json` (`{ name, instructions, tools }`). `--max-runs` is 1 to 100, `--max-total-credits` caps credits across every run of the goal, and without `--cadence` the judge runs right after each run. From code, `Workflows.setGoal` takes the same fields; the [`Workflows`](/reference/sdk/workflows) reference says what the deployed runtime accepts.
  </Step>

  <Step title="Steer a goal">
    Read and change a goal by id; never delete its schedule.

    ```bash theme={null}
    lua workflows goals get <goalId>
    lua workflows goals pause <goalId>
    lua workflows goals raise <goalId> --max-runs 20
    lua workflows goals close <goalId> --note "Objective met by hand"
    ```

    A goal is `active`, `paused`, `done`, or `closed`; a paused goal carries a `pauseReason` of `user`, `budget`, `max_runs`, or `strikes` (three consecutive failed iterations). `pause` skips fires, `resume` does not replay missed ones, `raise` lifts `--max-runs` or `--max-credits` and re-arms a goal parked at its cap, and `close` is final. A goal's cadence is a schedule tagged with the goal id; `schedules pause`, `resume`, `patch`, and `delete` refuse it with `goal_schedule`, because the schedule follows the goal.
  </Step>

  <Step title="Verify">
    `view` lists a workflow's schedules and goals beside its versions.

    ```bash theme={null}
    lua workflows view lead-outreach
    ```

    ```text Output theme={null}
    🧭 lead-outreach — Draft outreach emails for a batch of leads, get human approval on the batch, then send the approved drafts.
       Id:      a95a5d5d-2529-460c-aadb-ec5fb6926000
       Status:  active
       Active:  1.0.3
       Outputs: run readers (no ACL)
       Schedules: none
       Goals:     none
    …
    ```

    `schedules list -i lead-outreach` and `goals list lead-outreach` print the full tables, and every fire appears in `lua workflows runs --workflow lead-outreach` with `Trigger` `schedule`.
  </Step>
</Steps>

## Options you may need

### What the agent can do from chat

An agent that has workflows turned on gets tools for them in conversation: it starts, inspects, resumes, signals, repairs, and cancels runs (`startWorkflowRun`, `getWorkflowRun`, and their siblings; `cancelWorkflowRun` unless the organization turns it off), edits a workflow's one schedule in place (`scheduleWorkflow`, `unscheduleWorkflow`), runs goals (`setWorkflowGoal` and the goal lifecycle verbs), and, unless composition is turned off, composes new workflows with `composeWorkflow`. A request that needs steps to run unattended, wait for a person, or recur becomes a composed workflow the agent starts only after presenting the plan and estimate, always with an idempotency key.

### Consent and caps

A composed workflow over the organization's thresholds (by default 15 steps, 20 credits, or 3,600 seconds) asks the end user for consent first, or is refused with `consent_refused` where the organization forbids asking. A goal created from chat defaults to 3 runs. A workflow the agent composed is `dynamic`: `lua workflows list --all` shows it, `deploy` refuses it with `WORKFLOW_DYNAMIC`, and `export <name>` brings it into source.

| Cap                                  | Value    |
| ------------------------------------ | -------- |
| Cadence entries per schedule or goal | 5        |
| `maxRuns` per goal                   | 1 to 100 |
| Active goals per organization        | 20       |
| Tags per run                         | 10       |

## If it isn't working

<Accordion title="goal_schedule">
  The job you tried to change is a goal's cadence. Use `lua workflows goals pause <goalId>` or `goals close <goalId>`; a done or closed goal's leftover schedule can then be deleted.
</Accordion>

<Accordion title="GOAL_NOT_ACTIVE">
  Only an active goal pauses and only a paused one resumes; a done or closed goal stays closed. A goal paused at its cap re-arms with `goals raise`.
</Accordion>

<Accordion title="SCHEDULE_CAP">
  A schedule takes at most 5 cadence entries. Pass fewer `--cadence` flags or combine them into one cron expression.
</Accordion>

## Next steps

<Columns cols={2}>
  <Card title="lua workflows reference" href="/reference/cli/workflows">Every `schedules` and `goals` flag and refusal.</Card>
  <Card title="Operate runs" href="/build/workflows/operate-runs">Follow, answer, and cancel the runs a schedule or goal starts.</Card>
  <Card title="Workflows runtime API" href="/reference/sdk/workflows">`setGoal` and `goals.*` from code, and what the deployed runtime accepts.</Card>
</Columns>
