Free Intro Class
    Botspot Logo

    agenthub · automation.filter

    Filter

    The node that decides who the rest of the workflow runs for. It takes a list in, tests every row against a condition you build on the node itself, and passes through only the rows that match.

    category Automationin rows · dataout outputexecution asyncmodel cost none

    Why the node earns its place

    An automation is a fan-out, and Filter decides how wide. A batch pipeline reads a dataset then does something per row — send the message, start the run, raise the invoice. The dataset is almost never the audience: out of four hundred members, thirty-seven are due this week.

    It is the only step in that chain that is both deterministic and free. No model is called, so nothing is spent deciding, and the verdict is a fixed comparison you can read off the node when someone asks why a member was or was not contacted. Every row it drops before a Fan out is an agent run that is never billed.

    How it works at run time

    1. 1
      Find the list: a wired rows input wins, else rows_path walked into data, else data itself if it is already an array. Nothing found is an empty list, not an error.
    2. 2
      Per row, read the left side — field is a dotted path into that row; blank means the whole row.
    3. 3
      Read the right side according to value_source: a literal, a run variable, or another field of the same row.
    4. 4
      Normalise both sides together, then evaluate the tree — each group is all-of or any-of its children, nested groups recurse, empty groups are skipped.
    5. 5
      Collect the rows whose verdict matches keep, so non-matching turns the whole predicate into a drop-list.

    What comes out

    output.rows     the rows that passed, unchanged
    output.count    how many passed
    output.dropped  how many did not — watch this in a Test Run

    What you wire

    Inputs

    • rows
      array
    • data
      object

    Outputs

    • output
      object

    Configuration

    KeyTypeDefault & optionsWhat it does
    rows_pathstringPath to the array inside the wired input `data` (e.g. 'data.results'). Ignored when the `rows` input is wired directly.
    conditionsobjectdefault {"join":"and","children":[]}Condition tree: {join:'and'|'or', children:[...]}. A child is either a leaf {field, operator, value, value_source} or a nested group with its own join. One joiner per group — no mixed AND/OR at a level. Empty groups are skipped.
    fieldstringLEGACY single-condition field. Superseded by `conditions`; still honoured when no tree is set.
    operatorstringtruthy · falsy · exists · absent · eq · ne · gt · gte · lt · lte · contains · not_contains · in · not_inLEGACY single-condition operator. Superseded by `conditions`.
    valuestringLEGACY single-condition comparand. Superseded by `conditions`.
    value_context_keystringLEGACY — read the comparand from run context. Superseded by a leaf with value_source='context'.
    normalizestringdefault "none" none · trim · lower · phone10Normalize both sides before comparing (per row).
    keepstringdefault "matching" matching · non_matchingKeep rows that MATCH the condition (default), or invert to keep the ones that DON'T (e.g. drop opted-out).

    What usually goes wrong

    Watch for this

    dropped tells you the truth fastest. A filter that drops everything is nearly always a path problem, not a logic problem — check field against the shape the run actually observed before touching the operator.

    One joiner per group: a box is all-AND or all-OR, never mixed. That restriction is what lets the box border stand in for a bracket, so there is no second bracket notation to learn. An empty group constrains nothing rather than dropping every row.

    Behaviour & provenance

    buildersAutomations
    routesno
    side effectsnone — computes only
    replay safetynot applicable
    talks tonothing outside the run
    holdsno credentials