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.
Liquid Control Flow
Section titled “Liquid Control Flow”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.
Template-Form Operands
Section titled “Template-Form Operands”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.
Available Operators
Section titled “Available Operators”| Category | Operators |
|---|---|
| Equality | ==, !=, ===, !== |
| Comparison | >, <, >=, <= |
| Logic | and, or, !, !! |
| Branching | if (ternary-style) |
| Membership | in (substring or array contains) |
| Existence | var, missing, missing_some |
| Arithmetic | +, -, *, /, %, min, max |
| String | cat (concat), substr, strlen |
| Regex | matches — case-sensitive regular-expression match |
| Debug | log |
Examples
Section titled “Examples”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$"] }