---
title: "API: rules"
url: https://wpforeman.com/docs/api-rules/
description: "List, create, change, test, run and delete rules, and read their run history."
updated: 2026-10-11
---

# API: rules

Rules over the API use exactly the same options and checks as the app. Read [Rules reference](https://wpforeman.com/docs/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](https://wpforeman.com/docs/api-webhooks/).

---

WP Foreman: WordPress site management for agencies and freelancers. First site free, no card: https://app.wpforeman.com/signup
