API: rules
List, create, change, test, run and delete rules, and read their run history.
Updated Oct 11, 2026
Rules over the API use exactly the same options and checks as the app. Read Rules reference first; this page shows how those options look as JSON.
Abilities: read to list, view, test and read history. rules to create, change, delete and run.
The rule object
{
"id": 12,
"name": "Patch Tuesday",
"enabled": true,
"description": "Every Tuesday at 2:00 AM, on all sites, apply minor and patch plugin and theme updates (backup first) except woocommerce, skipping ones that need a license. Email me only if something needs me.",
"trigger": { "type": "schedule", "every": "weekly", "time": "02:00", "day_of_week": 2, "day_of_month": 1 },
"scope": { "type": "all" },
"conditions": {
"update_level": "minor",
"include": [],
"exclude": ["woocommerce"],
"skip_needs_license": true,
"window_start": "",
"window_end": ""
},
"actions": {
"backup": false,
"updates": { "plugins": true, "themes": true, "core": false },
"notify": false,
"webhook": false,
"report": { "enabled": false, "period": "last_month", "send_to": "client" }
},
"notify": "problems",
"next_run_at": "2026-10-13T06:00:00+00:00",
"last_run_at": null,
"paused_reason": null
}
| Field | Values |
|---|---|
trigger.type |
schedule or event |
trigger.every |
daily, weekly, monthly, quarterly (schedule; quarterly runs in Jan, Apr, Jul, Oct on day_of_month) |
trigger.time |
HH:MM, 24-hour, in the account’s timezone (schedule) |
trigger.day_of_week |
0 (Sunday) to 6. Weekly only. |
trigger.day_of_month |
1 to 28. Monthly only. |
trigger.event |
backup.failed, update.failed, outside.any, outside.plugin_installed, outside.plugin_removed, outside.admin_added, outside.core_updated, site.disconnected, site.down, site.up, ssl.expiring, performance.dropped, security.found, visual.changed |
trigger.times |
1 to 10. Failures in a row, for backup.failed. |
scope.type |
all, client (with client_id), sites (with site_ids) |
conditions.update_level |
patch, minor (default), any |
conditions.include / exclude |
Arrays of plugin or theme names, folders or slugs. Up to 50. |
conditions.window_start / window_end |
HH:MM or empty |
actions |
At least one of backup, updates.*, notify, webhook, report.enabled must be true. report.period: last_month, last_7, last_30, last_quarter, this_month. report.send_to: client, me, both, none. |
notify |
always, problems (default), never |
next_run_at |
Read only. Null for event rules and switched-off rules. |
paused_reason |
Read only. Set when the rule paused itself after 3 failed runs. |
List and view
GET /rules
GET /rules/{id}
Create
POST /rules
curl -X POST https://app.wpforeman.com/api/v1/rules \
-H "Authorization: Bearer $WPF_TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Client installed a plugin",
"trigger": { "type": "event", "event": "outside.plugin_installed" },
"scope": { "type": "client", "client_id": 4 },
"actions": { "backup": true, "notify": true }
}'
Answers 201 with the rule. Anything you leave out gets the app’s default. Validation errors answer 422 with the field names, for example actions: “Pick at least one thing for the rule to do.”
Change
PATCH /rules/{id}
Send only what changes. Objects are merged; lists (include, exclude, site_ids) are replaced. Send "enabled": false to pause, true to switch back on (this also clears paused_reason).
{ "conditions": { "exclude": ["woocommerce", "elementor"] } }
Test
POST /rules/{id}/test
What the rule would do on each site right now. Changes nothing.
{ "data": [
{ "site": "Acme Plumbing", "would": ["Back up, then update Akismet 5.3.1 → 5.3.2"], "skip": null },
{ "site": "Old Shop", "would": [], "skip": "Not connected" }
] }
Run now
POST /rules/{id}/run
Answers 202 and runs the rule on all its sites. 409 if the rule is switched off.
History
GET /rules/{id}/runs
{ "data": [ {
"id": 88, "trigger": "Scheduled run", "status": "problems",
"sites": [
{ "site_id": 3, "name": "Acme Plumbing", "did": ["Backing up, then updating Akismet 5.3.1 → 5.3.2"] },
{ "site_id": 9, "name": "Old Shop", "did": [], "skipped": "Not connected" }
],
"started_at": "2026-10-13T06:00:04+00:00", "finished_at": "2026-10-13T06:00:09+00:00"
} ],
"meta": { "page": 1, "last_page": 1, "total": 1 } }
status is running, done, problems or failed. Paginated with page and per_page.
Delete
DELETE /rules/{id}
Answers 204. Log entries the rule made are kept.
To hear about runs as they finish, subscribe a webhook to rule.ran and tick Send the rule.ran webhook on the rule ("actions": {"webhook": true}). See Webhooks.
Thanks. If something was missing, tell us what.