Scheduled rules
Run a pool on a schedule — mint a lease at a subject on a cadence, with no event and no person.
An assignment rule starts a worker when a task enters a stage. A scheduled rule starts one on a clock: on the cadence you set, it mints a lease on a pool at a subject — the whole space, a workflow, or a single task — with no event and no person in the loop. Use it for work that should run on a timer: a nightly sweep of a workflow, a six-hourly check against a task.
Scheduled rules are managed over the Model Context Protocol endpoint and in the web interface. Managing a rule — creating, editing, toggling, or deleting one — needs admin, on either surface. See Authentication for what a role grants.
What a scheduled rule is
A scheduled rule binds three things:
-
A pool — the pool whose worker runs, named by
pool_id. Naming it is what authorises the rule to dispatch it. -
A subject — what the worker answers for. One of:
- the space — name no workflow and no task. This is the default.
- a workflow — name it by
workflow(its slug) orworkflow_id. - a task — name it by
task_key(itsKEY-Nid) ortask_id.
The kind is inferred from the references you give: a task reference means a task subject, a workflow reference a workflow subject, neither means the space itself. A rule you created at a workflow before this release still points at the same workflow; you do not need to recreate it.
-
A schedule — when it fires (below).
An enabled flag turns the rule on and off; it defaults to true.
The schedule
schedule accepts three forms:
- a cron string —
0 */6 * * * - the natural form —
every 6 hours - a bare duration —
6h
A schedule that parses as none of these is refused when you save the rule, with
Schedule is not a recognised cron or interval expression.
The next fire time is computed from the rule's last run (or, for a rule that has never run, from when it was created). A rule that missed its window while the instance was down fires once when the instance returns — not once for every window it missed.
Manage scheduled rules over MCP
scheduled_rules_list
Lists the scheduled rules in a space. Pass space_key. Each row carries its
last_run_at and computed next_due_at, so you can tell a rule that is firing
from one that is stuck:
{
"scheduled_rules": [
{
"id": 7,
"space": "OPS",
"space_id": 31,
"subject_kind": "workflow",
"workflow": "nightly-sweep",
"workflow_id": 52,
"task_key": null,
"task_id": null,
"pool_id": 19,
"schedule": "0 2 * * *",
"enabled": true,
"last_run_at": "2026-09-11T02:00:00Z",
"next_due_at": "2026-09-12T02:00:00Z",
"created_at": "2026-08-01T09:14:00Z",
"updated_at": "2026-09-01T11:02:00Z"
}
]
}scheduled_rules_create
Creates a rule. pool_id and schedule are required. Name the subject with a
task_key/task_id, a workflow/workflow_id, or neither for the space itself. Pass
enabled to start the rule switched off (false); it defaults to true. Scope
the space with space_key or space_id.
The pool and the subject are looked up in the rule's space, so one from another
space is refused as unknown: unknown pool_id, unknown workflow_id,
unknown workflow slug, unknown task_id or unknown task_key. A missing
pool_id is refused with pool_id required.
A rule that exactly repeats one that already exists — the same subject, pool, and
schedule — is refused with Schedule already has an identical rule for this subject and pool. Two rules that name the same subject and pool with different
schedules are allowed, so you can express a compound cadence.
scheduled_rules_update
Changes a rule's subject, pool, schedule, or enabled flag. Pass the rule's id
and at least one field to change; a call that changes nothing is refused with
must supply a subject, pool, schedule and/or enabled to update.
To move the subject, pass a task_key/task_id or a workflow/workflow_id, or
subject_kind: space to point it at the space itself. Omit all three to leave the
subject as it is. An unknown rule id is refused with unknown scheduled_rule id.
scheduled_rules_delete
Deletes a rule by id. It returns the deleted rule. An unknown id is refused with
unknown scheduled_rule id.
What fires without you, and what it does not do
Every 30 seconds, Cadence fires each enabled rule whose next fire time has arrived, minting a lease at the rule's subject and pool. This runs with no person and independently of any stage or assignment rule.
Two conditions bound what that guarantees, and you should know both before you rely on a scheduled rule:
- One worker at a time per subject and pool. A mint is idempotent for a given
subject and pool: if a worker from that pool is still live on that subject when
the rule is due, the tick starts nothing new and leaves the rule's
last_run_atuntouched. The run happens on a later tick once the subject is free. A scheduled rule does not stack workers on top of a worker that is still running. - Minting a lease runs a worker — it does not move work by itself. A scheduled lease at a workflow or space subject runs a worker that answers for that workflow or space; it does not advance a task or transition anything on its own. What a worker does once it runs is up to the worker.
A rule whose subject or pool has been deleted, or whose space is archived, is skipped — it mints nothing and is not an error.
A scheduled rule at a held or blocked task
A scheduled rule with a task as its subject does not wait for the task the way an assignment rule does. When it is due, it creates its lease whatever state the task is in:
- An uncleared dependency or an unpassed
requirecheck does not stop it. The worker starts and runs. - A blocking hold does not stop the rule creating
the lease either, but the worker is refused when it tries to start. Over MCP,
lease_claimrefuses withtask is on hold. Over the HTTP API the refusal is409with the codetask_on_holdand the messageThe task is on hold; no agent runs on a held task.
What you see for a refused worker:
- The task's timeline shows an
assignedentry for the lease, fromsystem. - The lease never reaches
running. If the worker stops without ending the lease, Cadence marks the leaselostonce it has gone 2 minutes without a heartbeat. The task's timeline then shows alease_endedentry with outcomelostand the summaryagent never claimed lease. A lease that endslostthis way places no further hold on the task. See Leases § Lease states. - The rule counts the lease as a run: its
last_run_atmoves on when the lease is created. It creates another lease at its next due time, and that worker is refused in the same way while the hold stands.
If a blocking hold is placed while the rule's lease is still waiting to start,
Cadence cancels that lease. The timeline shows a lease_ended entry with outcome
cancelled and the summary cancelled: task placed on hold.
Cadence never clears the blocking hold on its own, but the hold does not
guarantee every later worker is refused. A refused worker's lease stays usable
until Cadence marks it lost, and during that time the worker can call
tasks_set_hold with kind: "advisory" on its own task. That replaces the
blocking hold with an advisory one, and the worker can then start. Any other
worker whose lease reaches the task can do the same. Whether a worker does this
depends on its instructions. The task's timeline shows the change as a hold_set
entry with kind: "advisory" and the worker's pool in by. See
Web interface § Holds on a task's page.
If the task should be worked, clear
the hold (see Hold a task and clear a hold).
If the task should wait, turn the rule off: call scheduled_rules_update with
the rule's id and enabled: false, or use the toggle on the
Automation tab. Turn it back on when the task is ready.
An assignment rule with target: parent behaves the same way towards the parent
it leases. Cadence checks a hold, dependency or required check against the child
that entered the stage, not the parent. See
Stage rules § Assignment rules.
The Automation tab
The web interface manages the same rules at
/spaces/:key/settings/automation — the Automation section of a space's
settings. It is the same capability reached a second way:
- the list of the space's scheduled rules,
- a dialog to add a rule or edit one, with the same subject, pool, schedule, and enabled inputs,
- an inline toggle on each row to enable or disable a rule without opening the dialog.
Viewing the list of rules on the tab is not admin-only. Creating, editing, toggling, or deleting a rule from it needs admin, the same as the tools.
A rule written in the browser validates and fires exactly like one written over MCP.