ACTION_ID: filter NAME: Filter CATEGORY: Data Operations CREDITS: 0 Skip rows that don't satisfy a set of conditions. Rows that pass continue to the next action in the chain. Rows that fail stop here — downstream cells stay queued and never run for that row. Distinct from `run_if` on a regular action: `run_if` gates ONE action per row, `filter` gates the entire downstream chain. Use filter when several downstream actions all share the same row-level prerequisite (non-empty email, score above a threshold, country in a list, etc.) so you don't have to copy the same `run_if` onto each one. INDEX: 1. Inputs 2. Outputs 3. How to configure 4. Key notes 5. Where it fits in a workflow 6. When to use 7. When not to use 8. Operator catalog ================================================================================ 1. INPUTS ================================================================================ path_conditions (array of condition groups, required) An array of GROUPS. A row passes the filter when the whole boolean expression — groups joined by their `combinator`, leaves inside each group also joined by their `combinator` — evaluates true. Each entry in `path_conditions` is a GROUP: conditions Array of leaf conditions (described below). Leaves are joined left-to-right using each leaf's `combinator` (AND / OR). The first leaf's `combinator` is ignored — it has nothing to combine with. combinator How this group joins the previous group at the group level: `"AND"` (default) or `"OR"`. Ignored on the first group. Each entry in a group's `conditions` is a LEAF: variable Public reference token whose resolved per-row value is compared. Use `{{input.}}` for a sheet input or `{{.}}` for an upstream action's output. operator Comparison operator from the catalog in section 8. The variable's stored type is auto-derived from the chain — you don't pass it. values Array of comparison values. Required for most operators; ignored by `is empty` / `is not empty`. For `is between`, pass `[low, high]`. Each item is a literal — variable references inside `values` are NOT resolved. combinator How this leaf joins the previous leaf in the same group: `"AND"` (default) or `"OR"`. Ignored on the first leaf in a group. ================================================================================ 2. OUTPUTS ================================================================================ (no output fields — passing rows continue unchanged; failing rows stop at this action and downstream cells are absent.) ================================================================================ 3. HOW TO CONFIGURE ================================================================================ Configure Action body — single group, all leaves AND'd (equivalent to: email is not empty AND employee_count >= 2): { "inputs": { "path_conditions": [ { "conditions": [ { "variable": "{{input.email}}", "operator": "is not empty" }, { "variable": "{{enrich_company_linkedin_profile_1.employee_count}}", "operator": "greater than or equal to", "values": ["2"], "combinator": "AND" } ] } ] } } Configure Action body — two groups joined by OR (equivalent to: (email is not empty AND employee_count >= 2) OR (size between 1 and 5)): { "inputs": { "path_conditions": [ { "conditions": [ { "variable": "{{input.email}}", "operator": "is not empty" }, { "variable": "{{enrich_company_linkedin_profile_1.employee_count}}", "operator": "greater than or equal to", "values": ["2"], "combinator": "AND" } ] }, { "combinator": "OR", "conditions": [ { "variable": "{{enrich_company_linkedin_profile_1.size}}", "operator": "is between", "values": ["1", "5"] } ] } ] } } Field-by-field: - path_conditions Array of GROUPS. Each group is a `{ conditions[], combinator? }` object. - path_conditions[].combinator `"AND"` (default) or `"OR"`. Determines how this group joins the previous group. Ignored on the first group. - path_conditions[].conditions Array of LEAF conditions inside the group. - path_conditions[].conditions[].variable Public reference token. - path_conditions[].conditions[].operator Comparison operator (section 8). - path_conditions[].conditions[].values Array of literal comparison values. - path_conditions[].conditions[].combinator `"AND"` (default) or `"OR"`. Determines how this leaf joins the previous leaf in the same group. Ignored on the first leaf. Pass `values` as an array of strings; number / date matchers coerce as needed. An empty `path_conditions` array passes every row. ================================================================================ 4. KEY NOTES ================================================================================ - Two layers of `combinator`. Leaves inside a group are joined left-to- right by each leaf's `combinator`; groups are joined left-to-right by each group's `combinator`. Default at both levels is `"AND"`. The first leaf / first group's `combinator` is ignored — it has nothing to combine with. - Mixing AND / OR in the same flat list. Because every leaf carries its own `combinator`, you can express `A AND B OR C` inside a single group. To force precedence (`(A AND B) OR (C AND D)`), put the AND-clauses in separate groups and join the groups with `OR`. - `variable` is auto-typed from the chain. The same operator can mean different things for different types (`contains` for strings vs. arrays); the catalog in section 8 lists what's valid per type. - Empty `path_conditions` (no groups) passes every row. To drop every row, use a contradictory condition or just delete the action. - Rows that fail the filter are NOT errors — their cell status on the filter action is `complete` (the filter ran), but downstream cells stay `queued` indefinitely. Treat that pattern as expected, not a bug, when polling Rows / List. ================================================================================ 5. WHERE IT FITS IN A WORKFLOW ================================================================================ UPSTREAM — what produces the value(s) being filtered on Sheet inputs (most common): `{{input.country}}`, `{{input.email}}`. Enrichment outputs: `{{enrich_company_linkedin_profile_1.employee_count}}`, `{{person_work_email_waterfall_1.email}}`. Classifier outputs: `{{llm_models_1.industry}}`. THIS ACTION Evaluates each condition for the current row. Rows that pass continue to the next action; rows that fail stop here — downstream cells stay queued and never bill. DOWNSTREAM — actions that should only run for qualifying rows Outreach: `instantly_add_to_campaign`, `reply_add_and_push_to_campaign` (only contact qualifying leads). CRM updates: `hubspot_update_object`, `salesforce_update_record` (only sync rows that meet the bar). Expensive enrichment: `person_enrich_using_apollo`, `scrape_web_page_using_firecrawl` (only spend credits on the rows that need it). ================================================================================ 6. WHEN TO USE ================================================================================ Use filter when the same row-level prerequisite gates several downstream actions, OR when you want failing rows to short-circuit the rest of the chain instead of running every action and writing empty / placeholder values. Email-required outreach Several downstream actions (`instantly_add_to_campaign`, `reply_add_and_push_to_campaign`) require a non-empty work email. Add filter on `{{.email}} is not empty` once upstream of the outreach actions instead of putting the same `run_if` on each one. Country / region routing Filter on `{{input.country}} is "US"` or `is in ["US", "CA"]` so US/CA rows continue to the US-specific actions and other rows skip the rest of the chain. Quality threshold gating Filter on `{{.score}} greater than or equal to 70` so only high-quality rows reach the outreach / CRM-write actions. Skip rows already in CRM Pair `hubspot_lookup_object` upstream with filter on `{{.id}} is empty` to keep only rows that don't already exist in HubSpot. ================================================================================ 7. WHEN NOT TO USE ================================================================================ Skip filter when: Only one downstream action needs the gate Use `run_if` on that single action instead — no need to insert a filter node into the chain. The decision is per-action, not per-row `run_if` is per-action; filter is per-row-and-then-downstream. If different downstream actions need different conditions, use multiple `run_if`s rather than chaining filters. You want failing rows to still run downstream Filter halts the chain for failing rows. If you want every row to reach the next action regardless, set `run_if` with `continue_workflow_if_run_condition_not_met: true` on that action instead. ================================================================================ 8. OPERATOR CATALOG ================================================================================ The variable's type is auto-derived from the chain — pick the operator from the row that matches the variable's stored type: Universal (any type) is, is not, is empty, is not empty `is empty` / `is not empty` ignore `values`. string / email / url / imageUrl contains, does not contain, starts with, does not start with, ends with, does not end with Matching is case-insensitive. number / currency greater than, less than, greater than or equal to, less than or equal to, is between For `is between`, pass `values: [min, max]`. date is after, is before, is between Values are parsed by `moment(...)`. For `is between`, pass `values: [start, end]`. array / structured_array / json contains, does not contain ================================================================================ This file is maintained manually. Last updated: 2026-05-08. Full interactive reference: https://floqer.com/docs/reference Action catalog: https://floqer.com/docs/action-catalog.txt