Skip to main content

Filters

Internal beta — not released yet

The Automation API is an internal beta and is not released yet. Its endpoints and payloads are not final and may change or be withdrawn at any time without notice.

A filter decides whether an automation runs: a recursive and/or tree of conditions the triggering record must match. An automation's filter is null when it has none (it always runs). The same tree shape is reused for a conditional action's condition and a for_loop's break/continue conditions — see actions.

The same tree is used everywhere conditions appear

This public tree (operator: "and" | "or", rows) is used for an automation's filter, a control-flow action's condition / break_condition / continue_condition, and a condition embedded inside an action's config (e.g. a collect_app_records / filter_record_collection match_condition) — all with the same lower-case operator + rows shape.

The tree: groups and conditions

A group combines its rows with and (match all) or or (match any); rows are nested groups or leaf conditions. The root group has no id; nested nodes carry one.

FieldTypeOnDescription
idstringnested nodesOpaque node ID. Optional on input (server-minted when omitted); always present on read.
operatorstringgroupand or or.
rowsarraygroupNested groups and/or conditions.
subjectobjectconditionA field reference — the value being tested.
operatorstringconditionOne of the operators below.
valueobjectconditionThe tagged comparison value (below). Absent for value-less operators.
codearrayconditionA raw code predicate — a code dynamic value. A script condition carries code instead of subject/operator/value.
Filter — "Priority is High AND Amount ≥ 1000"
{
"operator": "and",
"rows": [
{
"subject": { "kind": "variable", "source": "field", "field_id": 512, "field_type": "single_category", "previous": false, "collection": false, "triggering": false },
"operator": "is_any_of",
"value": { "type": "ids", "ids": [9001] }
},
{
"subject": { "kind": "variable", "source": "field", "field_id": 513, "field_type": "number", "previous": false, "collection": false, "triggering": false },
"operator": "greater_or_equal_than",
"value": { "type": "number", "number": ["1000"] }
}
]
}

Operators

There are 22 operators. Each has a value_aritynone (no value, e.g. is_empty), single, or multiple. The operators valid for a given field type differ; the authoritative per-field-type set is returned by GET /v1/automation/meta/filter?field_type=….

OperatorArityOperatorArity
issinglehas_any_ofmultiple
is_notsinglehas_all_ofmultiple
containssinglehas_none_ofmultiple
does_not_containsingleis_exactlymultiple
starts_withsingleis_any_ofmultiple
ends_withsingleis_none_ofmultiple
greater_thansingleis_emptynone
greater_or_equal_thansingleis_not_emptynone
less_thansingleis_completednone
less_or_equal_thansingleis_incompletenone
is_beforesingle
is_aftersingle

The value union

The condition value is tagged by type so you can read it without the per-field rules:

typePayloadMeaning
texttext: a template dynamic valueText / template comparison.
numbernumber: a code dynamic valueNumeric comparison as a code expression.
idsids: array of number or referenceOption / status / relation / user ID set.
datedate: { base, operator: "plus"\|"minus", unit: "hour"\|"day"\|"week"\|"month", amount }Relative date. base is required — omitting it currently makes the condition silently never-match (intended to become a 400).
booleanbooleanBoolean comparison — arises from a webhook-payload-property subject. Round-trips on write.
entity_typeentity_type: stringComment/reply entity-type comparison. Round-trips on write.
On write, the type tag must match the field

Reading a value is tag-driven, but writing one is field-driven. On create/update the server picks the value channel from the subject field's type — number / unique-id → number; date fields → date; category / status / relation / user → ids; text → text — and reads only that tag. A value whose type doesn't match the field's channel is silently dropped: the condition is stored with an empty comparison and no 400. The valid tag for a field type is not served by meta/filter (which returns operators only), so match the tag to the field yourself.

An empty comparison is a valid definition, so validate stays green — but at run time a filter value that composes to empty is refused with a protective error rather than matching every record. See Troubleshooting → Filter values.

v1 limitations

  • A subject must have a boolean-expression leaf. On create/update, a structured condition's subject may be a field reference, most record-metadata references (app_record_id, created_at, last_modified_at, created_by, last_modified_by), a trigger-output reference (e.g. webhook_payload_property) or a prior action's file collection — all of these are writable and round-trip. Value-only tokens that have no filter leaf (meta_type record_id / record_url / revision / all_comments, the raw webhook_payload) and global / custom references are rejected with a 400 on write.
  • Change-tracking conditions are read-only. A "has changed" style condition has no public operator, so it reads as an operator-less { id, subject } and cannot be re-submitted verbatim.

A verbatim GETPUT still fails only for an automation that carries a change-tracking condition — strip those first. Ordinary field / metadata / trigger / action-file subjects (including the boolean / entity_type value kinds) round-trip. This is a known v1 limitation, not a bug.