Skip to content

Variables and Expressions

Any string in a step’s config can contain {{ … }} placeholders. FlowMint replaces each one with a value from the run just before the step runs: "Hello {{ labels.name }}" becomes "Hello Ada Lovelace".

PathWhat it holds
data.<field_key>The value stored for each form field. For a dropdown, radio or checkbox this is the option value (joinery). A checkbox with several ticks is a list.
labels.<field_key>The same fields, with option values replaced by the label the visitor saw (Hand-Cut Joinery Intensive). Fields without options hold the same value as data.
entry.id, entry.form_id, entry.status, entry.created_at, entry.updated_at, entry.ip_addressThe stored entry’s details. Field values are not here: entry.fields.email does not exist, use data.email.
entry_files.<field_key>The file uploaded in a file field: file_name, file_path, file_size, mime_type, attachment_id, id, entry_id, field_key, created_at. There is no file_url.
steps.<step_name>.<field>The output of an earlier step, by its name.
vars.<name>A value stored by a set_variable step.
run.id, run.started_atThe current run.
workflow.id, workflow.titleThe workflow’s id and title.
form.id, form.titleThe form’s id and title; empty in a scheduled run. The titles are empty up to version 0.9.0.
env.site_name, env.site_url, env.admin_emailSite settings. These three are all env holds.
item.<field>The current record, only inside the map of pre_upsert_records.

Use labels.* for anything a person reads (email subjects and bodies, file names, notes) and data.* for anything a machine reads (HTTP bodies, Printavo fields), which wants the stable value.

entry_files changes shape with the number of files: one upload is a single object, two or more are a list. {{ entry_files.photos.file_name }} works for one file and is empty for two. The file steps (fre_get_file, drive_upload_file) use the first file either way.

Inside the braces you can write:

  • A path, as in the table: {{ data.email }}.
  • A fallback: {{ data.company || data.full_name }} gives the first value that is not empty. {{ data.phone || 'not given' }} falls back to text.
  • A literal: 'text' or "text", a number, true, false, null.
  • A function call, from the list below.

There are no filters: {{ data.email | upper }} resolves to an empty string.

When a string is exactly one placeholder, the value keeps its type: a list stays a list and a number stays a number. That is how "records": "{{ steps.fetch.body.items }}" passes a whole list. When the placeholder sits inside other text, the value becomes text; a list becomes its items joined with , , and true and false become true and false.

FunctionReturns
now('Y-m-d')The current site-local date and time in a PHP date format. With no argument, Y-m-d H:i:s.
template('name')The contents of wp-content/uploads/fmw-templates/name.html (or .txt if there is no .html), with its own placeholders filled in. Empty if neither exists.
has_file(entry, 'field_key')True when a file was uploaded in that field. The first argument is always entry.
is_empty(x)True when x is empty, 0, '0', false, null or an empty list.
length(x)The number of items in a list, or of characters in text.
contains(haystack, needle)True when a list contains the value, or text contains the text (case-sensitive).
equals_ci(a, b)True when two texts match, ignoring case.

An unknown function name resolves to an empty string and writes a warning to the log. Arguments cannot themselves be function calls.

skip_if on any step and if on the conditional step take a condition. You can use:

  • values: paths, literals and function calls;
  • comparisons: ==, !=, >, <, >=, <= (two numbers compare as numbers, so '5' == 5 is true);
  • logic: &&, ||, ! and parentheses.
{ "if": "{{ data.service == 'pothole' || data.service == 'streetlight' }}" }
{ "skip_if": "{{ is_empty(data.email) }}" }
{ "skip_if": "{{ !has_file(entry, 'photo') }}" }
{ "if": "{{ length(data.notes) > 100 && !is_empty(data.full_name) }}" }
{ "if": "{{ has_file(entry, 'photo') }} && {{ data.rush == 'yes' }}" }

A value counts as false when it is empty, 0, '0', false, null or an empty list; anything else is true. Inside one {{ }}, a || b on its own gives the first value that is not empty, so {{ data.nickname || data.name }} == 'Pat' compares a name.