Skip to Content
WorkflowsExecution Model

Execution Model

Understanding how the workflow engine executes your automations helps you build reliable workflows and debug issues when they arise.

Execution Lifecycle

Every workflow execution moves through these states:

pending --> running --> completed --> failed --> cancelled --> timed_out --> suspended (waiting for event/condition) --> running
StateDescription
pendingExecution is queued and waiting for a worker to pick it up
runningNodes are actively executing
completedAll nodes finished successfully
failedA node failed and no error handler caught it
cancelledThe execution was manually cancelled via API or UI
timed_outThe execution exceeded the 30-minute maximum
suspendedThe execution is paused, waiting for an external event or time condition

Node Execution Order

Nodes execute based on the DAG (directed acyclic graph) structure:

  1. The trigger fires and creates an execution in pending state
  2. The engine picks up the execution and sets it to running
  3. Starting from the trigger, each connected node executes in topological order
  4. When a node completes, its output is stored and the next connected nodes are queued
  5. For branching nodes (Condition, Switch), only the matching branch executes
  6. For Fork nodes, all branches execute concurrently
  7. Merge nodes wait for all incoming branches before continuing
  8. Execution completes when all reachable nodes have finished

BullMQ Job Scheduling

The workflow engine uses BullMQ (a Redis-backed job queue) for reliable job scheduling:

  • Each workflow execution is a BullMQ job
  • Jobs are processed by worker instances with configurable concurrency
  • Failed jobs are retried according to the workflow’s error handling configuration
  • Scheduled triggers (CRON) create jobs at the specified times
  • Delayed nodes (Delay, Wait-for-Event) use BullMQ’s delayed job feature

Suspension and Resumption

Some nodes pause execution until an external condition is met:

Wait-for-Event Node

Suspends the execution until a specific platform event occurs or a timeout is reached.

Modes:

  • Time — Resume after a fixed delay (e.g., 30 minutes)
  • Event — Resume when a specific event fires (e.g., inbox.message.received for the same conversation)
  • Condition — Resume when a data condition becomes true (polled periodically)

When suspended:

  1. The execution state changes to suspended
  2. The engine saves the current execution context to the database
  3. A listener is registered for the target event (or a timer is set)
  4. When the condition is met, the execution resumes from the suspended node
  5. All previously computed variables are restored

Delay Node

Pauses execution for a specified duration. Internally uses BullMQ delayed jobs.

Configuration: Duration in seconds, minutes, hours, or days.

Concurrency Limits

LimitValueScope
Max execution time30 minutesPer execution
Concurrent executions50Per workspace
Concurrent executions10Per workflow
Max nodes per workflow100Per workflow
Max nested loop depth3Per execution

When concurrency limits are reached:

  • New executions are queued in pending state
  • They execute as soon as a slot becomes available
  • FIFO ordering is maintained within the queue

Execution Context

Every node receives an execution context with:

{ workspaceId: string; // The workspace this execution belongs to executionId: string; // Unique ID for this execution run workflowId: string; // The workflow definition ID triggeredBy: string; // What triggered this execution variables: Record<string, unknown>; // Accumulated variables from all previous nodes }

Execution History

Every execution is logged with:

  • Start and end timestamps
  • Final state (completed, failed, cancelled, timed_out)
  • Per-node execution log — input config, output data, duration, errors
  • Variable snapshots at each step

Access execution history via:

  • The workflow builder UI (Executions tab)
  • GET /api/v1/workflows/:id/executions API endpoint

Failure Behavior

When a node fails:

  1. The engine checks for a Try-Catch wrapper — if found, the catch branch executes
  2. If no Try-Catch, the engine checks for a Retry wrapper — if found, the node is retried
  3. If no Retry, the engine checks for an On-Error handler — if found, the handler executes
  4. If no error handler at all, the execution state changes to failed

The failed node’s error message and stack trace are recorded in the execution log.

Idempotency

Workflow executions are not automatically idempotent. If you need idempotent operations:

  • Use the executionId as an idempotency key when calling external APIs
  • Check for existing state before creating resources (e.g., check if a lead already exists before creating)
  • Use the Condition node to skip actions that have already been performed

Timeouts

  • Execution timeout: 30 minutes total. If exceeded, the execution is marked timed_out and all in-progress nodes are cancelled.
  • Node timeout: Individual nodes have a 60-second default timeout for HTTP operations. Some nodes (like AI operations) have extended timeouts.
  • Suspended execution timeout: Suspended executions expire after 24 hours if the wait condition is never met.