Schedules
How a recurring schedule is set from plain words, how timezones and daylight saving behave, and what happens when a schedule stops making sense.
Tell the interview when the work should happen and it sets the schedule. "Every weekday at 9am" becomes a five-field cron expression and a timezone, and the interview reads it back to you in words before you publish: "that's every weekday at 9am, Europe/Berlin."
You can say it in whatever form is natural. What the interview will not do is convert your digits for you. If you say 9am, the schedule says 9am, and the zone is what decides which 9am that is.
Or write the cron expression yourself
Settings has a Workspace section with a Schedules list and an Add
button. The dialog asks for three things: which workflow, a cron expression,
and a timezone. The expression field takes a raw five-field expression such as
0 9 * * 1, with no builder and no preview.
Each schedule in that list shows its expression and zone, when it next runs, and
a switch to turn it on and off. The workspace shows the same schedule read back
in words, so 0 9 * * 1 appears there as "At 09:00, only on Monday".
Two things the form does not do: there is no edit and no delete, only the switch, and the console does not show when a schedule last fired.
Add always creates a new schedule. It never replaces one. To change the timing of a workflow that already runs on a schedule, tell the interview the new timing and publish, which re-points the schedule the workflow already has.
A schedule names a timezone
Cron fields are wall-clock fields. On their own they mean nothing until you say
whose clock they are read against, so a schedule carries an IANA timezone name
such as Europe/Berlin or America/Los_Angeles.
The zone comes from one of three places, in order:
- The zone you said. "9am Pacific" sets
America/Los_Angeles. - Your account default. Settings, under Workspace, has a Your timezone card. New schedules default to it, and the interview uses it when you say a local time without naming a zone. Leaving it at "Not set (UTC)" is a real answer, not an unfinished one.
- A question. With neither of those available, the interview asks once: "9am in which timezone?"
If you still do not answer, the schedule is set without a zone, its digits are read as UTC, and the interview tells you so in plain words. An unzoned schedule is a real schedule, not a broken one. It just means UTC.
Daylight saving moves the schedule, it never doubles it
A zoned schedule follows that zone's wall clock all year. A 9am Berlin schedule fires at 9am Berlin in January and at 9am Berlin in July, and the UTC instant behind it changes when the clocks do. That is the whole point of naming a zone.
The two awkward days of the year are handled explicitly:
- When the clocks go forward, a local time inside the skipped hour does not exist. The occurrence fires at the first instant after the gap instead of being dropped.
- When the clocks go back, a local time inside the repeated hour exists twice. The occurrence fires once, on the first of the two instants.
So a daily schedule fires once a day, every day, including those two. You never need to add extra hours to a cron expression to cover both offsets, and the interview is instructed not to: doing that would run the workflow twice a day for the other 363 days.
What a scheduled run receives
A scheduled run is not woken up empty. Its payload carries the occurrence's own
time under the reserved key fired_at, as a UTC timestamp:
{ "fired_at": "2026-08-19T07:00:00+00:00", "schedule_tz": "Europe/Berlin" }schedule_tz rides alongside, and only when the schedule names a zone. Both
keys are reserved by the platform and win over anything else in the payload, so
a step can select fired_at and know what it is getting.
fired_at is the occurrence's identity, not the wall clock at the moment the
code ran. It stays the same across a retry, which is what lets a retried
occurrence agree with itself. The agent is separately told the actual current
time, and the two read correctly together: this is the 09:00 occurrence, and
right now it is 09:00:07.
See Trigger payloads for what else can be in there.
Missed occurrences are not replayed
If the platform is down when a schedule is due, one overdue occurrence fires when it comes back. It does not backfill every interval that was missed while it was away.
This matters most for a schedule that runs often. A workflow on an hourly schedule that was unreachable for six hours runs once on recovery, not six times.
A schedule that stops making sense turns itself off
Three things can make a schedule impossible to keep firing, and all three are permanent conditions that no amount of retrying fixes:
- Its cron expression no longer parses.
- Its timezone name is no longer one the system recognises.
- Its trigger is no longer registered, so the occurrence has nowhere to go.
When any of them happens, the schedule is disabled and you are notified. The notification says the workflow could not start, gives the reason, and tells you that publishing the workflow again re-arms it.
The alternative would be a workflow that quietly stopped running while the console still showed it as armed, which is exactly the failure this exists to prevent. Nothing is disarmed silently.
Polling runs on a cadence, not a wall clock
An event trigger against a service with no webhooks is driven by polling, and polling also uses a schedule underneath. The difference is that a poll cadence is an interval rather than a time of day, so it carries no timezone at all. The default cadence is every five minutes.
You do not set that cadence during the interview. Choosing your own poll interval is not something the platform supports today.
Overview
The three things that can start a run, how a trigger gets armed and disarmed, and what happens when the same event arrives twice.
Event triggers
How a workflow runs when something happens in another system, why there is no catalogue of ready-made integrations, and how a broken trigger gets repaired.