Skip to main content

Jobs builder

A Nomaflow job is created and edited from Settings → Jobs. The page lists every job in the catalogue; clicking a row opens the job builder — a form with the schedule, the parameter sheet, the retry policy and the step pipeline (drag-and-drop). Saving reloads the job in the running scheduler.

This page documents every field of the builder.


At a glance

Settings → Jobs → billing-nightly-rebuild
▶ Run nowCancelSave & reload
General
Name
billing-nightly-rebuild
App
billing ▾
Enabled
● On
Schedule
Cron
0 2 * * * 02:00 every day
Timezone
Europe/Paris ▾
Next 5 fires
2026-05-21 02:00
2026-05-22 02:00
2026-05-23 02:00
2026-05-24 02:00
2026-05-25 02:00
Steps
+ Add step
⋮⋮
refresh-totals
sql_query
✏️ ✕
⋮⋮
rebuild-vat
python
✏️ ✕

The mockup shows the General, Schedule and Steps sections. Below them the builder also exposes Parameters, Retry policy, Dependencies and Notifications — each documented below.


General

FieldEffect
NameUnique identifier of the job. Surfaces in the run history, the CLI (liberty-admin job run <name>) and the permission code (job:<name>). Lowercase + hyphens by convention. Renaming is supported — the Rename operation rewrites every reference in one transaction.
AppDropdown of the apps registered on the install. The job is grouped under this app on the catalogue and the job:<app>:* permission key. Defaults to _default (framework-wide).
DescriptionFree text shown in the catalogue list. Helps the next operator.
EnabledToggle. Disabled jobs stay in the catalogue but never fire from cron. Manual triggers (the ▶ Run now button, the REST endpoint) still work.

Schedule

