Pipeline Event Trigger Node
Overview
The Pipeline Event Trigger Node automatically initiates a MaestroHub pipeline when another pipeline finishes execution with a matching status. This enables chaining workflows—for example, running a cleanup pipeline after a data-import pipeline completes, or triggering an alert pipeline when a critical workflow fails. Built-in chain-depth tracking and self-trigger prevention ensure that cascading pipelines cannot create infinite loops.
Core Functionality
What It Does
1. Cross-Pipeline Automation Start pipelines automatically when one or more watched pipelines reach a specified execution status—without manual intervention, polling, or external schedulers.
2. Status-Based Filtering
Subscribe to specific execution outcomes: completed, failed, completed_with_errors, cancelled, or the wildcard any to match all terminal statuses.
3. Chain-Depth Tracking
Every execution triggered by a Pipeline Event Trigger carries a chain_depth counter. When a triggered pipeline itself has a Pipeline Event Trigger, the depth increments. A chain stops after three event-triggered runs in a row, which prevents runaway loops.
4. Self-Trigger Prevention A pipeline cannot watch itself. This is enforced at the domain level during pipeline validation, ensuring that a pipeline's own execution events never re-trigger the same pipeline.
5. Multi-Pipeline Watching A single trigger can monitor multiple source pipelines. When any of the watched pipelines finishes with a matching status, the trigger fires.
Chain Depth & Loop Prevention
How Chain Depth Works
Chain depth tracks how many Pipeline Event Triggers have fired in sequence:
- Depth 0: A pipeline runs via manual, schedule, webhook, or any non-pipeline-event trigger
- Depth 1: A Pipeline Event Trigger fires in response to the depth-0 execution
- Depth 2: Another Pipeline Event Trigger fires in response to the depth-1 execution
- Depth 3: A third Pipeline Event Trigger fires in response to the depth-2 execution
When a depth-3 execution finishes, a Pipeline Event Trigger watching it does not fire. The event is dropped and a warning is logged.
The limit is the trigger's maxChainDepth. The designer has no control for it and always saves 3. A pipeline created or edited through the API or MCP can set a lower value (1–3) — see the Configuration reference.
Example
Pipeline A (manual run, depth 0)
└─ triggers Pipeline B (depth 1)
└─ triggers Pipeline C (depth 2)
└─ triggers Pipeline D (depth 3)
└─ a trigger watching D would start depth 4 → BLOCKED
Safety Guarantees
| Protection | Mechanism |
|---|---|
| Self-trigger | Domain validation rejects a pipeline that watches itself |
| Infinite chains | Chain depth counter compared against maxChainDepth (3 from the designer) |
| Missing depth metadata | Defaults to depth 0 if metadata is absent |
A chain can run at most three event-triggered pipelines in a row. Design your pipeline chains to complete meaningful work within this limit. If you need deeper orchestration, consider using a single coordinator pipeline that triggers each step sequentially.
Configuration Options
Basic Information
| Field | Type | Description |
|---|---|---|
| Node Label | String (Required) | Display name for the node on the pipeline canvas. Must be non-empty (trimmed). |
| Description | String (Optional) | Explains what this trigger monitors and initiates. |
Parameters
The trigger configuration is organized across two tabs in the UI: Parameters and Settings.
| Parameter | Type | Default | Required | Constraints | Description |
|---|---|---|---|---|---|
| Watched Pipelines | string[] | [] | Yes | At least 1 pipeline; cannot include the current pipeline | Pipelines whose execution events trigger this pipeline. |
| Watched Statuses | string[] | [] | Yes | At least 1 status from the allowed set | Execution statuses that trigger this pipeline. |
| Enabled | boolean | true | No | -- | Enable/disable the trigger. |
Watched Statuses
| Status | Description |
|---|---|
completed | Pipeline finished successfully—all nodes passed. |
failed | Pipeline stopped due to a node failure (after retries exhausted). |
completed_with_errors | Pipeline finished but some nodes encountered errors (with On Error set to continueExecution). |
cancelled | Pipeline execution was manually or programmatically cancelled. |
any | Wildcard—matches all terminal statuses above. |
completed_with_errors, not completedA run in which a node failed but the pipeline was set to Continue Execution ends as completed_with_errors. A trigger that watches only completed does not fire for it, and one that watches only failed does not either. Watch completed_with_errors explicitly, or use any, if the chained pipeline should also run after a partial success. See Run status and On Error.
The pipeline selector in the UI automatically excludes the current pipeline from the list. At the backend, domain validation also rejects a pipeline that includes its own ID in watchedPipelineIds.
Settings
Description
A free-text area for documenting the node's purpose and behavior. Notes entered here are saved with the pipeline and visible to all team members.
Execution Settings
| Setting | Options | Default | Description |
|---|---|---|---|
| Timeout (seconds) | number | Pipeline default | Maximum execution time for this node (1--600). Leave empty for pipeline default. |
| Retry on Timeout | Pipeline Default / Enabled / Disabled | Pipeline Default | Whether to retry the node if it times out. |
| Retry on Fail | Pipeline Default / Enabled / Disabled | Pipeline Default | Whether to retry on failure. When Enabled, shows Advanced Retry Configuration. |
| On Error | Pipeline Default / Stop Pipeline / Continue Execution | Pipeline Default | Behavior when node fails after all retries. |
Advanced Retry Configuration (visible when Retry on Fail = Enabled)
| Field | Type | Default | Range | Description |
|---|---|---|---|---|
| Max Attempts | number | 3 | 1--10 | Maximum retry attempts. |
| Initial Delay (ms) | number | 1000 | 100--30,000 | Wait before first retry. |
| Max Delay (ms) | number | 120000 | 1,000--300,000 | Upper bound for backoff delay. |
| Multiplier | number | 2.0 | 1.0--5.0 | Exponential backoff multiplier. |
| Jitter Factor | number | 0.1 | 0--0.5 | Random jitter (+-percentage). |
Output Data Structure
When a watched pipeline finishes with a matching status, the trigger produces a structured output with two top-level keys: _metadata and result.
$trigger.result is the watched run, not this pipeline's resultEvery node output in MaestroHub is the same { result, _metadata } envelope, so this trigger's payload lands under result like any other trigger's. Everything in it describes the source run that fired the trigger. When you need the source run's identity, prefer the unambiguous _metadata.source* keys (sourcePipelineName, sourceExecutionId, sourceStatus, sourcePipelineVersion); use result for the counts and the failed-node list.
Output Format
{
"_metadata": {
"type": "pipeline_event_trigger",
"sourcePipelineId": "bf29be94-fc0a-4dc4-8e5c-092f1b74eb4b",
"sourcePipelineName": "Daily Data Import",
"sourcePipelineVersion": 12,
"sourceExecutionId": "aef374c3-aa2b-454e-aabc-5657faac5950",
"sourceStatus": "completed_with_errors",
"sourceTriggerType": "schedule",
"sourceDuration": "2m15.3s",
"chainDepth": 1
},
"result": {
"executionId": "aef374c3-aa2b-454e-aabc-5657faac5950",
"pipelineId": "bf29be94-fc0a-4dc4-8e5c-092f1b74eb4b",
"pipelineName": "Daily Data Import",
"pipelineVersion": 12,
"status": "completed_with_errors",
"duration": "2m15.3s",
"nodeCount": 8,
"successCount": 7,
"failureCount": 1,
"skippedCount": 0,
"triggerType": "schedule",
"errors": [
{
"nodeId": "node-1726000000000-ab3k9x2qz",
"nodeName": "Parse Orders",
"nodeType": "logic.javascript",
"errorMessage": "javascript execution failed: TypeError: cannot read 'sku' of undefined",
"errorCode": "SCRIPT_ERROR",
"errorCategory": "permanent",
"attemptCount": 1
}
]
}
}
A run with no failed node carries "errors": null. Guard on result.failureCount, not on the array's length.
_metadata Fields
| Field | Type | Description |
|---|---|---|
type | string | Always "pipeline_event_trigger". |
sourcePipelineId | string | UUID of the watched pipeline that completed. |
sourcePipelineName | string | Display name of the source pipeline. |
sourcePipelineVersion | number | Version of the source pipeline that ran. Node names and wiring can change between versions; this is the version the summary below describes, and the one GET /pipelines/:id/versions/:version returns. |
sourceExecutionId | string | UUID of the specific execution that triggered this event. |
sourceStatus | string | Terminal status of the source execution ("completed", "failed", "completed_with_errors", "cancelled"). |
sourceTriggerType | string | How the source pipeline was started ("schedule", "webhook", "manual", ...). This pipeline's own trigger type is always pipeline_event. |
sourceDuration | string | Total execution duration as a Go duration string (e.g., "2m15.3s", "500ms"). |
chainDepth | number | Current depth in the pipeline event chain (starts at 1 for the first triggered pipeline). |
result Fields
| Field | Type | Description |
|---|---|---|
executionId | string | UUID of the source execution (same as _metadata.sourceExecutionId). |
pipelineId | string | UUID of the source pipeline (same as _metadata.sourcePipelineId). |
pipelineName | string | Display name of the source pipeline. |
pipelineVersion | number | Version of the source pipeline that ran (same as _metadata.sourcePipelineVersion). |
status | string | Terminal status of the source execution. |
duration | string | Total execution duration as a Go duration string. |
nodeCount | number | Total number of nodes in the source pipeline. |
successCount | number | Number of nodes that completed successfully. |
failureCount | number | Number of nodes that failed. |
skippedCount | number | Number of nodes that were skipped. |
triggerType | string | How the source pipeline was originally triggered (same as _metadata.sourceTriggerType). Not this pipeline's trigger type. |
errors | array | null | One entry per failed node, described below. null when no node failed. Nodes that failed under On Error: Continue Execution are included; skipped and cancelled nodes are not. |
result.errors[] Fields
| Field | Type | Description |
|---|---|---|
nodeId | string | The node's id. Stable for the life of the node and unique in the pipeline — use it to key on, correlate, or look the node up. |
nodeName | string | The node's display name as of the version that ran — the $node["Name"] key an author uses. Meant for humans: put it in alert text. Names are not unique, so never branch on it. |
nodeType | string | The node type, e.g. logic.javascript, connected.postgres.write. |
errorMessage | string | The error the node recorded. |
errorCode | string | The classified code when the executor set one, e.g. CONN_TIMEOUT; SCRIPT_ERROR when the node's own JavaScript or expression threw, failed to evaluate or did not compile (TIMEOUT when it ran past the node's timeout); UNKNOWN_ERROR otherwise. |
errorCategory | string | transient, permanent or timeout. |
attemptCount | number | Attempts made, including retries. |
Referencing in Downstream Nodes
Use expressions to access pipeline event data in subsequent nodes:
$trigger._metadata.type-- always"pipeline_event_trigger"$trigger._metadata.sourcePipelineId-- UUID of the source pipeline$trigger._metadata.sourcePipelineName-- name of the source pipeline$trigger._metadata.sourcePipelineVersion-- version of the source pipeline that ran$trigger._metadata.sourceExecutionId-- UUID of the source execution$trigger._metadata.sourceStatus-- terminal status ("completed","failed", etc.)$trigger._metadata.sourceTriggerType-- how the source pipeline was started$trigger._metadata.sourceDuration-- execution duration string$trigger._metadata.chainDepth-- current chain depth$trigger.result.nodeCount-- total nodes in the source pipeline$trigger.result.successCount-- nodes that succeeded$trigger.result.failureCount-- nodes that failed$trigger.result.skippedCount-- nodes that were skipped$trigger.result.triggerType-- how the source pipeline was triggered$trigger.result.errors-- the failed nodes (nullwhen none), each withnodeId,nodeName,errorMessage, ...
Ids minted by the designer look like node-1726000000000-ab3k9x2qz, so an alert built from nodeId tells nobody anything. Use nodeName for the text and keep nodeId for anything a machine compares:
if ($trigger.result.failureCount > 0) {
const e = $trigger.result.errors[0];
return {
text: `${$trigger._metadata.sourcePipelineName} failed at "${e.nodeName}": ${e.errorMessage}`,
nodeId: e.nodeId,
executionId: $trigger._metadata.sourceExecutionId,
};
}
Validation Rules
Parameter Validation
Node Label
- Must not be empty
- Must not consist only of whitespace
- Error: "Node name is required"
Watched Pipeline IDs
- Must be provided and non-empty
- Must contain at least one pipeline ID
- Cannot include the current pipeline's own ID (self-trigger prevention)
- Each ID must be a valid UUID referencing an existing pipeline
- Error: "At least one pipeline must be selected"
- Error: "Pipeline cannot watch itself"
Watched Statuses
- Must be provided and non-empty
- Must contain at least one status
- Each status must be one of:
completed,failed,completed_with_errors,cancelled,any - Error: "At least one status must be selected"
- Error: "Invalid status: must be one of completed, failed, completed_with_errors, cancelled, any"
Max Chain Depth (maxChainDepth, API and MCP only)
- Must be a whole number between 1 and 3 (inclusive)
Enabled Flag
- Must be a boolean if provided
- Error: "Enabled must be a boolean value"
Usage Examples
Post-Import Cleanup
Key configuration
- Label: Post-Import Cleanup
- Watched Pipelines: Daily Data Import
- Watched Statuses:
completed - Enabled: true
- Settings: retry disabled, on error
stop
Downstream usage: $trigger._metadata.sourceExecutionId to correlate with the import execution, $trigger.result.nodeCount and $trigger.result.successCount to verify all import steps ran.
Failure Alert Pipeline
Key configuration
- Label: Critical Pipeline Failure Alert
- Watched Pipelines: Payment Processor, Order Fulfillment, Inventory Sync
- Watched Statuses:
failed,completed_with_errors - Enabled: true
- Settings: retry enabled, on error
continue
Downstream usage: $trigger._metadata.sourcePipelineName to identify which pipeline failed, $trigger.result.errors[0].nodeName and .errorMessage to say where and why in the alert, $trigger._metadata.sourceTriggerType to understand the original trigger context.
Cascading ETL Pipeline
Key configuration
- Label: Transform After Extract
- Watched Pipelines: Data Extraction Pipeline
- Watched Statuses:
completed - Enabled: true
- Settings: retry disabled, on error
stop
Downstream usage: $trigger._metadata.chainDepth to track position in the ETL chain, $trigger.result.duration to log extraction time, $trigger._metadata.sourceStatus to confirm successful extraction before transforming.
Audit Logging
Key configuration
- Label: Execution Audit Logger
- Watched Pipelines: All critical business pipelines
- Watched Statuses:
any - Enabled: true
- Settings: retry enabled, on error
continue
Downstream usage: Log $trigger.result.pipelineName, $trigger.result.status, $trigger.result.duration, $trigger.result.nodeCount, $trigger.result.successCount, $trigger.result.failureCount, and $trigger.result.skippedCount to an audit database for compliance tracking.
Best Practices
Designing Pipeline Chains
| Practice | Rationale |
|---|---|
| Keep chains short | Most workflows need one or two hops; use all three only when each stage does distinct, necessary work |
| Use a coordinator pattern | For deep orchestration, have one pipeline that triggers each step via webhooks or schedules |
| Avoid circular dependencies | Even though self-trigger is blocked, A → B → A is possible; the chain depth limit stops it after three hops |
| Document chain relationships | Use the Settings description to note which pipelines watch this one |
Status Selection
- Use
completedfor success-dependent workflows (ETL chains, post-processing) - Use
failedorcompleted_with_errorsfor alerting and remediation pipelines - Use
anysparingly—primarily for audit logging or monitoring dashboards - Combine statuses when a single response pipeline handles multiple outcomes
Error Handling Strategies
For Critical Chains:
- Set On Error to
stopPipelineon the triggered pipeline - Watch for
failedstatus on the triggered pipeline with a separate alert pipeline - Use
$trigger.result.errorsto propagate error context through the chain
For Best-Effort Workflows:
- Set On Error to
continueExecution - Use
$trigger.result.failureCountto detect partial failures - Log results for later analysis without blocking the chain
Performance Considerations
| Scenario | Recommendation |
|---|---|
| High-frequency source pipelines | Ensure downstream pipelines complete before the next trigger fires to avoid queue buildup |
| Many watchers on one pipeline | Each watcher creates an independent execution; monitor total throughput |
| Long-running triggered pipelines | Set appropriate timeouts; consider whether the source pipeline's next run will re-trigger |
| Deep chains with parallel watchers | Can create exponential fan-out; map the full chain before deploying |
Enable vs. Disable
- Use the trigger's Enabled parameter to temporarily pause event-driven chaining without modifying pipeline configuration
- Disable triggers during maintenance on source pipelines to prevent cascading failures
- Document the reason for disabled triggers in the node's Notes field
- Re-enable triggers only after verifying source pipelines are stable
Configuration reference
The fields below are generated from the node's config contract, so they match what the pipeline validator enforces and what the designer's form offers.
trigger.pipeline.event
| Field | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
watchedPipelineIds | string[] | yes | — | — | Ids of the pipelines whose execution end should start this one, e.g. ["4f2c…"]; at least one, and not this pipeline itself |
watchedStatuses | string[] | yes | — | completed, failed, completed_with_errors, cancelled, any | Execution statuses of a watched pipeline that fire this trigger, e.g. ["completed", "failed"]; any matches every terminal status. At least one |
maxChainDepth | integer | no | 3 | 1–3 | How many pipeline-event triggers may fire in a row before this one stops the chain, 1 to 3 (default 3): a run started by a manual, schedule or webhook trigger is depth 0, each event-triggered run adds one. Guards against A→B→A loops |
enabled | boolean | no | true | — | false pauses this trigger without deleting it: the watched pipelines are not subscribed and nothing fires. Default true |
testData | any | no | — | — | Event payload the designer's sandbox (Test) run pretends to receive, read downstream as $trigger.result exactly as a real event would be. Unused in production |