Expression syntax
Write the expression of a condition in a lean syntax, inspired by spreadsheet formulas. The expression always results in true or false and combines at least one variable, one operation, and one value.
$merchant_id == "XYZ"
The expression above is true when the event’s merchant_id field equals "XYZ".
The three prefixes
Every variable starts with a prefix that indicates where the value comes from and how long it lasts:
| Prefix | Holds | Duration |
|---|---|---|
$field | a field of the event | persisted with the event |
@list | a list of values in the account | maintained by the account, across events |
%temporary | a value computed during this run | only exists while the rule runs |
Event fields ($)
The variables are exactly the fields of the event type the rule belongs to. There is no fixed list. If the event type has an amount field, the rule can use $amount; a different event type will have a different set of variables.
As you write the condition, the panel offers the available fields and highlights the expression. Fields starting with _ are reserved by the platform and are not available for use in expressions.
Beyond the event type’s fields, $id contains the event identifier supplied in POST /events. The $id variable applies in conditions and as the dimension of an analysis.
The field type determines how the value is compared:
| Field type | Comparison example |
|---|---|
| Text | $status == "approved" |
| Integer | $installments > 6 |
| Decimal | $amount >= 1500.00 |
| Boolean | $is_first_purchase == true |
| Date and time | $signup_at > "30 days ago" |
A field that did not come in the event is treated as empty. To test that absence, compare it with null, without quotes:
$device_fingerprint_id == null
The expression above is true when the event carried no device fingerprint. Use != for the opposite case.
Operations
| Operation | Meaning |
|---|---|
== | Equality |
!= | Inequality |
> | Greater than |
>= | Greater than or equal |
< | Less than |
<= | Less than or equal |
in | Belongs to a collection |
not in | Does not belong to a collection |
any in | At least one of the values belongs to a collection |
all in | All values belong to a collection |
=~ | Matches a regular expression |
!~ | Does not match a regular expression |
Values
The value compared with a variable can be text, a number, or a boolean:
- Text: always between double quotes, such as
"XYZ". Single quotes and unquoted text are not accepted. - Number: without quotes, using a dot as the decimal separator, such as
1000or99.01. - Boolean:
trueorfalse, without quotes.
All text goes between double quotes. An IP, a date, or a word used in the syntax itself (for example, in) also needs the quotes to be treated as text.
Dates
Date and time fields accept relative and absolute values, always between quotes:
$signup_at > "30 days ago"
$signup_at == "2026-03-15"
The relative format is [number] [hour|day|week|month|year]s ago. The absolute format accepts a year ("2026"), a year and month ("2026-03"), or a full date ("2026-03-15"). With a year, or with a year and month, the comparison covers the whole period.
Combining expressions
Use and and or to combine expressions. and takes precedence over or: in a combination without parentheses, the platform evaluates the parts joined by and first. So A and B or C is equivalent to (A and B) or C.
Use parentheses to group expressions and change that precedence:
($amount > 1000 and $user_email_domain == "gmail.com") or $issuer_country != "BR"
A line break works as a space between the terms. Spread a long combination across several lines for readability, without changing the result:
($amount > 1000 and $user_email_domain == "gmail.com")
or $issuer_country != "BR"
true and false as an entire condition
An expression can consist of just true or false, with no variable or comparison. true is always true and keeps the condition always active. false is always false and disables the condition without removing it. This use differs from comparing a boolean field with true or false, described in Values. In that comparison, the literal is on the right of the operator; as an entire condition, the literal makes up the whole expression.
Collections (@)
The platform offers the account’s lists as collections, identified by the @ prefix. For example, to check whether a customer is on a quarantine list:
$user_id in @quarantined_customers
Write the collection directly in the expression too, between brackets and with the values separated by commas:
$issuer_country not in ["BR", "AR", "CL"]
Beyond the format above, in and not in accept:
- fixed text on the left, to test a known value against a collection:
"BR" in @served_countries; - a field on both sides, to test whether one field’s value is contained in another’s:
$user_id in $allowed_users.
Testing several values at once
A collection written between brackets on the left side requires a quantifier. The expression states how many of the values need to belong to the collection on the right:
| Operation | True when |
|---|---|
any in | at least one of the values belongs |
all in | all values belong |
not in | none of the values belongs |
["susp_59", "susp_83"] any in %ruler.triggered_cells
["BR", "AR"] all in @served_countries
["tor", "vpn"] not in @allowed_types
There is no form without a quantifier. Writing ["a", "b"] in @list is an authoring error. “A and B are on the list” is ambiguous between “at least one” and “both”, and a risk condition cannot depend on which of the two readings the author had in mind. In the negative there is no ambiguity, since “A and B are not on the list” can only mean that neither one is. That is why not in needs no quantifier, and for the same reason not any in and not all in are not accepted.
The collection on the right can be an account list, a set-valued temporary variable, a field, or another literal collection.
Regular expressions
The =~ and !~ operators compare a text with a regular expression. =~ is true when the pattern occurs in the text, and !~ when the pattern does not occur:
$merchant_id =~ "(?i)^m"
split($user_email, "@", -1) !~ "\.com\.br$"
The first expression is true when the merchant ID starts with m, uppercase or lowercase.
The pattern follows the RE2 syntax:
- Partial match: without anchors, the pattern can occur at any position in the text.
^and$anchor at the start and at the end of the whole text. - Uppercase and lowercase: the comparison is case-sensitive. The
(?i)prefix removes the distinction. - Line break:
.does not match a line break. - Missing features: lookahead, lookbehind and backreferences are not accepted. A pattern such as
a(?=b)is refused on save.
The pattern is fixed text between double quotes, with up to 256 characters. A field cannot take the place of the pattern. A double quote inside the pattern is written as \x22.
On the left of the operator, the expression takes a text or list field, a text temporary variable, a concat or a split. Numeric, boolean and date fields, as well as sets, are refused on save.
An absent value is treated as empty text. So $x !~ "foo" is true when the event does not contain the x field.
A text longer than 4 KB is not compared, and the condition is not met, with both =~ and !~. The rule’s other conditions proceed normally. An event field has at most 1,024 bytes, so only a concat or a temporary variable reaches that size.
Temporary variables (%)
A temporary variable holds a value computed while the rule runs. An action produces the temporary variable and the following conditions read the value. The platform does not write the temporary variable to the event, does not return it in the API response and does not carry it over to the next event. Write to a field any value that has to outlive the event.
Temporary variables come in two forms.
Simple values, set by a set temporary variable action:
%manual_review == "1"
Published results of a producing action, such as run analysis or enrich, under the result name, with keys accessed through a dot. Each action’s page lists its keys. The result of an analysis has the following keys:
| Key | Holds | Type |
|---|---|---|
score | score calculated by the analysis | Decimal |
score_cell | name of the cell the score came from | Text |
triggered | whether the analysis was triggered | Boolean |
triggered_count | how many cells were triggered | Integer |
triggered_cells | names of the triggered cells | Set |
version | version of the analysis that ran | Integer |
%bot.triggered_count >= 2
The triggered_cells key is a set, not a single value. Use triggered_cells only on the right of a membership operation, to test which cells were triggered.
"email_velocity" in %bot.triggered_cells
["susp_59", "susp_83"] any in %bot.triggered_cells
Comparing the set with ==, or using it in any other operation, is an authoring error and the condition is not accepted. In an action, the join function converts the set to text.
An action in expression mode can also hold a set in a simple temporary variable. The value stays a set and is tested in the same way.
Functions
concat
The concat function joins several values into a single piece of text, comparable with a value. Each argument is a field of the event ($field), a temporary variable (%temporary), or fixed text, separated by commas:
concat($user_id, "-", $merchant_id) == "customer-1234-store-1234"
concat($merchant_id, "-", %bot.score_cell) == "store-1234-email_velocity"
The first expression is true when the combination of the user ID and the merchant ID, joined by a hyphen, results in "customer-1234-store-1234".
Considerations about the joined values:
- A variable with no value is treated as empty text.
- A numeric value is converted to text in its original form (for example,
99.01and0.9). - A set, such as
%bot.triggered_cells, cannot go inside aconcat. The set is tested with a membership operation or converted to text withjoin.
The result of concat also accepts in and not in, to compare the combination against a list:
concat($user_id, "-", $merchant_id) in @blocked_pairs
split
The split function divides a text by a separator and returns one of the parts. The arguments are the value (an event field or a temporary variable), the separator between double quotes and the position of the part:
split($user_email, "@", -1) == "gmail.com"
The expression above is true when the last part of the email after @, the domain, is gmail.com.
Considerations about the split:
- The position starts at
0. A negative position counts from the end, and-1is the last part. - The separator cannot be empty and can have more than one character.
- A value that is not text is converted to text before the split. A set is not accepted.
- Empty parts count. Split by
"@", the text"ana@"has""at position-1. - A position out of range results in empty text in a condition. In an action, nothing is written.
The result of split is text and takes the same operations as text, including in, not in and regular expressions. A split does not go inside a concat, and a concat does not go inside a split.
join
The join function converts a set to text, with the elements separated by the given separator:
join(%bot.triggered_cells, ",")
The first argument is a set-valued temporary variable, such as %bot.triggered_cells. The second is the separator, between double quotes.
The join function is valid only as the entire value of an action in expression mode. A condition does not use join, and a join does not go inside a concat. An empty set writes nothing.
Authoring errors
The platform validates the expression the moment you save the condition. The panel points out the problem and the rule is not published while there is an error. The most common cases:
- unknown field: the variable does not match any field of the event type bound to the rule;
- incompatible type: the compared value does not make sense for the field type, such as comparing a numeric field with
"approved"; - unknown temporary variable: the name or the attribute is not produced by any action of the rule;
- temporary variable read too early: no earlier condition produces it. Since conditions are evaluated in order, the producer must come before the reader;
- unquoted text: text values need double quotes.
Each expression has a size limit. See limits and parameters.