Schedule icon
Webhook icon
aws icon
Loop icon
List icon
OutputValues icon
If icon
SlackIncomingWebhook icon
Set icon

File Arrival SLA Monitor for S3 Feeds

Monitor expected partner, vendor, and bank files in S3. Alert Slack when a feed is late, missing, empty, or duplicated, and post a daily arrival report.

Categories
CloudData

Diagram unavailable

We could not build the topology for this blueprint. The flow itself is valid, use the YAML on the left to run it.

Almost every company depends on files it does not control: bank statements, payroll exports, vendor price lists, partner order feeds, CRM dumps from another team. When one of them silently does not arrive, nothing fails — the downstream pipeline just loads nothing, and someone notices days later in a dashboard. This blueprint turns "we expect these files every day by these times" into a declarative SLA monitor: you list the feeds, and Kestra tells you once when a feed is missing, empty, or duplicated, tells you again when it recovers, and sends an end-of-day arrival report.

It works with Amazon S3 and any S3-compatible storage (MinIO, Cloudflare R2, Wasabi, Backblaze B2, Ceph) and needs no database, no Lambda, and no changes on the sender's side.

How it works

  1. Triggers. check_every_15_min (Schedule) runs the checks. daily_arrival_report (Schedule, 18:00) runs them with mode: report. on_upload_event (Webhook) lets an S3 event notification or the sender's upload script request an instant check.
  2. Per feed (check_feeds, io.kestra.plugin.core.flow.Loop, 5 in parallel):
    • list_files (io.kestra.plugin.aws.s3.List) lists objects under the feed's prefix matching its pattern. {yyyy}, {MM} and {dd} in the prefix are replaced with today's date in your time zone, so date-partitioned landing folders work out of the box.

    • evaluate (io.kestra.plugin.core.output.OutputValues) keeps files modified since local midnight and classifies the feed:

      Status Meaning Alert
      NOT_EXPECTED Today is not in the feed's days –
      WAITING Fewer than min_files, deadline not reached –
      MISSING Fewer than min_files after the deadline :rotating_light:
      TOO_SMALL A file is smaller than min_bytes (empty / truncated) :warning:
      DUPLICATE Two files today share an ETag (same content re-sent) :warning:
      LATE Complete, but the first file arrived after the deadline recovery only
      ON_TIME Complete before the deadline recovery only
    • alert_on_change (If) posts to Slack only when the state changes into a problem state or out of one, so a missing feed produces one alert and one recovery message, not one alert every 15 minutes.

    • record_state (io.kestra.plugin.core.kv.Set) stores today's state per feed in the KV store (7-day TTL) for de-duplication and reporting.

  3. Report. In report mode, slack_report posts one message with every feed's state, file count, first arrival time and deadline.
  4. Self-monitoring. If the check itself fails (credentials, bucket access, invalid feed JSON), the errors branch alerts Slack — a broken monitor is never mistaken for "all files arrived".

What you get

  • One place that documents which files the business depends on, when they are due, and on which days.
  • Alerts that point at the problem (missing vs. empty vs. duplicated), sent once, with a recovery message when the file finally lands.
  • A daily arrival report your operations or finance team can read without opening a console.
  • Arrival history per feed in the KV store and in the execution log.

Who it's for

  • Data and analytics engineers whose pipelines start from files dropped by partners or other teams.
  • Finance, payroll and operations teams waiting on bank, PSP, payroll, or vendor files.
  • Platform teams replacing ad-hoc "did the file come?" cron scripts and Airflow sensors.

Why orchestrate this with Kestra

An S3 event can tell you that a file arrived; it can never tell you that one did not. Detecting absence needs a schedule, a notion of deadlines and delivery days per feed, state to avoid alert storms, and a report — usually spread across cron, a script, and a database. Kestra provides all of it declaratively: schedules and a webhook, a parallel Loop over feeds, the KV store for state, and Slack alerting, in one versioned YAML file. Because each feed is configuration, adding a new partner feed is a one-line change in the feeds input, not new code.

Prerequisites

  • A bucket where the files land (S3 or S3-compatible) and credentials with s3:ListBucket on it. No read access to file contents is needed.
  • A Slack incoming webhook.

Secrets

  • AWS_ACCESS_KEY_ID: access key allowed to list the inbound bucket(s).
  • AWS_SECRET_ACCESS_KEY: matching secret key.
  • AWS_DEFAULT_REGION: bucket region (e.g. eu-west-1; use us-east-1 or your provider's region for S3-compatible stores).
  • SLACK_WEBHOOK_URL: Slack incoming webhook for alerts and the daily report.

Inputs

  • feeds (JSON): list of expected feeds. Per feed:
    • name (required): unique identifier, used in alerts and KV keys.
    • prefix (required): key prefix; supports {yyyy}, {MM}, {dd}.
    • pattern (default .*): Java regex matched against the full key.
    • deadline (default 23:59): local HH:mm by which the feed must be complete.
    • min_files (default 1), min_bytes (default 1).
    • days (default every day): e.g. ["MON","TUE","WED","THU","FRI"].
    • bucket (optional): overrides the bucket input.
  • bucket (STRING, default my-inbound-bucket): default inbound bucket.
  • timezone (STRING, default UTC): IANA zone for deadlines, "today" and weekdays.
  • mode (SELECT check | report, default check).

Quick start

  1. Add the secrets to your namespace.
  2. Replace the example feeds with your own, and set bucket and timezone (also set timezone on the daily_arrival_report trigger).
  3. Run once manually — feeds before their deadline show WAITING, feeds past their deadline with no files alert MISSING.
  4. Upload a test file that matches a feed; the next run posts a recovery message.

S3-compatible storage

Add the endpoint to the plugin defaults, for example for MinIO:

pluginDefaults:
  - type: io.kestra.plugin.aws
    values:
      endpointOverride: https://minio.example.com
      compatibilityMode: true
      forcePathStyle: true

Pitfalls

  • "Today" is based on each object's LastModified in your time zone. Prefixes that hold years of history are listed in full — prefer date-partitioned prefixes ({yyyy}/{MM}/{dd}) or move processed files away.
  • list_files returns at most 1,000 objects per feed (maxFiles).
  • Weekday names in days are English three-letter abbreviations.

Expected outputs

  • outputs.evaluate.values.evaluation (per iteration): JSON such as {"name": "bank_statements", "status": "LATE", "files": 1, "first_arrival": "07:42", "deadline": "07:00", ...}.
  • KV entries file_sla_<feed>_<yyyy-MM-dd> holding the same JSON.
  • Slack alerts on state changes and one daily report.

How to extend

  • Trigger the ingestion flow for a feed as soon as it becomes ON_TIME or LATE with a Subflow task in alert_on_change.
  • Route alerts per feed by adding a channel or webhook field to each feed.
  • Open a ticket (Jira, ServiceNow) or page on-call (PagerDuty, Opsgenie) when a business-critical feed is MISSING.
  • Load the KV states into a warehouse table for monthly SLA reporting per partner.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.