Skip to main content

Steps

A step is one unit of work in a job. Steps run in order; when one fails, the following ones are recorded as skipped: earlier step failed.

The step…Type
runs one named query of a SQL connectorsql_query
copies a whole table from one connector to anothersql_copy
loads a spreadsheet or a CSV file into a tablefile_load
writes the result of a read query to an Excel or CSV filefile_export
calls a Python function of a pluginpython
calls a URLhttp
reads an LDAP / Active Directory directory into a tableldap_sync
runs another job and waits for itcall_job
renders a saved analysis, a dashboard or a report to an Excel or PDF filereport
sends a message, with the files of the run attachednotify

In the job editor, the Add step: bar has one button per type, and each step opens a form with its own fields. A few keys exist only in jobs.toml (noted below).


At a glance​

Steps · sales-copyAdd step:sql_querysql_copyfile_loadpythonhttpldap_synccall_jobsql_copycopy-F0101sourcejdedwards · PRODDTA · F0101targetwarehouse · public · f0101modeoverwrite ▾type_coercionjde ▾batch_size10000sql_queryrefresh-totalsSeven step typesSQL, file, Python, HTTP…Step formfields of its typeoverwritework table, swapped at the endRun in orderretries apply per step

Common fields​

FieldDefaultNotes
name—Shown in the run detail; unique within the job.
type—One of the ten types.
enabledtruefalse skips the step (recorded as skipped: disabled). Set in jobs.toml, or for one run in the Run with parameters window.
timeout_seconds3600The step is cancelled after this time. Set in jobs.toml.
continue_on_failurefalsetrue: when the step fails (after its retries), the next steps run anyway and the run ends failed, naming it — for a job chaining independent work. A checkbox in the step editor; for every step of a job, [jobs.step_defaults].

