Workflow Nodes

Nodes are the building blocks of workflows. Each node performs a specific function and can be connected to other nodes to create automation sequences.

Node Anatomy

Every node has:

┌─────────────────────────────────────┐
│  ○ Input Handle(s)                  │
├─────────────────────────────────────┤
│  [Icon]  Node Name                  │
│  Description text...                │
│  ⚙️ Configured / ⚠️ Not configured │
├─────────────────────────────────────┤
│                    Output Handle(s) ○│
└─────────────────────────────────────┘
  • Input Handles: Where data flows in (left side)
  • Output Handles: Where data flows out (right side)
  • Configuration: Node-specific settings
  • Category Badge: Visual indicator of node type

Node Categories

Triggers (Green)

Triggers start workflow execution. Every workflow needs at least one trigger.

Characteristics:

  • No input handles (they start the flow)
  • One or more output handles
  • Provide initial data to the workflow
  • Marked as is_entry_point in database

Actions (Blue)

Actions perform operations - send emails, make API calls, update records.

Characteristics:

  • One input handle
  • One output handle
  • May have side effects (sending emails, HTTP requests)
  • Can be async (delays, waiting for responses)

Conditions (Yellow)

Conditions branch the workflow based on logic.

Characteristics:

  • One input handle
  • Multiple output handles (one per branch)
  • Evaluate input data against rules
  • Output includes _branch indicator

Transformers (Purple)

Transformers modify data without side effects.

Characteristics:

  • One input handle
  • One output handle
  • Pure functions (same input = same output)
  • Used for data mapping, filtering, formatting

Built-in Nodes Reference

Manual Trigger

Identifier: workflows:manual-trigger Category: Trigger

Starts workflow when user clicks Run or calls the execute API.

Configuration:

Field Type Description
sampleData object Sample data for testing

Output:

{
  "trigger_type": "manual",
  "triggered_at": "2025-01-16T10:00:00Z",
  "triggered_by": "user-uuid",
  ...triggerData
}

Schedule Trigger

Identifier: workflows:schedule-trigger Category: Trigger

Runs workflow on a cron schedule.

Configuration:

Field Type Description Example
cron string Cron expression 0 9 * * * (daily at 9am)
timezone string Timezone for schedule Asia/Kolkata

Common Cron Patterns:

* * * * *      Every minute
0 * * * *      Every hour
0 9 * * *      Daily at 9:00 AM
0 9 * * 1      Every Monday at 9:00 AM
0 0 1 * *      First day of month at midnight
*/15 * * * *   Every 15 minutes

Output:

{
  "trigger_type": "schedule",
  "scheduled_time": "2025-01-16T09:00:00Z",
  "cron": "0 9 * * *"
}

Webhook Trigger

Identifier: workflows:webhook-trigger Category: Trigger

Receives HTTP POST requests to trigger the workflow.

Configuration:

Field Type Description
requireSignature boolean Validate webhook signature
signatureHeader string Header containing signature
signatureSecret string Secret for HMAC validation

Webhook URL:

POST https://your-domain.com/api/webhooks/workflows/{workflow-token}

Output:

{
  "trigger_type": "webhook",
  "method": "POST",
  "headers": { ... },
  "query": { ... },
  "body": { ...posted_data }
}

Send Email

Identifier: workflows:send-email Category: Action

Sends an email using the configured mail provider.

Configuration:

Field Type Required Description
to string Yes Recipient email (supports expressions)
subject string Yes Email subject
body string Yes Email body content
bodyType enum No text or html (default: text)

Expression Example:

to: "{{customer.email}}"
subject: "Order {{order.id}} Confirmation"
body: |
  Hi {{customer.name}},

  Your order #{{order.id}} has been confirmed.
  Total: ${{order.total}}

  Thank you!

Output:

{
  ...input,
  "_email_sent": true,
  "_email_to": "john@example.com",
  "_email_subject": "Order 123 Confirmation",
  "_sent_at": "2025-01-16T10:30:00Z"
}

HTTP Request

Identifier: workflows:http-request Category: Action

Makes HTTP requests to external APIs.

Configuration:

Field Type Required Description
method enum Yes GET, POST, PUT, PATCH, DELETE
url string Yes Request URL (supports expressions)
headers object No Request headers
body string No Request body (for POST/PUT/PATCH)
bodyType enum No json, form, raw
timeout number No Timeout in seconds (default: 30)
retryOnError boolean No Retry on HTTP errors

Output:

{
  ...input,
  "_http_status": 200,
  "_http_response": { ...response_body },
  "_http_headers": { ...response_headers },
  "_http_duration_ms": 234
}

Delay

Identifier: workflows:delay Category: Action

Pauses workflow execution for a specified duration.

Configuration:

Field Type Required Description
duration number Yes Delay amount
unit enum Yes seconds, minutes, hours, days

Example:

duration: 5
unit: minutes

Output:

