Automate Workflow Dependencies by Chaining Flows

For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append .md to any kestra.io/docs/* URL for plain Markdown.

Trigger one flow based on the execution of another flow.

type: io.kestra.plugin.core.trigger.Flow

A Flow trigger runs a flow after another flow completes, enabling event-driven dependencies across teams and namespaces.

Check the Flow trigger documentation for the list of all properties.

Upstream flow dependencies

The dependsOn property is a list of upstream flow entries that must all complete in matching states before the trigger fires.

Basic single upstream flow

The example below triggers flow_b when flow_a from the company.team namespace completes successfully:

id: flow_b
namespace: company.team
tasks:
- id: hello
type: io.kestra.plugin.core.log.Log
message: "Hello World!"
triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: extract
namespace: company.team
states: [SUCCESS]

Multiple upstream flows

List multiple entries under dependsOn. All entries must be satisfied before the trigger fires:

triggers:
- id: after_staging
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: stg_sales
namespace: company.team
- flowId: stg_marketing
namespace: company.team

Entry properties

PropertyTypeDescription
flowIdStringThe ID of the upstream flow to match. Omit to match any flow (combine with when to narrow the scope).
namespaceStringThe namespace of the upstream flow. Exact match only — use when for prefix or pattern matching.
statesList<State>States that satisfy this entry. Defaults to all terminal states and PAUSED when omitted.
labelsMap<String, String>Key-value pairs that must all be present on the upstream execution’s labels.
whenStringA Pebble expression evaluated against the upstream execution. The entry is satisfied only when this evaluates to true.

Satisfaction mode

The mode property controls how dependsOn entries are combined before the trigger fires:

ValueBehaviorExtra property
ALL (default)All entries must be satisfied
ANYFires as soon as any one entry is satisfied
AT_LEASTFires when at least N entries are satisfiedminSatisfied (integer ≥ 1)

Use mode: ANY to replace multiple separate Flow triggers with one:

triggers:
- id: react_to_any_source
type: io.kestra.plugin.core.trigger.Flow
mode: ANY
dependsOn:
- flowId: ingest_salesforce
namespace: company.sources
states: [SUCCESS]
- flowId: ingest_hubspot
namespace: company.sources
states: [SUCCESS]

Use mode: AT_LEAST with minSatisfied for N-of-M logic — fire when 2 out of 3 upstream flows succeed:

triggers:
- id: partial_success
type: io.kestra.plugin.core.trigger.Flow
mode: AT_LEAST
minSatisfied: 2
dependsOn:
- flowId: ingest_salesforce
namespace: company.sources
states: [SUCCESS]
- flowId: ingest_hubspot
namespace: company.sources
states: [SUCCESS]
- flowId: ingest_zendesk
namespace: company.sources
states: [SUCCESS]
window:
deadline: "09:00:00"

Prefix and pattern matching

When namespace is set, Kestra matches it exactly. To match a range of namespaces or flows, omit namespace and use when with a Pebble expression:

triggers:
- id: alert_on_failure
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- states: [FAILED, WARNING]
when: "{{ namespace | startsWith('company') }}"

Conditional guard with when

Like all triggers, the Flow trigger supports a top-level when Pebble expression. It is evaluated before dependsOn — if it returns a falsy value, the trigger does not fire regardless of upstream state:

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
when: "{{ labels.env == 'production' }}"
dependsOn:
- flowId: extract
namespace: company.team

Time window

The window property controls how long Kestra accumulates upstream executions before evaluating whether all dependsOn entries are satisfied.

Deadline

All upstream flows must complete before a fixed time each day. The deadline is a java.time.LocalTime value in HH:mm:ss format. It is resolved in the server timezone unless a timezone is set:

triggers:
- id: after_staging
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: stg_sales
namespace: company.team
- flowId: stg_marketing
namespace: company.team
window:
deadline: "09:00:00"

Daily time range

Only executions that completed within a specific time range each day are counted:

triggers:
- id: after_staging
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: stg_sales
namespace: company.team
- flowId: stg_marketing
namespace: company.team
window:
from: "06:00:00"
to: "12:00:00"

Fixed interval

every defines the window size and offset shifts its start relative to midnight:

triggers:
- id: after_staging
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: stg_sales
namespace: company.team
window:
every: PT1H
offset: PT30M

Lookback

Count executions that completed within the past duration, relative to the current evaluation time:

triggers:
- id: after_staging
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: stg_sales
namespace: company.team
window:
lookback: PT1H

Timezone

deadline, from, to, every and offset are anchored on daily or midnight boundaries, which are resolved in the server timezone by default. A flow therefore behaves differently depending on where it is deployed. Set timezone to a time-zone ID so the window follows the intended zone, including across daylight-saving transitions:

triggers:
- id: after_staging
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: stg_sales
namespace: company.team
window:
deadline: "09:00:00"
timezone: Europe/Paris

lookback is relative to the evaluation time rather than to a daily boundary, so it is not affected by timezone.

Firing more than once in a window

Once every dependsOn entry has been satisfied and an execution has been created, the stored results are reset. The trigger can fire again in the same window, though every dependency has to be satisfied again first. To create an execution as soon as any single upstream flow succeeds, use mode: ANY instead.

Scoped trigger outputs

When a Flow trigger fires, upstream execution outputs are available under trigger.outputs. Outputs are scoped by flow ID to avoid key collisions when multiple upstream flows are involved:

trigger.outputs.<flowId>.<outputKey>

For example, to pass an output from an upstream flow named extract:

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
inputs:
date: "{{ trigger.outputs.extract.date }}"
dependsOn:
- flowId: extract
namespace: company.team

Label-based filtering

Use the labels map on a dependsOn entry to restrict which upstream executions are counted. All specified labels must be present on the upstream execution:

triggers:
- id: after_prod
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- namespace: company.team
labels:
env: production
states: [SUCCESS]

Filtering with when expressions

Use when on a dependsOn entry to apply arbitrary Pebble conditions against the upstream execution context.

Filter on an output value:

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: extract
namespace: company.team
when: "{{ outputs.row_count > 0 }}"

Filter on retry attempts:

triggers:
- id: after_flaky
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: flaky_pipeline
namespace: company.team
states: [SUCCESS]
when: "{{ hasRetryAttempt == true }}"

Example: data pipeline with SLA deadline

This example triggers the silver_layer flow once the bronze_layer flow finishes successfully by 9 AM:

id: silver_layer
namespace: company.team
tasks:
- id: transform_data
type: io.kestra.plugin.core.log.Log
message: deduplication, cleaning, and minor aggregations
triggers:
- id: flow_trigger
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: bronze_layer
namespace: company.team
states: [SUCCESS]
window:
deadline: "09:00:00"

Example: alerting on failure

This example creates a system flow that sends a Slack alert on any failure or warning state within the company namespace:

id: alert
namespace: system
tasks:
- id: send_alert
type: io.kestra.plugin.slack.notifications.SlackExecution
url: "{{secret('SLACK_WEBHOOK')}}"
channel: "#general"
executionId: "{{trigger.executionId}}"
triggers:
- id: alert_on_failure
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- states: [FAILED, WARNING]
when: "{{ namespace | startsWith('company') }}"

Example: mixed success and failure triggers

You can define multiple Flow triggers on the same flow to react differently to upstream success vs. failure:

triggers:
- id: on_completion
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: flow_a
namespace: company.team
states: [SUCCESS]
- id: on_failure
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: flow_a
namespace: company.team
states: [FAILED]

Example: passing upstream outputs downstream

Reference upstream outputs using the scoped path trigger.outputs.<flowId>.<key>:

id: flow_b
namespace: company.team
inputs:
- id: value_from_a
type: STRING
tasks:
- id: hello
type: io.kestra.plugin.core.log.Log
message: "{{ inputs.value_from_a }}"
triggers:
- id: upstream_dep
type: io.kestra.plugin.core.trigger.Flow
inputs:
value_from_a: "{{ trigger.outputs.flow_a.return_value }}"
dependsOn:
- flowId: flow_a
namespace: company.team
states: [SUCCESS]

Input rendering failures create FAILED executions

If an inputs expression on a Flow trigger fails to render — for example, because an upstream output key does not exist — Kestra creates a FAILED execution instead of silently dropping the event. This makes failures visible in the UI and actionable via alerting.

Removed: preconditions and conditions

The preconditions and conditions properties are removed in Kestra 2.0. Flows that still use them will fail to parse after upgrading. Migrate to dependsOn.

See the trigger conditions migration guide for a complete before/after reference.

Was this page helpful?