Skip to main content

Scheduling Workflows

A schedule runs a deployed workflow on a cron or a fixed interval: a nightly report, an hourly cleanup job, a health ping every five minutes. Schedules run on the Temporal runtime and keep firing whether or not anyone is logged in.

What a schedule is​

PartMeaning
Id<namespace>/<name>. name is yours to choose and unique within the namespace, so one workflow can have several schedules. Letters, digits, ., _, - and @
WorkflowThe name of a deployed workflow. Without a version, every fire runs whatever version is deployed at that moment, so a redeploy takes effect at the next fire. Add @<version> to pin one
TimingExactly one of cron (one or more cron expressions) or every (a fixed interval)
InputThe input passed to every run
OwnerThe user who created or last updated the schedule. Every run executes as that user

Optional settings:

SettingDefaultMeaning
timezoneUTCIANA timezone the cron expressions are read in, e.g. America/New_York
jitternoneDelays each fire by a random amount up to this, to spread load
overlapskipWhat to do when a fire is due while the previous run is still going: skip, buffer_one, buffer_all, cancel_other, terminate_other, allow_all
catchup5 minutesAfter an outage, missed fires up to this old are still run; older ones are dropped
pausedfalseCreate the schedule paused
noteFree text, shown with the schedule (why it is paused, who to ask)

Only deployed workflows can be scheduled. A local file cannot, because a schedule outlives your session. Publish the package first.

With the CLI​

# Every night at 02:00 New York time
moco schedule create ops/nightly-report nightly-report \
--cron "0 2 * * *" --tz America/New_York --input '{"days": 1}'

# Every 5 minutes, pinned to version 1.2.0, created paused
moco schedule create ops/ping health-ping@1.2.0 --every 5m --paused --note "enable after go-live"

moco schedule list ops # schedules in a namespace
moco schedule describe ops/nightly-report # spec, next fires, recent runs
moco schedule trigger ops/nightly-report # run once now (also while paused)
moco schedule pause ops/nightly-report --note "incident 42"
moco schedule resume ops/nightly-report
moco schedule update ops/nightly-report nightly-report --cron "0 3 * * *"
moco schedule delete ops/nightly-report # asks to confirm; --force skips

Durations (--every, --jitter, --catchup) are seconds (90) or units: 30s, 5m, 1h30m, 2d. Input comes from --input '<json>' or --input-file <file> (JSON or YAML) and must be an object. Add --json to list or describe for machine-readable output.

describe lists the workflow ids of recent runs. Pass one to moco status or moco history to inspect that run. A scheduled run's id has the form <workflow>:<version>:<owner>:schedule-<schedule id>-<fire time>.

update replaces the whole schedule: workflow, timing, input and options. Pass everything you want to keep. A paused schedule stays paused through an update unless you pass --paused (or paused: false through the API).

Declaring schedules in the workflow​

The author of a workflow usually knows when it should run. Write that down in the workflow's header, and anyone who can run the deployed workflow can create the schedule by name, without repeating the timing:

header:
schema_version: 2.0.0
workflow_name: nightly-report
metadata:
schedules:
nightly:
description: Full report, every night at 02:00 New York time
cron: ["0 2 * * *"]
timezone: America/New_York
input: {days: 1}
hourly:
every_sec: 3600
overlap: skip

Each key under schedules (lowercase letters, digits, _, -) is one declaration. It takes exactly one of cron (a list) or every_sec, the optional settings above as timezone, jitter_sec, overlap, catchup_window_sec and paused, an input for every run, and a description. moco validate checks the block; a declaration creates nothing on its own.

After you publish the package:

moco schedule declared nightly-report # what the deployed version declares
moco schedule create nightly-report --declared nightly
# -> schedule <namespace>/nightly-report.nightly, as declared
moco schedule create ops/eu-nightly nightly-report --declared nightly \
--tz Europe/Berlin --input '{"region": "eu"}'
# -> your own id; each flag overrides one field of the declaration

Without an id, the schedule is <the workflow's namespace>/<workflow>.<key>. Flags override single fields: --cron or --every replaces the declared timing, --input keys are merged over the declared input. In a package with several workflows, name the one you mean as <package>/<workflow>; moco schedule declared <package> lists every workflow's declarations. In the console, the New schedule dialog offers Start from a declared schedule once you enter a workflow that declares any; it fills in the form and every field stays editable.

The schedule keeps the timing it was created with. Publishing a new version with a different declaration does not change existing schedules; run moco schedule update <id> <workflow> --declared <key> to pick the new one up. moco schedule describe shows which declaration a schedule came from.

Through the API​

The same operations are available as REST endpoints under /api/schedule and as MCP tools.

OperationRESTMCP tool
CreatePOST /api/schedule/createcreate_schedule
Declared schedulesGET /api/schedule/declared?wfspec_name=&version=list_declared_schedules
ReplacePOST /api/schedule/updateupdate_schedule
DescribeGET /api/schedule/describe?namespace=&name=describe_schedule
ListGET /api/schedule/list?namespace=list_schedules
Pause / resumePOST /api/schedule/pause, /unpausepause_schedule, unpause_schedule
Run nowPOST /api/schedule/triggertrigger_schedule
DeletePOST /api/schedule/deletedelete_schedule

A create request:

{
"namespace": "ops",
"name": "nightly-report",
"wfspec_info": {"name": "nightly-report"},
"spec": {"cron": ["0 2 * * *"], "timezone": "America/New_York"},
"input_data": {"days": 1}
}

spec takes cron (a list) or every_sec, plus timezone, jitter_sec, overlap, catchup_window_sec, paused and note. To create from a declaration, send declared: "<key>" instead of spec; namespace and name may then be left out, and any spec or input_data you do send overrides single fields. The API reference at /docs on your Moco server has the exact request and response models.

From a workflow​

A workflow can manage schedules with the builtin.schedule.* activities, for example a setup workflow that installs its package's recurring jobs. builtin.schedule.create replaces an existing schedule of the same name by default, so the setup can be run again safely, and with declared it takes the timing from the workflow's own declaration. The schedules run as the user the setup workflow ran as.

Permissions​

ActionWho may do it
CreateWrite access to the namespace (deploy_wfspec, or namespace admin), and permission to run the workflow
UpdateThe same, plus being the owner or a namespace admin. You become the new owner
Describe, listThe owner, or view access to the namespace
Pause, resume, run now, deleteThe owner or a namespace admin

Your home namespace (your user id) is always yours to administer.

note

A schedule keeps running as its owner. If the owner leaves or loses access, pause the schedule, or have a namespace admin update it so that it runs as someone else.

Limits​

  • Schedules need the Temporal runtime. A server running workflows in memory rejects them.
  • A schedule cannot start an entity workflow (a long-lived workflow addressed by a fixed id).
  • Fire times have one-second resolution. Two fires of the same schedule cannot start in the same second.