{
  ...input,
  "_delayed": true,
  "_delay_duration": 300,
  "_delay_started_at": "2025-01-16T10:00:00Z",
  "_delay_ended_at": "2025-01-16T10:05:00Z"
}

Set Variable

Identifier: workflows:set-variable Category: Action

Stores a value in the workflow execution context.

Configuration:

Field Type Required Description
name string Yes Variable name
value any Yes Value to store (supports expressions)

Example:

name: "discount_percentage"
value: "{{order.total > 100 ? 10 : 0}}"

Output:

{
  ...input,
  "_variable_set": "discount_percentage",
  "_variable_value": 10
}

Variables are accessible in subsequent nodes via {{variables.discount_percentage}}.


If/Else

Identifier: workflows:if-else Category: Condition

Binary branching based on a condition.

Configuration:

Field Type Required Description
condition string Yes Expression that evaluates to boolean
operator enum No Comparison operator
value any No Value to compare against

Output Handles:

  • true - When condition is true
  • false - When condition is false

Example Conditions:

// Simple comparison
{{order.total}} > 100

// String matching
{{customer.type}} == "premium"

// Array check
{{items.length}} > 0

// Nested property
{{order.shipping.method}} == "express"

Output:

{
  ...input,
  "_branch": "true",  // or "false"
  "_condition_result": true
}

Switch

Identifier: workflows:switch Category: Condition

Multi-way branching based on value matching.

Configuration:

Field Type Required Description
value string Yes Expression to evaluate
cases array Yes List of case values
defaultCase string No Default output if no match

Example:

value: "{{order.status}}"
cases:
  - "pending"
  - "processing"
  - "shipped"
  - "delivered"
defaultCase: "other"

Output Handles: One handle per case plus optional default.

Output:

{
  ...input,
  "_branch": "processing",
  "_switch_value": "processing"
}

Map

Identifier: workflows:map Category: Transformer

Transforms the structure of input data.

Configuration:

Field Type Required Description
mapping object Yes Key-value mapping of output fields
keepOriginal boolean No Keep unmapped fields

Example:

mapping:
  email: "{{customer.email}}"
  name: "{{customer.first_name}} {{customer.last_name}}"
  orderTotal: "{{order.total}}"
keepOriginal: false

Input:

{
  "customer": {
    "email": "john@example.com",
    "first_name": "John",
    "last_name": "Doe"
  },
  "order": { "total": 99.99 }
}

Output:

{
  "email": "john@example.com",
  "name": "John Doe",
  "orderTotal": 99.99
}

Filter

Identifier: workflows:filter Category: Transformer

Filters items in an array based on conditions.

Configuration:

Field Type Required Description
arrayPath string Yes Path to array in input
condition string Yes Filter condition per item
outputPath string No Where to put filtered array

Example:

arrayPath: "items"
condition: "{{item.price}} > 50"
outputPath: "expensive_items"

Input:

{
  "items": [
    { "name": "A", "price": 30 },
    { "name": "B", "price": 75 },
    { "name": "C", "price": 120 }
  ]
}

Output:

{
  "items": [...],
  "expensive_items": [
    { "name": "B", "price": 75 },
    { "name": "C", "price": 120 }
  ],
  "_filtered_count": 2,
  "_original_count": 3
}

Creating Custom Nodes

See Extending Workflows for a complete guide to creating custom nodes.

Quick Example

<?php

namespace Modules\MyModule\Nodes\Actions;

use Modules\Workflows\Nodes\BaseNode;

class MyCustomNode extends BaseNode
{
    public static function getIdentifier(): string
    {
        return 'my-module:custom-action';
    }

    public static function getName(): string
    {
        return 'My Custom Action';
    }

    public static function getCategory(): string
    {
        return 'action';
    }

    public static function getConfigSchema(): array
    {
        return [
            'type' => 'object',
            'properties' => [
                'myField' => [
                    'type' => 'string',
                    'title' => 'My Field',
                ],
            ],
            'required' => ['myField'],
        ];
    }

    public function execute(array $input, array $config, array $context): array
    {
        // Your logic here
        return array_merge($input, [
            '_custom_result' => 'done',
        ]);
    }
}

Node Connection Rules

Valid Connections

Source Category Can Connect To
Trigger Action, Condition, Transformer
Action Action, Condition, Transformer
Condition Action, Condition, Transformer
Transformer Action, Condition, Transformer

Invalid Connections

  • Triggers cannot be targets - Nothing can connect TO a trigger
  • Circular connections - A node cannot eventually connect back to itself
  • Type mismatches - Some nodes require specific input types

Best Practices

Node Configuration

  1. Use expressions - Make configs dynamic with {{path.to.value}}
  2. Validate early - Check required data at the start of workflow
  3. Handle errors - Configure retry settings for unreliable operations
  4. Log important data - Use Set Variable to track state

Workflow Design

  1. Start with triggers - Always have a clear entry point
  2. Validate data - Use conditions to handle edge cases
  3. Keep it simple - Break complex workflows into smaller ones
  4. Document - Use node labels to explain purpose

Next Steps