New to Kestra?
Use blueprints to kickstart your first workflows.
List recent OpenAI Batch API jobs every hour, change nothing, and alert Slack when a batch fails, expires or nears its 24-hour window unfinished.
The OpenAI Batch API runs large jobs (embeddings for a whole catalog, nightly classification, evaluation sets) at half price, in exchange for a completion window of 24 hours. That trade has quiet failure modes: a batch fails validation because one line of the input file is malformed, a busy period means it is still in_progress when the window closes and it ends expired with only part of the results, or it completes but a share of its requests failed. The job that was waiting for the output file just finds nothing, often the next morning. This blueprint turns a scheduled, read-only call to the Batch API into a batch health gate: io.kestra.plugin.core.http.Request lists recent batches, one jq query finds failed, expired and at-risk batches and the failed-request share, and the result routes to a quiet log or a Slack alert.
list_batches (io.kestra.plugin.core.http.Request) calls GET /v1/batches?limit=100 with a bearer API key. It is a read: no batch is created, canceled or changed, and no tokens are spent. Retries cover short network blips, and allowFailed plus allowFailure keep an API error from aborting the flow so it can be reported instead.summarize_batches (io.kestra.plugin.core.debug.Return) first guards for a missing or non-200 response (UNREACHABLE). Otherwise one jq query keeps batches whose created_at falls in the lookback window and lists those with status failed or expired (with the first validation error message), and those still validating, in_progress or finalizing with less than expiry_warning_hours left before expires_at. It adds up request_counts.total and request_counts.failed across completed batches. Any failed or expired batch, or a failed-request share at or above critical_failed_request_pct, is CRITICAL; an at-risk batch or a share at or above warning_failed_request_pct is WARNING; no batch in the window is LOW_VOLUME; otherwise HEALTHY.route_by_batch_health (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 10 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 downstream load until the batch results are complete, or to resubmit the failed lines.expired batch the morning after.The OpenAI platform shows batches in a list, but nothing tells you a batch is about to miss its window or that 8% of its lines failed. 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 the flows that consume batch results wait, retry or resubmit automatically.
OPENAI_API_KEY: an API key (a project key is enough) for the project whose batches you want 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_OPENAI_API_KEY=$(echo -n 'sk-proj-xxxxxxxx' | base64), and read back with {{ secret('OPENAI_API_KEY') }}. Use the Enterprise secrets backend for encryption at rest and access control.
/v1/chat/completions or /v1/embeddings), plus one with a malformed input file to produce a failed batch.OPENAI_API_KEY and SLACK_WEBHOOK as secrets.hourly_check.has_more and last_id with the after parameter when you create more than 100 batches in the window.metadata (for example a job key you set at submission) to give each pipeline its own limits.error_file_id with the Files API and attach the first failing lines to the Slack message.lookback_hours: type INT, default 48.expiry_warning_hours: type INT, default 4.warning_failed_request_pct: type FLOAT, default 2.0 (percent).critical_failed_request_pct: type FLOAT, default 10.0 (percent).status: one of HEALTHY, WARNING, CRITICAL, LOW_VOLUME, UNREACHABLE.{{ fromJson(outputs.summarize_batches.value).dead }} for the failed or expired batches.after if you create more.cancelled batch is counted in the window but is not an alert, because it was canceled on purpose.request_counts.failed covers requests that returned an error; a request that succeeded with an unhelpful answer is not a failure here.