New to Kestra?
Use blueprints to kickstart your first workflows.
Verify a dataset against a Soda Core 4 data contract, alert Slack on failed checks, and log the result for data platform teams.
SQL review catches a schema change when someone remembers to ask for one; it does not run on
a schedule, and it does not stop a row_count collapse or a missing-value regression the
moment it lands. Soda Core 4 replaced ad hoc SodaCL scans with an explicit data contract, and
this blueprint verifies that contract on a schedule and treats any check that fails the
contract as a real governance breach, not a dashboard someone has to remember to check.
This blueprint was created by Mohit Jeswani.
seed_dataset (io.kestra.plugin.scripts.python.Script) creates a fresh orders.duckdb
with three clean rows, so this flow runs end to end with no external warehouse account.
Swap this task for your real data source and delete it once you point verify_contract
at production data.verify_contract (io.kestra.plugin.soda.VerifyContract) verifies the kestra/orders
dataset against a Soda Core 4 contract: id and amount must never be missing, and
row_count must stay above 2. allowFailure: true covers a verification session error,
for example a bad connection or a renamed dataset, without failing the flow outright.evaluate (io.kestra.plugin.core.output.OutputValues) reads hasErrors/hasWarnings
directly from VerifyContract's own output and counts individually failed checks with a
jq filter.route (io.kestra.plugin.core.flow.If):summary logs the failed check count and severity, closing the governance cycle.errors alerts Slack if the flow itself fails, for example a pip install error.VerifyContract (Soda Core
4's data-contract model) used as a standing compliance gate, distinct from this repo's
existing SodaCL Scan-based blueprints.Soda Core verifies a contract once, when invoked; it does not decide a cadence, compare the
result against a failure tolerance, or notify anyone. Kestra keeps max_failed_checks as a
flow input, turns the verification into a Slack page naming the exact checks that failed, and
keeps a full execution history of every run.
python:3.12-slim, since Soda Core 4
has no official image yet and this task installs it via requirements on top of the
default image.soda-core/soda-duckdb.This flow's seed_dataset step makes it fully runnable locally with nothing beyond Docker;
no external account is required to exercise the default example.
SLACK_WEBHOOK_URL: used by alert and the errors block. In Kestra OSS, provide it as
the base64-encoded environment variable SECRET_SLACK_WEBHOOK_URL.| Input | Default | Purpose |
|---|---|---|
max_failed_checks |
0 |
Failed-check count above which the Slack alert fires |
{{ outputs.verify_contract.result }}: full per-check verification payload
(contractResults[].checks[], each with name, type, column, outcome).{{ outputs.verify_contract.checkCount }} / .hasFailures / .hasWarnings /
.hasErrors / .exitCode: VerifyContract's own summary fields.{{ outputs.evaluate.values.failed_check_count }} / .failed_check_names: how many
checks failed and their names, semicolon-joined.{{ outputs.evaluate.values.severity }}: OK, WARNING, CONTRACT_BREACH, or
CRITICAL.row_count with no threshold is a metric only, it never fails on its own; the
must_be_greater_than threshold used here is what turns a row-count collapse into a real
failure.hasErrors, for example a renamed dataset) and a real check
failure (hasFailures) are different severities here (CRITICAL vs. possible
CONTRACT_BREACH); read failed_check_names to tell a true data-quality regression from a
broken connection.missing: {} on a column with no rows at all passes trivially since there is nothing to be
missing; pair it with the row_count threshold in this contract so an empty dataset is
still caught.SLACK_WEBHOOK_URL secret.compliant logs a clean contract.seed_dataset's INSERT to fewer than 3 rows, or set amount to NULL on one row,
and rerun to confirm alert fires naming the failed check.seed_dataset and dataSource/contracts with your real dataset and contract,
then enable the daily trigger.contracts list to verify several datasets in one run; jq
already iterates contractResults[], so the evaluate step needs no change.CRITICAL severity (session error) to an on-call channel separately from a
CONTRACT_BREACH (real check failure), since the two have different owners.severity is OK, turning this gate into a
pre-publish check before a dataset is marked ready for consumption.