New to Kestra?
Use blueprints to kickstart your first workflows.
Block downstream pipelines on bad data with Soda and Kestra. SodaCL checks feed an If branch that alerts Slack and fails the execution explicitly.
A quality check that only writes a log line is a suggestion; a quality gate stops the pipeline. This blueprint turns io.kestra.plugin.soda.Scan into a gate: the scan runs with allowFailure: true so a failing check does not short-circuit the flow, then an If task branches on {{ outputs.gate_scan.result.hasFailures }}. Bad data alerts Slack and fails the execution through an explicit Fail task, so anything gated on this flow, dbt runs, exports, publishing jobs, never sees it. Good data logs a clean verdict and lets downstream work proceed.
load_orders (io.kestra.plugin.jdbc.duckdb.Queries) creates and refreshes a small orders table in the DuckDB file at warehouse_path. It exists so the blueprint runs anywhere; in production you replace it with your dlt, Airbyte, or JDBC load task.gate_scan (io.kestra.plugin.soda.Scan) receives the warehouse connection through the configuration map, which must define a data source named kestra, and the SodaCL checks through the checks map. The requirements list adds soda-core-duckdb for the DuckDB connection. allowFailure: true converts a failed check into a WARNING state instead of a hard failure, handing the decision to the next task.route_gate (io.kestra.plugin.core.flow.If) branches on {{ outputs.gate_scan.result.hasFailures }}. The failure branch posts a Slack alert naming the flow and execution, then fail_execution (io.kestra.plugin.core.execution.Fail) fails the execution with a message pointing at the scan logs.errors block alerts on any failure path, including a scan that could not run at all, and a disabled-by-default Schedule trigger runs the gate every morning.Fail task, with an error message that names the next action.Soda decides whether the data passes; it cannot stop a downstream job it knows nothing about. Kestra supplies the gate mechanics: allowFailure defers the verdict, the If task turns a structured output into control flow, and the Fail task makes the block explicit in the execution history rather than implicit in a skipped schedule. Every gate decision is preserved as structured outputs, so you can audit exactly which check closed the gate and when.
warehouse_path input) visible to the task containers. For production warehouses, swap the configuration map to Snowflake, BigQuery, or Postgres and add the matching soda-core-* package to requirements.SLACK_WEBHOOK_URL: Slack incoming webhook URL.SLACK_WEBHOOK_URL secret to your Kestra namespace.missing_count(customer_id) = -1 and rerun to watch the gate close: Slack alert, explicit Fail, errors block.load_orders with your real load task, put your downstream tasks in the else branch, and enable the after_load_window trigger.configuration map at Snowflake, BigQuery, or Postgres with credentials from {{ secret('...') }}, and add soda-core-snowflake, soda-core-bigquery, or soda-core-postgres to requirements.else Log with a Subflow task so the gate protects an entire downstream flow.