Skip to main content

Event Entity

The Event entity is the entry point for workflow processing. It evaluates conditions against incoming messages and determines which path the workflow should take.

Purpose

Event entities serve as gatekeepers that:

  1. Filter messages based on conditions
  2. Extract and transform data via preScript / postScript
  3. Route to different branches based on logic

Configuration

Available Fields

FieldDescription
NameDisplay name (e.g., "Check User Signup")
DescriptionOptional documentation
ConditionJSON condition tree for message matching
Pre-ScriptOptional JavaScript before arguments and condition evaluation
Post-ScriptOptional JavaScript after prompt/model (if any), before branching
ArgumentsVariables injected after pre-script, before condition
tfConditionBranching mode: Single Path, True/False, Multi, Iterable
logicFieldVariable name/path for Multi/Iterable branching
Logic ValuesBranch values for Multi mode

Condition Builder

The condition builder creates a JSON tree structure:

{
"type": "AND",
"conditions": [
{
"field": "type",
"operator": "equals",
"value": "user.signup"
},
{
"type": "OR",
"conditions": [
{
"field": "source",
"operator": "equals",
"value": "web"
},
{
"field": "source",
"operator": "equals",
"value": "mobile"
}
]
}
]
}

Supported Operators:

OperatorDescriptionExample
equalsExact matchfield === value
not_equalsNot equalfield !== value
containsString containsfield.includes(value)
not_containsString doesn't contain!field.includes(value)
starts_withString starts withfield.startsWith(value)
ends_withString ends withfield.endsWith(value)
greater_thanNumeric greaterfield > value
less_thanNumeric lessfield < value
is_nullIs null/undefinedfield == null
is_not_nullIs not nullfield != null

Branching Modes

Single Path (Default)

The simplest mode — condition passes or workflow exits:

Event: Check User Type
Condition: type === 'signup'

├── Condition PASSES → Continue to children
└── Condition FAILS → Exit workflow (message ignored)

Configuration:

{
"tfCondition": "Single Path"
}

True/False Mode

Branch based on condition result:

Event: Is Premium User?
Condition: user.premium === true

├── true → Premium welcome flow
└── false → Basic welcome flow

Configuration:

{
"tfCondition": "True/False"
}

Canvas Representation:

The Event node displays two output ports:

  • true port → connects to premium branch
  • false port → connects to basic branch

Multi Mode

Branch based on a variable's value:

Event: Route by Role
Logic Field: userRole

preScript or postScript sets: var userRole = user.role;

├── userRole === "admin" → Admin flow
├── userRole === "user" → User flow
├── userRole === "guest" → Guest flow
└── * (wildcard) → Default/catch-all flow

Configuration:

{
"tfCondition": "Multi",
"logicField": "userRole",
"logic": [
{ "name": "Admin", "value": "admin" },
{ "name": "User", "value": "user" },
{ "name": "Guest", "value": "guest" },
{ "name": "Other", "value": "*" }
]
}

Variable requirement:

The logicField variable must be set before branching — typically in preScript (before the condition) or postScript (after the condition and any model call):

// Extract role from incoming fields
var userRole = user.role || 'guest';

// Or compute it
if (user.admin) {
var userRole = 'admin';
} else if (user.id) {
var userRole = 'user';
} else {
var userRole = 'guest';
}

Wildcard Branch (*)

Use * as a branch value to create a catch-all that matches any value not matched by other branches:

Event: Route by Event Type
Logic Field: event.event_type

├── "touchdown" → Touchdown handler
├── "field_goal" → Field goal handler
├── "interception"→ Turnover handler
└── "*" → Default handler (catches all other event types)

How it works:

  1. Exact matches are evaluated first
  2. If no exact match, the * wildcard branch is used
  3. If no wildcard and no match, workflow exits
tip

Use the wildcard branch to ensure your workflow always has a path forward, even for unexpected values. This is especially useful for logging or error handling of unknown cases.

Iterable Mode

Process each item in an array separately. The workflow executes once for each element in the specified array field.

Event: Process Each Recipient
Logic Field: recipients

If recipients = [user1, user2, user3]:
→ Execute children for user1 (___iterableItem___ = user1)
→ Execute children for user2 (___iterableItem___ = user2)
→ Execute children for user3 (___iterableItem___ = user3)

Configuration:

{
"tfCondition": "Iterable",
"logicField": "recipients"
}

Use Cases:

  • Batch notifications (send to multiple recipients)
  • Processing order items (multiple products)
  • Multi-target publishing (post to multiple accounts)
  • Parallel-style iteration over data arrays

Accessing Current Item:

In downstream scripts (Prompts, Actions), access the current iteration item:

// The current item being processed
const currentItem = ___iterableItem___;

// Example: if iterating over recipients array
const recipientEmail = currentItem.email;
const recipientName = currentItem.name;

print('Processing:', recipientName, recipientEmail);

Example Input:

{
"type": "notification",
"content": "Your order has shipped",
"recipients": [
{ "name": "Alice", "email": "alice@example.com" },
{ "name": "Bob", "email": "bob@example.com" }
]
}

With logicField: "recipients", the workflow executes twice - once for Alice, once for Bob.

info

If the logicField points to a non-array value, the workflow executes normally (single execution) with ___iterableItem___ set to that value.

Script Execution

Pre-Script runs before arguments injection and condition evaluation. Use it to prepare incoming fields or other state used by the condition.

