Portal Community
ParallelFork and ParallelJoin must be used together. Fork opens the parallel execution lanes; Join synchronises on all lanes and collects their outputs into lane_outputs keyed by lane node key. Every branch connected to the Fork's success port must eventually connect into the paired Join.

When to Use

Configuration

No configuration required. The ParallelJoin node reads its inputs exclusively from ExecutionMemory — it detects the paired ParallelFork automatically from the workflow graph structure and waits for all lanes registered by that Fork to report completion.

Output Ports

PortFires When
successAll parallel lanes have completed successfully (or with handled errors when FailFast: false). Lane outputs are available in lane_outputs. Sequential execution resumes.
errorOne or more lanes failed AND the paired Fork has FailFast: true. Exception details from the failing lane are available. Wire this to a CatchBlock or error notification node.

OutputData Fields

Fields available downstream via $output.<joinNodeKey>:

FieldTypeDescription
join_idstringThe node key of this Join node — for audit and logging purposes.
lanes_completedstringISO 8601 timestamp of when the final lane completed and the Join fired.
lane_countintTotal number of parallel lanes that were collected. Matches the number of branches connected to the paired Fork's success port.
lane_outputsdictDictionary of lane results keyed by lane_<laneNodeKey>. Each entry contains the final output data of that lane's last node. Access: $output.myJoin.lane_outputs.lane_creditCheck.

Accessing Lane Outputs

After the Join fires, each lane's output is accessible by key. The key format is lane_ followed by the node key of the first node in that lane (the node directly connected to the Fork's success port).

ExpressionReturns
{{ $output.myJoin.lane_outputs.lane_creditCheck }}Full output object of the creditCheck lane.
{{ $output.myJoin.lane_outputs.lane_creditCheck.score }}Specific field from that lane's output.
{{ $output.myJoin.lane_count }}Total number of lanes collected.
{{ $output.myJoin.lanes_completed }}ISO timestamp when all lanes finished.

Sample Output

Success Port — All Branches Joined

ParallelJoin collects outputs from all forked branches and merges them into a single output when all are complete.

{
  "_join": {
    "waitedFor": 3,
    "receivedCount": 3,
    "timedOutCount": 0,
    "joinStrategy": "all",
    "totalWaitMs": 1650
  },
  "merged": {
    "inventory": { "totalItems": 1432, "lowStockCount": 12 },
    "pricing": { "priceListVersion": "2025-03", "updatedCount": 89 },
    "suppliers": { "activeSuppliers": 24, "pendingOrders": 7 }
  }
}

Timeout Port (if join times out)

{
  "_join": {
    "waitedFor": 3,
    "receivedCount": 2,
    "timedOutCount": 1,
    "timedOutBranches": ["fetchSuppliers"],
    "joinStrategy": "all",
    "timeoutMs": 5000
  }
}

Expression Reference

ExpressionDescription
{{ $output.joinNode.merged.inventory.totalItems }}Access merged output from the "inventory" branch.
{{ $output.joinNode._join.receivedCount }}Number of branches that completed successfully.
{{ $output.joinNode._join.timedOutBranches[0] }}Name of the first branch that timed out, if any.
{{ $output.joinNode._join.totalWaitMs }}Total milliseconds waited for branches to complete.

Node Policies and GuardRails

Pattern Examples

Pattern 1 — Three-Channel Notification with Result Check

Send notifications in parallel; after Join, check whether all channels succeeded or whether any failed. Escalate if all channels failed.

ParallelFork  [key: "notifyFork"]
  └─► success ──┬── EmailSmtp     [key: "sendEmail"]
                ├── SlackNode     [key: "sendSlack"]
                └── HttpRequest   [key: "sendSms"]
                      (POST /sms/send)
  ──► ParallelJoin  [key: "notifyJoin"]
        └─► success
              emailResult:  {{ $output.notifyJoin.lane_outputs.lane_sendEmail.status }}
              slackResult:  {{ $output.notifyJoin.lane_outputs.lane_sendSlack.ok }}
              smsResult:    {{ $output.notifyJoin.lane_outputs.lane_sendSms.statusCode }}

  └─► IfCondition  [key: "checkAnySucceeded"]
        Condition: {{ $output.notifyJoin.lane_outputs.lane_sendEmail.status === 'sent' ||
                      $output.notifyJoin.lane_outputs.lane_sendSlack.ok === true }}
        ├─► true  ──► [at least one channel succeeded]
        └─► false ──► [escalate: create manual contact task]

Pattern 2 — Concurrent Enrichment Merge

Fetch credit score, fraud risk, and loyalty tier concurrently. After Join, combine all three into a unified risk profile for the decision engine.

ParallelFork  [key: "enrichFork"]
  └─► success ──┬── HttpRequest  [key: "creditApi"]   GET /credit/{{ $json.customerId }}
                ├── HttpRequest  [key: "fraudApi"]    GET /fraud/{{ $json.customerId }}
                └── HttpRequest  [key: "loyaltyApi"]  GET /loyalty/{{ $json.customerId }}
  ──► ParallelJoin  [key: "enrichJoin"]
        └─► success ──► VariableAssignment  [key: "buildProfile"]
                          riskProfile = {
                            creditScore:   {{ $output.enrichJoin.lane_outputs.lane_creditApi.score }},
                            fraudRisk:     {{ $output.enrichJoin.lane_outputs.lane_fraudApi.riskLevel }},
                            loyaltyPoints: {{ $output.enrichJoin.lane_outputs.lane_loyaltyApi.points }}
                          }
        └─► [decision engine node uses $var.riskProfile]

Pattern 3 — Parallel Validation with All-Pass Gate

Run 4 compliance checks in parallel. After Join, verify all 4 passed before approving the application.

ParallelFork  [key: "validateFork"]  FailFast: false
  └─► success ──┬── HttpRequest [key: "idCheck"]     GET /verify/identity
                ├── HttpRequest [key: "creditCheck"] GET /verify/credit
                ├── HttpRequest [key: "incomeCheck"] GET /verify/income
                └── HttpRequest [key: "addressCheck"] GET /verify/address
  ──► ParallelJoin  [key: "validateJoin"]
        └─► success ──► IfCondition  [key: "allPassed"]
                          Condition: {{
                            $output.validateJoin.lane_outputs.lane_idCheck.passed &&
                            $output.validateJoin.lane_outputs.lane_creditCheck.passed &&
                            $output.validateJoin.lane_outputs.lane_incomeCheck.passed &&
                            $output.validateJoin.lane_outputs.lane_addressCheck.passed
                          }}
                          ├─► true  ──► [approve application]
                          └─► false ──► [route to human review]