Skip to main content
A schedule starts a workflow run without anyone asking for it: once, at a moment you set, or again and again on a cron cadence. Each fire creates an ordinary execution in a session you name, the same kind of run you start from the debugger or the API, and records whether starting it succeeded. Schedules are kept in a scheduler blob, which holds a set of them in its one revision. That revision is managed: writable for as long as the blob exists, never committed and never branched, so you edit the set in place and no earlier version of it is kept. The scheduler blob type reference has every field.

One-time and cron

Every schedule fires on one of two cadences, read in the IANA timezone it must name, such as America/Denver or UTC:
  • one_time fires once, at the moment you set.
  • recurring_cron fires on a six-field cron expression, such as 0 9 ? * MON-FRI * for 09:00 on weekdays.
An optional start date and end date bound either one. A schedule is active while it can still fire and completed once it can fire no more: a one-time schedule after its fire, a recurring one once its end date has passed. A completed schedule can be read and deleted, not changed. Short of deleting it, a schedule can also be disabled, which stops its fires until it is enabled again. A schedule may carry an alias, unique among its scheduler’s schedules, that works anywhere its id does, and a description of up to 256 characters. The description belongs to the schedule, and every fire hands it to the execution it starts as that run’s own description. In the web app, a scheduler’s page lists its schedules with their cadence, timezone, state, target and workflow, and New Schedule opens the form that creates one; over the API it is Create Schedule. Two limits bound what a scheduler holds and how often it fires: scheduler.schedules_per_revision caps how many schedules it holds, and scheduler.min_interval_seconds sets the shortest gap allowed between two fires of a cron expression. Limits has their values.

What a schedule runs, and where

A schedule names where it runs apart from what it runs, with the run’s input beside them:
  • target is where: a workflow blob, named by id or by alias with its organization, a revision of it, and a session in that revision.
  • workflow is what: a workflow reference, the same object a request to start a run takes. It names a definition in the target’s own revision, or one in any other workflow blob the schedule’s creator can read.
  • input_data is what the run receives.
How much of the target is fixed in advance is your choice. A pinned target names a revision by id, and its session is resolved when the schedule is saved and kept as an id, so renaming the session afterwards changes nothing. A following target names default in place of a revision and names its session by alias, and every fire resolves the blob’s default revision and that session afresh, so the schedule moves forward with the workflow instead of running an old revision nobody edits any more. A draft is the only revision that runs, so a following target fails while the default is committed: each fire in that time is recorded with that reason, and fires start runs again only once the default is a draft with an open session of that alias. The workflow is resolved again at every fire as well, so a definition named by alias runs whatever holds that alias at that moment. Saving a target, when a schedule is created or its target is changed, checks what a fire will check, with the saver’s own standing: write access to the target blob, a revision that belongs to it and still accepts writes, a session in that revision, and the workflow reference. Every failure gets the same refusal, so a refusal never says whether something exists. A typo fails when you save rather than at the first fire. A fire creates an ordinary execution in the named session, with the standing of whoever created the schedule and carrying its description. From there it is a run like any other: it runs from its own copy of the definition, writes into its session, and shows in the debugger. If that standing no longer reaches the target, the fire is recorded as failed and no run starts.

History

Each fire leaves a record in the schedule’s history, apart from the execution it starts: which schedule fired, when, the target as it was invoked, the description it sent, a status and a result. The status covers only the attempt to start the run: whether the execution was created, refused by a limit, or failed, and for what reason. How the run itself ended is on the execution, in the debugger. A fire whose target no longer resolves is recorded that way and never falls back to anything else, and a fire a limit refused does not stop the schedule: a recurring one keeps firing. A record is kept for 30 days by default, under scheduler.run_retention_days, counted from its fire; a daily sweep removes older ones, and Retention has the rules. A schedule’s page in the web app shows its target field by field and lists its fire records under Executions, the API’s name for them, and List Executions pages through them.

Schedules as code

The CLI’s scheduler group keeps a scheduler’s schedules in a manifest file in your repository. Each entry is a schedule in the API’s own fields, the ones the web form writes, keyed by its alias and naming blobs, sessions and definitions by alias or by id. deploy checks every target, then creates the schedules that are missing and updates the ones that differ, creating a target session that does not exist yet. diff reports what a deploy would change and writes nothing, pull writes the deployed schedules back into the file, and ls lists them. A schedule the manifest does not list is reported as drift and left alone, which means it keeps firing until it is deleted. Scheduler overview walks through the group, and Scheduler manifest has the file’s format.