Scheduling
A job’s schedule is a list of independent points — each one fires on its own. Points are either
clock-based (run on a wall-clock time) or event-based (afterParent, which fires when another
job finishes). Cronable does not use raw cron strings; you pick a structured kind.
Clock-based points
| Kind | Shape | Fires |
|---|---|---|
daily | { kind: daily, time: 'HH:mm' } | every day at that 24-hour time |
hourly | { kind: hourly, minute: 0–59 } | every hour at that minute |
weekly | { kind: weekly, weekday: mon…sun, time: 'HH:mm' } | that weekday at that time |
monthly | { kind: monthly, day: 1–31, time: 'HH:mm' } | that day of the month at that time |
schedule:
- { kind: daily, time: '09:00' } # every day at 9am
- { kind: hourly, minute: 0 } # every hour, on the hourTimes are interpreted in the job’s timezone (an IANA name like Europe/Helsinki) if set, otherwise
the daemon’s default timezone.
Event-based: afterParent (chaining)
schedule:
- { kind: afterParent, parent: fetch-stats, on: succeeded }afterParent fires when the parent job’s run settles. on is succeeded (default), failed, or
completed (any outcome). The child receives the parent’s output as {{ $parent }} (see
Expressions & data flow) — this is how one job hands data to the next, and how the
draggable DAG’s edges are derived. An optional branch: fires the edge only when a switch parent
selected that branch. For error handlers specifically, see Failure handling.
A job can have several points — or none
schedule is a list, so a job can fire on several triggers at once (e.g. a daily point and an
afterParent point) — each fires independently. A job with an empty schedule (schedule: [])
never fires on its own; it runs only when invoked — as an onFailure handler, an afterParent
child, or manually with Run now.
Cooldown between runs — minDelay
minDelay is an optional cooldown measured from a job’s last finish, written as a duration
string ("30s", "20m", "2h"):
minDelay: 20m # at most one run per 20 minutes, measured from the last finishIf a trigger fires again within the cooldown, that run is skipped (recorded as a skipped run with
a reason — not executed), which is handy to debounce a chatty webhook or a fast-firing
afterParent chain. Because it’s measured from when the previous run finished, a slow job never
overlaps its own cooldown. A manual Run now deliberately bypasses minDelay. And if the daemon
restarts while a run is in flight, that run is recorded as finished (interrupted) at restart — so the
cooldown still applies from the restart, and a crash can’t slip an extra run past the minimum spacing.
Trigger jobs are scheduled by their source
The trigger types (webhook, folder-watch, inbox, calendar-watch,
telegram-watch, sheet-watch, hostaway-watch) never take a schedule point — they fire on an
external event (an inbound call, a new file, new mail, a new booking, …) and are always the root of a
pipeline.