Scheduler
Manage Grav's cron-based scheduler. List registered jobs, inspect cron installation status, review execution history, and trigger manual runs. Permissions: api.scheduler.read / api.scheduler.write.
Manage Grav's cron-based scheduler. List registered jobs, inspect cron installation status, review execution history, and trigger manual runs.
Permissions: api.scheduler.read / api.scheduler.write.
List Scheduler Jobs
/scheduler/jobs
{"data": [{"id": "default-site-backup", "command": "Grav\\Common\\Backup\\Backups::backup", "expression": "0 3 * * *", "enabled": true, "status": "success", "last_run": "2026-03-26T03:00:00+00:00", "last_run_trigger": "cron", "next_run": "2026-03-27T03:00:00+00:00", "overdue": false, "error": null}]}
Response Codes
Scheduler Status
/scheduler/status
{"data": {"crontab_status": "installed", "cron_detection": "crontab", "process_available": true, "last_run": "2026-08-20T12:00:00+00:00", "last_manual_run": null, "environment": "www.example.com", "environment_has_overrides": true, "last_run_environment": "cli", "cron_command": "* * * * * cd /path/to/grav && /usr/bin/php bin/grav scheduler", "scheduler_command": "bin/grav scheduler", "whoami": "www-data", "health": {}, "triggers": [], "webhook_installed": false, "webhook_enabled": false}}
Response Codes
crontab_status is one of: not_installed, installed, unknown. cron_detection names how that was worked out, or unavailable when detection failed. process_available is false when PHP's proc_open is disabled, in which case POST /scheduler/run returns 501. last_run is the last run by a real trigger (cron or webhook); last_manual_run is the last run started from the admin or the API, which never counts as proof that cron is working.
environment is the environment this request resolved to (normally the hostname), environment_has_overrides says whether it has its own user/env/<name>/config folder, and last_run_environment is the environment the scheduler last actually ran under (cli for a crontab line that does not pass --env). When the last two disagree and overrides exist, jobs defined only in the override never run from that trigger; scheduler_command already carries the --env flag to fix the crontab line.
Job History
/scheduler/history
Parameters
| Name | Type | Description |
|---|---|---|
| page optional | integer | Page number (default 1). |
| per_page optional | integer | Items per page (default `plugins.api.pagination.default_per_page`, 20). |
{"data": [{"job_id": "default-site-backup", "status": "success", "last_run": "2026-03-26T03:00:00+00:00", "error": null}], "meta": {"pagination": {"page": 1, "per_page": 20, "total": 12, "total_pages": 1}}, "links": {"self": "/api/v1/scheduler/history?page=1&per_page=20"}}
Response Codes
Run Scheduler
/scheduler/run
Parameters
| Name | Type | Description |
|---|---|---|
| mode optional | string | `overdue` (default) runs the jobs that missed their last scheduled slot, `due` runs only what a cron tick this minute would run, `all` runs every enabled job whatever its schedule says. |
| job optional | string | Run only this job id, whatever its schedule says. Ignores `mode`, and the response reports `mode: job`. |
| force optional | boolean | Legacy alias for `mode: all`. |
{"mode": "overdue"}
{"data": {"message": "1 job ran successfully.", "mode": "overdue", "forced": false, "jobs_run": 1, "jobs_failed": 0, "duration": 1.42, "results": [{"id": "default-site-backup", "successful": true, "output": ""}], "job_states": {"default-site-backup": {"state": "success", "last-run": 1790000000, "trigger": "manual"}}}}