FieldEffect
CronStandard 5-field cron expression. The builder parses it live; the Next 5 fires read-out below confirms what the operator typed (catches a misread 0 2 * * * "02:00 every day" vs 2 0 * * *" "00:02 every day"). Aliases recognised: @daily, @hourly, @weekly, @monthly, @yearly`.
TimezoneIANA timezone (Europe/Paris, UTC, America/New_York). Cron times are interpreted in this timezone. DST transitions are handled automatically.
Next 5 firesRead-only preview of the next five fire times, in the operator's timezone.
Single instanceToggle. When on (default), a fire-time that overlaps with a still-running run is skipped (recorded with reason = "single-instance"). When off, the new run starts and the two run concurrently.
TimeoutOptional hard ceiling on the total run duration. A run past it is aborted. Per-step timeouts are set in the step editor.

Jobs that should only fire on manual / API trigger leave Cron empty.

Cron picker

For operators less comfortable with cron syntax, a Cron picker button next to the expression field opens a dialog with preset templates — Every X minutes, Daily at HH:MM, Weekly on weekday(s) at HH:MM, Monthly on day N at HH:MM. The picker writes the resulting expression into the field; advanced operators can still type the expression directly.


Parameters

A table of job-level parameters available to every step under the params namespace. Useful for sharing a value across steps — typically the period a nightly job is running for.

FieldEffect
NameThe parameter name (e.g. period, dry_run).
Typestring / int / float / bool / date / datetime. Drives the widget on the Run now dialog.
DefaultValue used when no override is provided. Supports the special tokens ${today}, ${yesterday}, ${week.monday}, ${month.first}, ${month.previous}, etc.
DescriptionSurfaces as the field's tooltip on the Run now dialog.

Click ➕ Add parameter to extend the list. The Run now button (top of the page) opens a dialog with one input per declared parameter — manual triggers can override defaults for a single run.


Retry policy

FieldEffect
Max attemptsTotal attempts including the first one. 1 disables retries. Default 3.
BackoffNone / Constant / Exponential. Drives the delay between attempts.
Initial delayFirst delay between attempts. Constant uses this for every retry; Exponential doubles each time.
Max delayCap on exponential backoff. Default 600 seconds.
Retry onMulti-select of failure categories — Error (any exception), Timeout, Connection. Empty list = retry on nothing (synonym of Max attempts = 1).
Add jitterToggle. Adds up to 25% random jitter to each delay — avoids thundering-herd retries. Default on.

Per-step retry policy overrides the job-level default; the step editor exposes the same fields.


Steps

The step pipeline is a drag-and-drop list — one row per step, executed top to bottom. The ⋮⋮ grip on the left re-orders; ✏️ opens the step editor; deletes.

Step builder fieldEffect
NameUnique within the job. Surfaces on the run-detail page.
TypeOne of sql_query, sql_copy, python, http, ldap_sync. Each type expands a type-specific form — see Step types.
ConditionOptional expression — the step runs only when truthy. References ${params.*} and ${previous_step.*}. Falsy → step is skipped, the job continues.
Continue on errorWhen on, a failed step marks the run as partial-success and the rest of the steps run. Off by default.
Retry policyPer-step override of the job-level policy. Same shape as above.
TimeoutPer-step ceiling.

The step editor's body changes with the Type picker — see Step types for each variant.


Dependencies

A multi-select of other jobs that must have succeeded most recently for this job to run.

Most recent status of every dependencyEffect on the current job
succeededRun proceeds normally.
failed / abortedRun is skipped with reason = "dependency-failed: <name>".
Has never runRun is skipped with reason = "dependency-never-ran: <name>".
runningRun is skipped with reason = "dependency-still-running: <name>".

Cycles are refused at save — the builder rejects "job A depends on B, B depends on A".

Use dependencies sparingly; long chains are usually better expressed as one job with several steps.


Notifications

Lightweight routing of the run outcome.

FieldEffect
On successMulti-select of channels notified when the job ends succeeded.
On failureMulti-select of channels notified when the job ends failed / aborted.
On skippedMulti-select of channels notified when the job is skipped.

Channels available:

ChannelConfigured at
Slack #channelSettings → Framework → Notifications → Slack — paste the webhook URL once, every job picks from the channel list.
Email addr@…Settings → Framework → Notifications → Email — SMTP relay credentials.
Webhook <url>Per-job custom URL. POSTs a JSON body with the run id, name, status and duration.
Internal <role>Drops a notification in the in-app notification centre for every user carrying the role.

For richer payloads (the failure log tail in a Slack message), add an explicit http step at the end of the job rather than relying on the notifications block.


Run now

The ▶ Run now button at the top of the builder triggers a one-off manual run. When the job declares parameters, a dialog asks for the values (with the declared defaults pre-filled). Confirming dispatches the run through the same scheduler as a cron-triggered one — same step engine, same retry policy, same persistence.

The button is gated by job:<name> permission; revoke job:*:run for an auditor role that should see jobs but not trigger them.


Save

Saving validates the form:

  • Cron syntax parses.
  • Connector / query / endpoint references in each step resolve against the loaded catalogue.
  • Python callable references import cleanly — a missing function fails the save.
  • Dependencies exist.
  • No cycle in Dependencies.

A failing save shows the diagnostic inline; the catalogue stays on the previous version. A successful save reloads the job in the running scheduler — the next cron tick picks up the new definition.


Permissions

The Jobs tab is gated by settings:jobs. Per-job actions inherit job:<name>. Operators who only need to see runs without editing jobs get jobs:read + job:<name> without settings:jobs.


Under the hood

Job definitions are stored under liberty-apps/plugins/<app>/jobs.toml. Operators do not edit this file by hand in normal operation; the Jobs builder is the canonical interface. The file is parsed at startup and on every Save & reload; advanced operators reach for the Raw TOML tab when a builder gap blocks them.

For CI scripts and external orchestrators, the same surface is reachable via the REST endpoints under /admin/jobs/* — see REST API → Jobs.


What's next