---
"@context": https://schema.org
"@type": TechArticle
"@id": https://www.twilio.com/docs/cohorts/cel/operators-and-precedence#article
headline: Operators and precedence
description: Reference for the operators supported in Twilio Cohorts filter criteria and the order in which the system evaluates them.
url: https://www.twilio.com/docs/cohorts/cel/operators-and-precedence
inLanguage: en
dateModified: 2026-09-15T09:58:28.000Z
author:
  "@type": Organization
  name: Twilio Developer Education Team
publisher:
  "@type": Organization
  name: Twilio
---

# Operators and precedence

## Supported operators

| Operator             | Description                                                              | Example                                                                                                                                 |
| -------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `!`                  | Logical NOT                                                              | `!profile.trait.Account.isTest`                                                                                                         |
| `-`                  | Unary negation                                                           | `-1`                                                                                                                                    |
| `*`, `/`, `%`        | Multiplication, division, remainder                                      | `profile.trait.Account.lifetimeValue * 100`                                                                                             |
| `+`, `-`             | Addition, subtraction                                                    | `profile.trait.Account.lifetimeValue + 100`                                                                                             |
| `==`, `!=`           | Equal to, not equal to                                                   | `profile.trait.Account.status == "active"`                                                                                              |
| `<`, `<=`, `>`, `>=` | Less than, less than or equal to, greater than, greater than or equal to | `profile.trait.Account.age >= 18`                                                                                                       |
| `in`                 | Membership                                                               | `"US" in ["US", "CA"]`                                                                                                                  |
| `&&`                 | Logical AND                                                              | `profile.trait.Account.isActive && profile.trait.Account.country == "US"`                                                               |
| `\|\|`               | Logical OR                                                               | `profile.trait.Account.isAdmin \|\| profile.trait.Account.isOwner`                                                                      |
| `?:`                 | Conditional (ternary): `condition ? value_if_true : value_if_false`      | `profile.trait.Account.plan == "enterprise" ? profile.trait.Account.lifetimeValue > 10000 : profile.trait.Account.lifetimeValue > 1000` |
| `.`, `[]`            | Field access, method calls, and list indexing                            | `profile.trait.Account.status`, `str.contains("x")`, `list[0]`                                                                          |

## Precedence

When your filter criteria combine more than one operator, Cohorts evaluates the operators that bind tightest first, then works outward. The table below lists operator groups from the tightest binding (evaluated first) to the loosest binding (evaluated last).

| Order        | Operators                              | Notes                                                                                                      |
| ------------ | -------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| 1 (tightest) | `.`, `[]`                              | Field access, method calls, and indexing                                                                   |
| 2            | `!`, unary `-`                         | Logical NOT, unary negation                                                                                |
| 3            | `*`, `/`, `%`                          | Multiplication, division, remainder                                                                        |
| 4            | `+`, `-`                               | Addition, subtraction                                                                                      |
| 5            | `==`, `!=`, `<`, `<=`, `>`, `>=`, `in` | Comparison and membership. All seven operators share one precedence level and evaluate from left to right. |
| 6            | `&&`                                   | Logical AND                                                                                                |
| 7            | `\|\|`                                 | Logical OR                                                                                                 |
| 8 (loosest)  | `?:`                                   | Conditional                                                                                                |

Key points to remember:

* **Comparison and membership share one tier.** Expressions such as `a == b in c` and `a < b == c` evaluate left to right; none of the comparison or membership operators binds tighter than the others.
* **Field access binds tighter than everything else,** including unary `!`. In `!profile.trait.Account.isTest`, the field access resolves first, and then `!` negates the result.

Use parentheses whenever the intended grouping is not obvious. Parentheses remove ambiguity for anyone who reads the criteria.

## Worked example

```txt
!profile.trait.Account.isTest && (profile.trait.Account.age >= 18 || profile.trait.Account.hasGuardianConsent) && profile.trait.Account.country in ["US", "CA"]
```

Evaluated from the tightest to the loosest binding:

1. `profile.trait.Account.isTest`, `profile.trait.Account.age`, `profile.trait.Account.hasGuardianConsent`, and `profile.trait.Account.country` are field accesses, so they resolve first.
2. `!profile.trait.Account.isTest` negates the value of `isTest`.
3. The parentheses force `profile.trait.Account.age >= 18 || profile.trait.Account.hasGuardianConsent` to evaluate as a group, even though `||` would otherwise bind more loosely than the surrounding `&&`.
4. `profile.trait.Account.country in ["US", "CA"]` evaluates the membership check.
5. The two `&&` operators combine all three results, left to right.

These criteria match profiles that are not test accounts, are either at least 18 years old or have guardian consent, and are located in the United States or Canada.

## Next steps

* Browse the available [functions](/docs/cohorts/cel/functions).
* See [example criteria](/docs/cohorts/cel/common-filtering-patterns) for common filtering patterns.
