Scheduling

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

KindShapeFires
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 hour

Times 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 finish

If 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.