Turn a step off for an install. A step the install doesn't need — the LDAP collection without LDAP — is listed in the job's disabled_steps = ["SECURITY_LDAP"] (the step editor's Turned off for this install checkbox). It never runs, even when a run or a preset selects every step: the Run with parameters window shows it off and not selectable, the run records it as skipped: turned off for this install. Unlike enabled — the default a run may override — it is the operator's last word, and the licensed apps installer keeps it across upgrades.

When a step fails on a database error, the run detail shows the database's own sentence — invalid input for query argument $1: 2025 (expected str, got int) — and the statement goes to the server log.


SQL query — sql_query​

Runs one named query of a SQL connector — a query defined on the connector's page, read or write.

FieldRequiredNotes
connectoryesThe SQL connector.
queryyesThe name of the query, not SQL text.
paramsnoValues bound to the query's parameters.
poolnoAnother pool of the connector's set — see Multi-pool connectors. jobs.toml only.

Records the number of rows returned or affected. To refresh a materialised view, call a procedure or purge a table, write the statement as a query of the connector, then call it here.


SQL copy — sql_copy​

Streams the rows of a source table to a target table, possibly on another database.

FieldDefaultNotes
source = {connector, schema, table}—Required. pool may be added (jobs.toml only).
target = {connector, schema, table}—Required. Same.
modeoverwriteoverwrite: the rows are written to a work table <table>__new, which then replaces the target in one transaction — the target is never empty during the copy. append: rows are inserted into the target as it is.
type_coercionjdejde applies the JD Edwards rules (scaled decimals, Julian dates, padded codes); none copies the values as they are.
decimal_modetruncateWith jde: truncate or preserve the decimal part.
batch_size10000Rows per batch.
strip_both_columns[]Columns whose values are trimmed on both sides.

upsert is accepted by the schema but refused at run time: for an insert-or-update, write a MERGE / ON CONFLICT query and call it with sql_query. Records the number of rows written.


File load — file_load​

Loads an Excel workbook (.xlsx, .xlsm) or a text file (.csv, .txt, .tsv) into a table — on a schedule, without anyone at the keyboard, at the volume a facts table needs, and replayable for one period without touching the others.

The file​

The file comes from, in this order of priority:

  1. a file chosen when the job is run — the Run with parameters window shows a file picker for each file_load step (the window opens for any job that has one), and so does the job's page opened from an application's menu, for the steps with neither path nor watched folder;
  2. path — a path on the server, which may contain ${params.x} (a run parameter), ${run.id} and ${job.id}: /data/in/sales-${params.period}.xlsx;
  3. a watched folder — watch_dir and watch_pattern (*.xlsx): every matching file is loaded in name order, each in its own transaction, then moved to done/ or failed/ under a time-stamped name. A file that fails stops the run; the files after it stay in the folder. An empty folder is not an error.
FieldDefaultNotes
sheetfirst sheetA name or a number (1 = first).
header_row1The row of the column headers; data starts below. Empty rows are skipped.
delimiterdetectedCSV separator; detected among ; , tab `
encodingutf-8-sigCSV only.

The target​

FieldNotes
target = {connector, schema, table}A SQL connector; the table must exist.
columnsTarget column ← file header or position (#3). Headers match ignoring case, spaces, _ and -; unmapped columns match by name.
constantsTarget column ← value, for what the file does not carry: period = "${params.period}", batch_id = "${run.id}".

Each cell is converted to the type of its target column: integers, decimals (12,5 and 12.5 both read as 12.5; a lone comma is the decimal separator), dates (2026-10-27, 27/10/2026, 27.10.2026, 20261027, and Excel dates), booleans (1, true, yes, oui…), text. A number typed as text keeps no leading zeros that the spreadsheet already removed.

Load mode​

FieldChoice
load_modeappend (default) inserts the rows. replace_partition first deletes the rows of the partitions the file carries, then inserts — in one transaction: a failed load leaves the previous data in place, and a corrected month replaces that month only.
partition_byThe partition columns (period, company) — required with replace_partition. The partitions come from the constants when they cover every column, otherwise from the distinct values found in the file.
on_errorfail (default): the first bad cell fails the step and names the row. skip: bad rows are counted as rejected and written to a CSV (file, row, reason) offered in the run detail.
batch_size10000 rows per insert.

Guards​

FieldNotes
refuse_queryA named query run before anything is touched, with the run parameters and the constants as binds. With partition_by, it runs once for each partition the file carries, with the partition columns as binds (:YEAR, :MONTH, :COMPANY_CODE): the guard follows the file, not a parameter typed when the job is run. If it returns a row, the load is refused and the first column is the reason: period 2026-02 is closed. refuse_connector: its connector, the target's by default.
control_column + control_totalThe column is summed over the loaded rows and compared with a value, ${params.total}, or a cell of the file (@B2, @Totals!B2), within control_tolerance. A difference rolls the file's load back and names both figures.

In the run​

The run detail shows N read · N inserted · N rejected and, when rows were rejected, Rejected rows (csv) to download them. The log adds the partitions replaced and the rows deleted.

Uploaded files and reject files go to the jobs' work area, .nomaflow/files next to the configuration; an upload is limited to 200 MB.

jobs.toml
[[jobs.steps]]
type = "file_load"
name = "load-sales"
path = "/data/in/sales-${params.period}.xlsx"
sheet = "Sales"
target = { connector = "sales", schema = "public", table = "fact_sales" }
columns = { company = "Company", article = "Item", qty = "Quantity", revenue = "Revenue" }
constants = { period = "${params.period}", batch_id = "${run.id}" }
load_mode = "replace_partition"
partition_by = ["period", "company"]
refuse_query = "period_closed_get"
control_column = "revenue"
control_total = "@Totals!B2"

Python — python​

Calls a function of a plugin: callable = "package.module:function".

FieldNotes
callablemodule.path:function — checked for shape when saved, imported when the step runs.
op_kwargsArguments passed to the function, merged over the job's shared parameters.

The function receives its arguments as keyword arguments and, when it declares them by name, connectors (the connector registry), ctx (the run: ctx.run_id, ctx.job_id, ctx.trigger) and settings. It may be async or not. Its return value: an int is the number of rows affected, a dict is kept as the step's details, None is fine; an exception fails the step. To reach the run log, write with a logger of a registered namespace. The full contract is in Write a callable.


HTTP — http​

FieldDefaultNotes
url—Required. Used as written.
methodGETGET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.
headers{}Sent as they are.
bodynoneText, or a table sent as JSON (with Content-Type: application/json when none is set).

A 2xx answer succeeds; anything else fails the step with the status. The step keeps the status, the first 4 KB of the answer and the duration. Values from the environment are written ${NAME} in jobs.toml and replaced when the file is loaded.


LDAP sync — ldap_sync​

Reads entries from an LDAP directory and runs a write query of a SQL connector for each entry.

FieldNotes
serverldaps://ad.example.com:636.
bind_dn, bind_passwordThe account used to read; write the password as ${LDAP_PASSWORD}.
search_base, search_filterWhere and what to search: DC=example,DC=com, (objectClass=user).
attributesThe attributes to read.
target_connector, target_queryThe write query run for each entry.
mappingLDAP attribute → query parameter. A missing attribute gives NULL.

Records the number of entries read and rows written.


Call a job — call_job​

target_job_id names another job. The step starts it, waits for it to end and takes its result: success, failure or cancellation. The called job has its own run in the history, and inherits the caller's log level and run parameters. A job that would end up calling itself is refused.


Report — report​

Renders a document into the run's work area: a saved analysis or dashboard of the Data Explorer, or a report of a plugin. Followed by a notify step, it makes a scheduled digest a plain job.

sales-monthly-digestRevenue by business unit, rendered last month and e-mailedreportrevenueanalysis:revenue_by_business_unit → xlsxWHAT TO RENDERSourceA saved analysis (Explorer) ▾Explorer modelSales · sales ▾AnalysesRevenue by business unit ▾FormatExcel ▾File namemonthly-salesPeriodLast month ▾Render as usercfoRender as roleUnrestricted ▾notifysendMESSAGERecipientsrole:managementemail:cfo@example.comSubjectSales digest {DATE}Attach (run files; ∗ = all)∗ChannelsportalemailSaved objectshared analyses and dashboardsVisibilitya user's or a role's data scopeRun files attachedevery file, or by namePeriod presetelse the analysis's ownPlaceholders{JOB} {RUN_ID} {FILES} {DATE}
Field (editor)Notes
SourceA saved analysis (Explorer), A dashboard of analyses (Explorer) — one section per tile — or A report (plugin).
Explorer model, Analyses / DashboardsThe model, then one of its shared analyses or dashboards. A private one cannot be picked.
ReportFor a plugin report: the report, from the reports of the installed applications.
FormatExcel or PDF — the same documents as the Explorer's export, the charts drawn in the PDF.
PeriodA preset (Last month, This quarter, Last 7 days…, or all dates), or The analysis's own period.
File nameThe file's name; by default, the analysis's title.
Render as userThe document holds what that user sees: the data scope of their roles, with their attributes. A digest rendered as the manager of one company holds that company only.
Render as roleThe data scope of a role. A rule whose values come from a user attribute allows nothing here: prefer Render as user for such roles.

For a plugin report, the run's parameters — the job's, those chosen when running it, the user parameters — are passed to the report on top of the step's own, converted to the type the report declares (a number stays a number); a blank value leaves the report's default.

With neither user nor role, the document is unrestricted. The run detail offers the file for download under the step; the log names it with its size.


Notify — notify​

Sends a message through the notification channels, with the files this run produced attached.

Field (editor)Notes
Recipientsuser:<login>, role:<role> or email:<address>, one per line.
Subject, BodyText with placeholders: {JOB}, {RUN_ID}, {FILES} (the names of the attached files), {DATE} (today).
Attach (run files; * = all)* attaches every file the run produced — every report, and the rejected rows of a file_load; or name the files. A named file the run did not produce fails the step.
Channelsportal (the bell) and email. The e-mail account is the one of Notifications.

The message links to the run. The run detail lists the attached files under the step.

jobs.toml — a monthly digest
[[jobs.steps]]
type = "report"
name = "revenue"
report = "analysis:revenue_by_business_unit"
app = "sales"
model = "sales"
format = "pdf"
period = "lastMonth"
as_user = "cfo"

[[jobs.steps]]
type = "notify"
name = "send"
recipients = ["role:management"]
subject = "Sales digest {DATE}"
body = "The monthly sales digest is attached: {FILES}."
attach = ["*"]

Schedule the job on the first of the month (cron 0 7 1 * *) — see Schedules.


File export — file_export​

Writes the result of a named read query to a file in the run's work area — for another system to pick up, or for a notify step to attach. A query that writes is refused.

Field (editor)Notes
Connector, QueryThe read query to run.
ParametersIts binds; the run's parameters are passed too. A value may use ${params.x}.
FormatExcel (xlsx) (default) or CSV.
File name (default: the query)May use ${params.x} and ${run.id}: sales-${params.period}.
SheetExcel only — the query's name by default.
CSV delimiter, EncodingCSV only — ; and utf-8-sig by default.
Also copy to this folder (optional)A server folder where a copy is written as well, created if missing: /data/out/${params.period}.

The rows are read from the database as they come, up to the connector's row limit — the log says when the export was capped. The header row carries the columns' labels; hidden columns are left out. The run detail offers the file under the step, and the retention of the work area applies to it — not to the copy in the other folder.


Retries and timeouts​

The job's Retry policy block (attempts, backoff fixed or exponential, base_seconds) applies to each step: a step is tried up to attempts times in all (1, the default, means no retry), with a wait of base_seconds between tries — constant, or doubling with exponential. The job as a whole stops after timeout_seconds (4 hours by default).