New to Kestra?
Use blueprints to kickstart your first workflows.
Read Postmark outbound stats every four hours, send nothing, and alert Slack when bounce or spam complaint rates cross your warning and critical lines.
Transactional email breaks quietly. Someone imports an old customer list, a signup form starts accepting typos, or a marketing send goes out on the transactional stream, and the bounce rate jumps. A few days later password resets and receipts land in spam or stop being delivered, because the provider protects its shared reputation by reviewing or pausing accounts with high bounce or complaint rates. This blueprint turns a scheduled, read-only call to the Postmark Stats API into a sending reputation gate: io.kestra.plugin.core.http.Request reads the outbound overview, one jq query checks the bounce and spam complaint rates against your limits, and the result routes to a quiet log or a Slack alert.
get_outbound_stats (io.kestra.plugin.core.http.Request) calls GET /stats/outbound with the server token in X-Postmark-Server-Token, for one messagestream and a fromdate/todate window in US Eastern time (Postmark's stats timezone). It is a read: no email is sent and no bounce is reactivated. Retries cover short network blips, and allowFailed plus allowFailure keep an API error from aborting the flow so it can be reported instead.summarize_reputation (io.kestra.plugin.core.debug.Return) first guards for a missing or non-200 response (UNREACHABLE). Otherwise one jq query reads Sent, Bounced, SMTPApiErrors, BounceRate, SpamComplaints and SpamComplaintsRate. Fewer than min_sent sent is LOW_VOLUME; a bounce or spam complaint rate at or above its critical line is CRITICAL; at or above its warning line is WARNING; otherwise HEALTHY. The summary names which rate tripped.route_by_reputation (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 every_four_hours trigger runs at 40 minutes past every fourth hour, shipped disabled: true.status output (HEALTHY / WARNING / CRITICAL / LOW_VOLUME / UNREACHABLE) other flows can gate on, for example to hold a bulk send or a list import while rates are high.Postmark shows bounce and spam rates on its dashboard and can send a webhook per bounce, but nothing tells your team that the rate crossed a line today. Kestra turns a plain, read-only API call into a scheduled, typed classification with retries, a failure path and an execution history, and the status output lets your sending flows pause themselves before the provider does it for you.
outbound is the default transactional stream).POSTMARK_SERVER_TOKEN: the Server API token of the Postmark server to watch.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_POSTMARK_SERVER_TOKEN=$(echo -n 'your-server-token' | base64), and read back with {{ secret('POSTMARK_SERVER_TOKEN') }}. Use the Enterprise secrets backend for encryption at rest and access control.
POSTMARK_SERVER_TOKEN and SLACK_WEBHOOK as secrets.min_sent and compare the counts and rates in the logs with the stream's statistics page in Postmark.warning_bounce_rate below your current rate to see the WARNING path, then set it back.every_four_hours.GET /bounces?type=HardBounce&count=50&offset=0 to list the addresses behind a spike in the same alert.tag query parameter to judge one email type, such as receipts or password resets.lookback_days: type INT, default 1.message_stream: type STRING, default outbound.warning_bounce_rate: type FLOAT, default 5.0 (percent).critical_bounce_rate: type FLOAT, default 10.0 (percent).warning_spam_rate: type FLOAT, default 0.05 (percent).critical_spam_rate: type FLOAT, default 0.1 (percent).min_sent: type INT, default 200.status: one of HEALTHY, WARNING, CRITICAL, LOW_VOLUME, UNREACHABLE.{{ fromJson(outputs.summarize_reputation.value).reason }} for the rate that tripped.lookback_days: 1 covers yesterday and today, not a rolling 24 hours.UNREACHABLE.