Post-Script runs after the condition (and after prompt/model work if configured) and before context capture and branching. In postScript, ___result___ is already set to the condition result:

// In postScript: condition has already been evaluated
// ___result___ contains the condition result (true/false)

print('Condition result:', ___result___);

// Access the incoming message (top-level fields)
const eventType = type;
const userId = user.id;

// Use environment variables
const debugMode = DEBUG_MODE;

// Transform data for downstream entities
var processedData = {
userId: userId,
timestamp: new Date().toISOString(),
source: source
};

// For Multi mode, set the logic field
var userRole = user.role;

Available in Script Context

VariableDescription
(top-level fields)Incoming message properties (type, organizationId, payload, user, …) are available directly in script scope
___result___Condition evaluation result (boolean)
__variables__Array of environment variables
__variablesByName__Map of variable name → value
{VARIABLE_NAME}Each variable as top-level var
ArgumentsEach argument as top-level var

Execution Flow

Events use UnifiedEntityEvaluator — the same seven-step pipeline as every entity type. Branching (Single Path, True/False, Multi, Iterable) happens last, after postScript and context capture.

┌─────────────────────────────────────────────────────────────┐
│ UnifiedEntityEvaluator (Event node) │
├─────────────────────────────────────────────────────────────┤
│ 1. Inject message │
│ 2. Run preScript (if any) │
│ 3. Inject arguments (if any) │
│ 4. Evaluate condition → set ___result___ (skip if no tree) │
│ 5. Prompt + model (if configured on this entity) │
│ 6. Run postScript (if any) │
│ 7. Capture context │
│ 8. Branching: Single Path / True-False / Multi / Iterable │
└─────────────────────────────────────────────────────────────┘

Examples

Example 1: Simple Event Filter

Filter for signup events only:

{
"name": "Signup Events Only",
"condition": {
"field": "type",
"operator": "equals",
"value": "user.signup"
},
"tfCondition": "Single Path"
}

Example 2: Premium vs Basic Routing

Route based on subscription status:

{
"name": "Check Premium Status",
"condition": {
"field": "user.subscription",
"operator": "equals",
"value": "premium"
},
"tfCondition": "True/False"
}

Example 3: Multi-Region Routing

Route based on user region:

{
"name": "Route by Region",
"condition": {
"field": "type",
"operator": "equals",
"value": "order.created"
},
"preScript": "var region = shipping.country === 'US' ? 'us' : shipping.country === 'CA' ? 'ca' : 'intl';",
"tfCondition": "Multi",
"logicField": "region",
"logic": [
{ "name": "US Shipping", "value": "us" },
{ "name": "Canada Shipping", "value": "ca" },
{ "name": "International", "value": "intl" }
]
}

Example 4: Complex Condition with Script

Validate and transform before processing:

{
"name": "Validate Order",
"condition": {
"type": "AND",
"conditions": [
{ "field": "type", "operator": "equals", "value": "order" },
{ "field": "total", "operator": "greater_than", "value": 0 }
]
},
"postScript": "var orderData = { id: orderId, total: total, currency: currency || 'USD', validated: true };",
"tfCondition": "Single Path",
"arguments": [
{
"argumentName": "minOrderValue",
"argumentValue": "10",
"argumentDescription": "Minimum order value to process"
}
]
}

Example 5: Iterable Array Processing

Process each recipient in a notification:

{
"name": "Notify Each Recipient",
"condition": {
"type": "group",
"operator": "AND",
"conditions": [
{ "type": "rule", "field": "type", "comparison": "equals", "value": "notification.broadcast" }
]
},
"tfCondition": "Iterable",
"logicField": "recipients"
}

With this message input:

{
"type": "notification.broadcast",
"content": "Server maintenance at 3 AM",
"recipients": [
{ "name": "Alice", "email": "alice@example.com", "sms": "+1234567890" },
{ "name": "Bob", "email": "bob@example.com", "sms": "+0987654321" }
]
}

The workflow executes twice. In downstream Actions/Prompts:

// Access current recipient
const recipient = ___iterableItem___;
print('Sending to:', recipient.name, recipient.email);

Example 6: Multi-Branch with Wildcard Catch-All

Route sports events with a default handler:

{
"name": "Sports Event Router",
"condition": {
"type": "group",
"operator": "AND",
"conditions": [
{ "type": "rule", "field": "payload.metadata.league", "comparison": "equals", "value": "nfl" }
]
},
"tfCondition": "Multi",
"logicField": "playType",
"preScript": "var playType = payload.event?.play_type || 'unknown';",
"logic": [
{ "name": "Touchdown", "value": "touchdown" },
{ "name": "Field Goal", "value": "field_goal" },
{ "name": "Interception", "value": "interception" },
{ "name": "Other Plays", "value": "*" }
]
}

Best Practices

Condition Design

  1. Start simple — Add complexity only when needed
  2. Use AND for required conditions — All must pass
  3. Use OR for alternatives — Any can pass
  4. Test with sample messages — Verify logic before deployment

Script Guidelines

  1. Set logic-field variables in pre or post — Use preScript if the branch variable should exist before the condition; postScript if it depends on ___result___ or model output
  2. Handle missing data — Use defaults for optional fields
  3. Log for debugging — Use print() during development
  4. Keep scripts focused — Transform data, don't add business logic

Branching Strategy

  1. Single Path — Use for simple filters
  2. True/False — Use for binary decisions
  3. Multi — Use for 3+ distinct paths (switch-like routing)
  4. Iterable — Use for array processing (loop over items)