New to Kestra?
Use blueprints to kickstart your first workflows.
Read a Netlify site's deploys every hour, change nothing, and alert Slack when production failed to deploy, failures pile up or a build is stuck.
When a Netlify production deploy fails, nothing looks broken: the site keeps serving the last good build, the merge is green in Git, and the only trace is a red deploy in the Netlify UI that nobody is watching. The fix the team thinks is live never shipped. The same happens more quietly with deploy previews that fail on every branch, or a build that sits in the queue for an hour. This blueprint turns a scheduled, read-only call to the Netlify API into a deploy health gate: io.kestra.plugin.core.http.Request reads the site's recent deploys, one jq query finds the latest production result, the failure rate and builds that are stuck, and the result routes to a quiet log or a Slack alert.
list_deploys (io.kestra.plugin.core.http.Request) calls GET /api/v1/sites/{site_id}/deploys?per_page=100 with a bearer token, which returns the most recent deploys of every context (production, deploy previews, branch deploys). It is a read: nothing is built, published, locked or rolled back. Retries cover short network blips, and allowFailed plus allowFailure keep an API error from aborting the flow so it can be reported instead.summarize_deploys (io.kestra.plugin.core.debug.Return) first guards for a missing or non-200 response (UNREACHABLE). Otherwise one jq query keeps deploys whose created_at is inside the lookback window, treats ready and error as finished, and counts other non-skipped states (enqueued, building, uploading, processing, ...) as in flight, flagging the ones older than stuck_minutes. It also picks the latest finished deploy with context == "production" from the whole page. A failed latest production deploy is CRITICAL; a failure rate at or above warning_failure_rate, or any stuck build, is WARNING; no finished deploy in the window is LOW_VOLUME; otherwise HEALTHY.route_by_deploys (io.kestra.plugin.core.flow.Switch) branches on that classification: HEALTHY and LOW_VOLUME log quietly, WARNING posts a Slack note, CRITICAL and UNREACHABLE page Slack. A defaults fallback logs anything unexpected.log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run with the HTTP status and the full summary.errors block pages Slack if the flow itself fails, and the hourly_check trigger runs at 25 minutes past every hour, shipped disabled: true.status output (HEALTHY / WARNING / CRITICAL / LOW_VOLUME / UNREACHABLE) other flows can gate on, for example to hold a release announcement until production really deployed.Netlify can email or post a notification per deploy event, but it does not tell you that the last production deploy is still failing an hour later, that one deploy in three fails, or that a build has been queued for 45 minutes. Kestra turns a plain, read-only API call into a scheduled, typed classification with retries, a failure path and an execution history, and the same flow runs against any number of sites by changing site_id.
NETLIFY_AUTH_TOKEN: a Netlify personal access token for a user on the team that owns the site.SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.In Kestra OSS, secrets are supplied as environment variables prefixed SECRET_ and base64-encoded, for example SECRET_NETLIFY_AUTH_TOKEN=$(echo -n 'your-netlify-token' | base64), and read back with {{ secret('NETLIFY_AUTH_TOKEN') }}. Use the Enterprise secrets backend for encryption at rest and access control.
NETLIFY_AUTH_TOKEN and SLACK_WEBHOOK as secrets.site_id and run the flow manually; compare the counts and the production line in the logs with the site's Deploys page.hourly_check.io.kestra.plugin.core.flow.ForEach and one site_id per item.context == "production", or give deploy previews their own limit.POST /api/v1/sites/{site_id}/builds from a separate, approved flow when production failed on a transient error.site_id: type STRING, default your-site-id.lookback_hours: type INT, default 24.warning_failure_rate: type FLOAT, default 20.0 (percent).stuck_minutes: type INT, default 45.status: one of HEALTHY, WARNING, CRITICAL, LOW_VOLUME, UNREACHABLE.{{ fromJson(outputs.summarize_deploys.value).production_error }} for the error of a failed production deploy.page paging or a shorter window.none.