Skip to content

Conditions and branching

QuickFlo uses two forms of conditional logic. Liquid controls text inside a template; JSONLogic decides which workflow branch or data item should continue.

These tags work inside Liquid template strings (step config fields, return values, etc.). For if/switch steps and data-pipeline predicates, see Conditionals & Data-Pipeline Logic below.

Templates support LiquidJS control flow tags for conditional content:

{% if fetch-users.$meta.success %}
Found {{ fetch-users.data | size }} users
{% else %}
Step failed: {{ fetch-users.$meta.error.message }}
{% endif %}

See the LiquidJS tags documentation for if/elsif/else, unless, case/when, for loops, and more.

Conditionals & Data-Pipeline Logic (JSONLogic)

Section titled “Conditionals & Data-Pipeline Logic (JSONLogic)”

core.if, core.switch, and the predicates inside data.filter, data.reduce, and data.classify evaluate JSONLogic expressions — not Liquid. JSONLogic is a JSON-shaped expression language with operators as keys and operands as arrays.

The recommended way to reference data inside a JSONLogic operand is the template form — wrap a Liquid expression in a string:

{ "==": ["{{ $item.status }}", "active"] }

QuickFlo’s resolver renders the templates first, then hands the rendered tree to the JSONLogic evaluator. This is the same syntax you use everywhere else in step config, so there’s nothing new to learn.

The legacy form uses a var operator with a path string:

{ "==": [{ "var": "$item.status" }, "active"] }

Both forms still work, but the template form is preferred — it’s consistent with the rest of the platform and supports filters, default values, and the full Liquid grammar inside the operand.

CategoryOperators
Equality==, !=, ===, !==
Comparison>, <, >=, <=
Logicand, or, !, !!
Branchingif (ternary-style)
Membershipin (substring or array contains)
Existencevar, missing, missing_some
Arithmetic+, -, *, /, %, min, max
Stringcat (concat), substr, strlen
Regexmatches — case-sensitive regular-expression match
Debuglog

core.if — branch on a step output:

{
"stepId": "is-paid",
"stepType": "core.if",
"input": {
"condition": { ">": ["{{ fetch-customer.body.balance }}", 0] },
"then": { "steps": [ /* charge them */ ] },
"else": { "steps": [ /* skip */ ] }
}
}

core.switch — multiple cases on the same value:

{
"stepId": "route",
"stepType": "core.switch",
"input": {
"cases": [
{
"id": "north-america",
"when": { "in": ["{{ initial.country }}", ["US", "CA", "MX"]] },
"steps": [ /* ... */ ]
},
{
"id": "europe",
"when": { "in": ["{{ initial.country }}", ["UK", "DE", "FR"]] },
"steps": [ /* ... */ ]
}
],
"default": { "steps": [ /* ... */ ] }
}
}

data.filter — keep items matching a predicate:

{
"stepId": "active-only",
"stepType": "data.filter",
"input": {
"items": "{{ fetch-users.body.data }}",
"filter": {
"and": [
{ "==": ["{{ $item.status }}", "active"] },
{ ">": ["{{ $item.lastLoginDays }}", 0] }
]
}
}
}

data.reduce — conditional aggregation:

{
"stepId": "summarize",
"stepType": "data.reduce",
"input": {
"items": "{{ fetch-orders.body.data }}",
"reduce": {
"highValueCount": {
"operation": "count",
"condition": { ">": ["{{ $item.amount }}", 1000] }
},
"totalRevenue": {
"operation": "sum",
"field": "{{ $item.amount }}"
}
}
}
}

matches — regex predicate:

{ "matches": ["{{ $item.email }}", "@(gmail|yahoo)\\.com$"] }