filter action evaluates a JSON Logic condition against the event context. If the condition returns true, the reaction chain continues. If it returns false, the chain stops and no further actions execute.
Filters are ordered by sort_order alongside other actions. Place filters before the actions you want to gate.
Filter action structure
Example: Filter by tier handle
Only continue if the achieved tier is “tier-2”:Example: Filter by product brand
Only continue if the transaction item’s brand is in a specific list:Multiple filters
Chain multiple filter actions with increasingsort_order values. All filters must pass for the chain to proceed:
Date-gating a campaign
Definition dates do not gate anything at runtime. An achievement definition’sstarts_at and ends_at, a benefit definition’s claim period, a credit definition’s expiry — these describe when a thing is presented or how long an issued instance lives. None of them is evaluated when a reaction creates an incentive or increments progress.
A dated campaign is only actually dated if the reaction says so. Add a filter action carrying the window, ahead of the action it protects.
Pick the right field
Gate on the transaction’stransacted_at, the time the sale happened.
Do not gate on created_at or on the reaction firing time. POS batches and resyncs deliver trade long after it occurred, so those values tell you when Omneo heard about the sale, not when the customer shopped. Gating on them lets pre-campaign trade earn the moment the campaign goes live.
Start bound
On atransaction.created or transaction.resent trigger, transacted_at is at the root of the context:
End bound
>= start, < the day after the last trading day — so the boundary needs no reasoning about seconds.
Both bounds, combined with the campaign’s other conditions
Fold the window into the sameand as your status exclusions, rather than adding a second filter action, so the whole gate is readable in one place:
achievement.increment.transaction at sort_order 2 only runs for trade inside the window.
Gating an item filter
achievement.increment.transaction and points.create take their own item_filter, evaluated once per transaction item. That is a different context, and the path is different: the item carries a trimmed transaction summary, so the field is transaction.transacted_at.
Gating relative to now
Where the boundary is relative rather than fixed — a rolling window, a cooling-off period — use the date operators instead of a literal.compare_dates compares one date against another plus an offset, and takes an optional timezone:
Timezones
A literal string comparison is lexicographic against the stored value, so the literal has to be written in the same zone the value is stored in. Read one realtransacted_at from the tenant before committing a boundary, and check it against a sale you can place in local time. An undetected offset moves the boundary by hours in whichever direction hurts.
For anything where the offset matters, prefer compare_dates with an explicit timezone argument over a bare string comparison.
Cover every reaction in the campaign
A campaign is rarely one reaction. The earn reaction ontransaction.created is normally paired with a twin on transaction.sync or transaction.resent so that corrected or replayed trade is picked up. Those twins need the identical window. A gate on the primary alone leaves the resync free to backfill the entire pre-campaign period in one run — and because achievement progress only goes up, that is not self-correcting.
Before activating a dated campaign, list every reaction referencing its definition handle and confirm each one carries the window.
Data available in filters
Filter conditions have access to the full event context for the triggering event. The available variables depend on what fired the reaction:- Profile fields: accessible at root level:
first_name,tier_handle,flattened_tags,reward_balance, etc. - Aggregations: accessible as
aggregations.spend_12m,aggregations.shop_count, etc. - Tier definition:
definition.handle,definition.name - Transaction data:
total,items,location_id, etc.