Conditions

Conditions determine when a trigger rule should be applied. They allow you to create complex logic for evaluating event data and determining whether actions should be executed.

Condition Structure

A condition can be either a simple field comparison or a logical group of other conditions:
type Condition = SimpleCondition | GroupCondition;

interface SimpleCondition {
  field: string;        // The field to evaluate (dot notation supported)
  operator: string;     // The comparison operator
  value?: any;          // The value to compare against (optional for some operators)
}

interface GroupCondition {
  operator: 'AND' | 'OR'; // Logical operator
  conditions: Condition[]; // List of conditions to combine
}

Simple Conditions

Simple conditions compare a field in the event data to a value using an operator:
{
  "field": "user.email",
  "operator": "contains",
  "value": "@example.com"
}

Field Path Notation

Fields are specified using dot notation to access nested properties:
user.address.country
payment.amount
document.metadata.tags[0]

Available Operators

The following operators are available for simple conditions:
OperatorDescriptionExample
equalsExact equality{"field": "user.role", "operator": "equals", "value": "admin"}
notEqualsInequality{"field": "user.status", "operator": "notEquals", "value": "inactive"}
containsString contains{"field": "user.email", "operator": "contains", "value": "@gmail.com"}
notContainsString does not contain{"field": "user.email", "operator": "notContains", "value": "test"}
startsWithString starts with{"field": "document.title", "operator": "startsWith", "value": "Draft:"}
endsWithString ends with{"field": "file.name", "operator": "endsWith", "value": ".pdf"}
greaterThanNumeric greater than{"field": "payment.amount", "operator": "greaterThan", "value": 100}
lessThanNumeric less than{"field": "payment.amount", "operator": "lessThan", "value": 50}
greaterThanOrEqualNumeric greater than or equal{"field": "user.age", "operator": "greaterThanOrEqual", "value": 18}
lessThanOrEqualNumeric less than or equal{"field": "product.stock", "operator": "lessThanOrEqual", "value": 5}
inValue in array{"field": "user.role", "operator": "in", "value": ["admin", "editor"]}
notInValue not in array{"field": "user.country", "operator": "notIn", "value": ["US", "CA"]}
existsField exists{"field": "user.phoneNumber", "operator": "exists"}
notExistsField does not exist{"field": "user.deletedAt", "operator": "notExists"}
isTrueBoolean is true{"field": "user.emailVerified", "operator": "isTrue"}
isFalseBoolean is false{"field": "user.suspended", "operator": "isFalse"}
matchesRegex match{"field": "user.email", "operator": "matches", "value": "^[a-z0-9]+@[a-z]+\\.[a-z]{2,3}$"}

Group Conditions

Group conditions combine multiple conditions using logical operators:
{
  "operator": "AND",
  "conditions": [
    {
      "field": "user.plan",
      "operator": "equals",
      "value": "premium"
    },
    {
      "field": "user.trialEnded",
      "operator": "isTrue"
    }
  ]
}

Logical Operators

Two logical operators are available:
  • AND: All conditions must be true
  • OR: At least one condition must be true

Nesting Conditions

Conditions can be nested to create complex logic:
{
  "operator": "OR",
  "conditions": [
    {
      "operator": "AND",
      "conditions": [
        {
          "field": "user.plan",
          "operator": "equals",
          "value": "premium"
        },
        {
          "field": "user.country",
          "operator": "equals",
          "value": "US"
        }
      ]
    },
    {
      "operator": "AND",
      "conditions": [
        {
          "field": "user.plan",
          "operator": "equals",
          "value": "enterprise"
        },
        {
          "field": "user.employeeCount",
          "operator": "greaterThan",
          "value": 100
        }
      ]
    }
  ]
}

Dynamic Values

You can use template strings in condition values to reference other fields in the event data:
{
  "field": "user.email",
  "operator": "equals",
  "value": "{{user.username}}@example.com"
}

Custom Operators

You can extend the rule engine with custom operators by registering them with the rule evaluator:
import { registerOperator } from "@repo/trigger-rules";

// Register a custom operator
registerOperator("containsAny", (fieldValue, conditionValue) => {
  if (!Array.isArray(conditionValue)) {
    return false;
  }
  
  return conditionValue.some(value => 
    String(fieldValue).includes(String(value))
  );
});

// Now you can use it in your rules
const rule = {
  // ...
  "condition": {
    "field": "user.email",
    "operator": "containsAny",
    "value": ["@gmail.com", "@hotmail.com", "@yahoo.com"]
  }
  // ...
};

Best Practices

  1. Start simple - Begin with simple conditions and add complexity as needed
  2. Use meaningful field paths - Choose clear, descriptive paths for your fields
  3. Avoid deep nesting - Too many nested conditions can be hard to understand
  4. Consider performance - Complex conditions may impact evaluation performance
  5. Test thoroughly - Validate your conditions with different inputs

Examples

User in Specific Region with Premium Plan

{
  "operator": "AND",
  "conditions": [
    {
      "field": "user.region",
      "operator": "in",
      "value": ["NA", "EU"]
    },
    {
      "field": "user.plan",
      "operator": "equals",
      "value": "premium"
    }
  ]
}

High-Value Transaction or VIP Customer

{
  "operator": "OR",
  "conditions": [
    {
      "field": "transaction.amount",
      "operator": "greaterThan",
      "value": 1000
    },
    {
      "field": "user.isVIP",
      "operator": "isTrue"
    }
  ]
}

Email from Company Domain with Specific Subject

{
  "operator": "AND",
  "conditions": [
    {
      "field": "email.from",
      "operator": "endsWith",
      "value": "@ourcompany.com"
    },
    {
      "field": "email.subject",
      "operator": "contains",
      "value": "urgent"
    },
    {
      "field": "email.read",
      "operator": "isFalse"
    }
  ]
}

See Also