New to Kestra?
Use blueprints to kickstart your first workflows.
Check Moodle assignments and submissions on a schedule, change nothing, and alert Slack when student work is overdue for marking or the site is unreachable.
Students submit work in Moodle and then wait. Moodle shows each teacher their own "needs grading" count, but nobody sees the whole site: which courses are past their marking deadline, how old the oldest unmarked submission is, or whether the backlog is growing week after week. This blueprint turns two read-only Moodle web service calls into a scheduled marking backlog gate: mod_assign_get_assignments returns every assignment with its "remind me to grade by" date, mod_assign_get_submissions returns every submitted piece of work with its grading status, a single jq query works out what is overdue and by how much, and the result routes to a quiet log, an informational Slack note or a Slack page.
fetch_assignments (io.kestra.plugin.core.http.Request) calls mod_assign_get_assignments and receives every course the token's user is enrolled in, with each assignment's id, name and gradingduedate. This is a GET: nothing is changed. allowFailed: true, a timeout, three retries and allowFailure: true keep a network blip or an outage from aborting the flow.fetch_submissions (io.kestra.plugin.core.http.Request) builds the assignmentids[] list from the first response and calls mod_assign_get_submissions with status=submitted, which returns each submission's timemodified and gradingstatus. A runIf skips it when the assignment list is missing, is a Moodle error, or is empty.classify_backlog (io.kestra.plugin.core.debug.Return) first guards for a missing response, a non-200 code, and a Moodle exception body (Moodle reports an invalid token with HTTP 200), then joins submissions to assignments in one jq query. It keeps submissions that are submitted and not yet marked (notgraded, or notmarked/inmarking when the marking workflow is on), and treats one as overdue once the assignment's gradingduedate has passed, or grace_days after it was submitted when the assignment has no marking deadline. It returns the counts, the oldest overdue age in days, the assignment with the biggest backlog, the number of assignments the token could not read, and the status: no visible assignments or no access to any of them is UNREACHABLE; at least critical_overdue overdue, or one at least critical_age_days late, is CRITICAL; at least warning_overdue overdue, or some assignments unreadable, is WARNING; otherwise HEALTHY.route_by_backlog (io.kestra.plugin.core.flow.Switch) branches on that status: HEALTHY logs a quiet confirmation, WARNING posts an informational Slack note, CRITICAL and UNREACHABLE each page Slack, and a defaults fallback logs any unexpected value.log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run with both HTTP codes and the full classification, independent of which branch fired.errors block alerts Slack separately if the flow fails outright, so a broken sentinel is never mistaken for an empty backlog.weekday_backlog_check trigger runs at 08:00 on weekdays and ships disabled: true, with moodle_url pinned in its inputs.status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on, and a backlog JSON output with assignments, waiting, overdue, oldest_days, no_access and worst.BIO101 / Lab report 3 (4).Moodle's own grading reminders are per teacher and per assignment, and there is no built-in site-wide alert when marking falls behind. Kestra runs the same read-only check on a schedule, keeps every result in the execution history, classifies it in one typed status, and routes it to the right channel, while the Switch branches are ready to grow into real follow-up (a digest per department, a ticket for the course lead) without rewriting the check.
mod/assign:grade to see submissions), and a web service token for an external service that contains only mod_assign_get_assignments and mod_assign_get_submissions.MOODLE_TOKEN: Moodle web service token for the monitoring account.SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.In Kestra OSS (no Enterprise secrets backend), secrets are supplied as environment variables prefixed SECRET_ and base64-encoded, then read in flows with {{ secret('NAME') }}, for example SECRET_MOODLE_TOKEN=$(echo -n '<token>' | base64).
moodlehq/moodle-php-apache image or a PHP and PostgreSQL install), and create a course with a few students and an assignment.MOODLE_TOKEN and SLACK_WEBHOOK as secrets, set moodle_url, and run the flow once manually. The status output reads HEALTHY when nothing is past its marking deadline.WARNING and Slack receives the overdue count.weekday_backlog_check once pointed at the real site.courseids[] to mod_assign_get_assignments to watch one faculty or programme instead of every course the token can see.ForEach.status and overdue in the KV store and alert only when the backlog grows, not on every run.mod_quiz_get_attempts style calls.moodle_url: type STRING, default "https://moodle.example.edu".grace_days: type INT, default 7.warning_overdue: type INT, default 1.critical_overdue: type INT, default 20.critical_age_days: type INT, default 14.status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.backlog: the full classification as JSON, for example {"assignments":4,"waiting":15,"overdue":7,"oldest_days":5,"no_access":0,"worst":"BIO101 / Lab report 3 (4)","status":"WARNING"}.wstoken query parameter, so it is part of the request URL. Keep HTTP request logging off on any proxy in between, and use a dedicated account with the smallest role that can see submissions.exception body. The flow checks for that body explicitly, so these cases report UNREACHABLE instead of an empty, healthy-looking backlog.WARNING; if none can be read, the check is UNREACHABLE.mod_assign_get_submissions returns every submission for every listed assignment in one response. On a very large site, filter by course (see How to extend) to keep the response small.