---
title: "openemail rules"
description: "Every command in this namespace, with its arguments, flags and examples."
url: "https://openemail.uk/docs/cli/reference/rules"
area: "CLI"
category: "Reference"
---

# openemail rules

Every command in this namespace, with its arguments, flags and examples.

## Commands

### `openemail rules list`

List mail rules in evaluation order

```bash
openemail rules list [flags]
```

Returns one page of the mailbox's rules in the order they run on arriving mail: ascending `position`, then `createdAt`, then `id`. This is deliberately not newest first. With `stopProcessing` in play, the same rules in a different order file mail differently, so the order you read is the order that matters.

Paging is keyset on that same `(position, createdAt, id)` tuple, and the cursor is opaque: it holds where the last rule on the page sat in that order. `--enabled` narrows to enabled or disabled rules, and the SDK sends it as the literal word `true` or `false`.

Add `--all` to walk every page: a table on a terminal, one JSON object per line when piped or with `--ndjson`, and one `{ items, hasMore, nextCursor }` document with `--json`. `--max <n>` stops after that many items.

- Scopes: `rules:read`.
- Needs a sign-in.
- Aliases: `ls`.

**Flags**

- `--enabled`: Restricts the page to enabled (`true`) or disabled (`false`) rules. Omit it for both.
- `--limit <n>` (default `25`): Rows per page, a whole number from 1 to 100. The server defaults to 25.
- `--cursor <value>`: The `nextCursor` from the previous page. Never build one yourself.
- `--all`: Fetch every page and stream the items as they arrive.
- `--max <n>`: Stop after this many items. Implies `--all`.
- `--ndjson`: Print every item as one JSON object per line. Implies `--all`

**Examples**

```bash
openemail rules list
```

With optional flags

```bash
openemail rules list --enabled --limit 100
```

Walk every page and stop after 100 items

```bash
openemail rules list --all --max 100
```

One JSON object per line when piped

```bash
openemail rules list --all > rules.ndjson
```

Also available in: API [`GET /rules`](https://openemail.uk/docs/api/reference/rules#get-rules); SDK [`rules.list()`](https://openemail.uk/docs/sdk/reference/rules#list).

### `openemail rules get`

Read one mail rule

```bash
openemail rules get <id> [flags]
```

Fetches a single rule by its `rul_` id. Rules have no slug and the name is editable, so store the id if your code needs to find the rule again.

The stored `conditions` are normalised, so `negate` is always present and false unless you set it. `lastMatchedAt` and `matchCount` move each time the rule fires on an arriving message, which makes them the quickest way to see whether a rule is doing anything without reading `listRuns`.

- Scopes: `rules:read`.
- Needs a sign-in.
- Aliases: `show`, `view`.

**Arguments**

- `<id>` (required): The rule's `rul_` id.

**Examples**

```bash
openemail rules get rul_4f1c9a2b7d3e8f6a0b5c1d2e
```

Print the raw JSON

```bash
openemail rules get rul_4f1c9a2b7d3e8f6a0b5c1d2e --json
```

Also available in: API [`GET /rules/{id}`](https://openemail.uk/docs/api/reference/rules#get-rules-id); SDK [`rules.get()`](https://openemail.uk/docs/sdk/reference/rules#get).

### `openemail rules create`

Create a mail rule at the end of the order

```bash
openemail rules create --name <value> --conditions <json|@file|-> --actions <json|@file|-> [flags]
openemail rules create --data <json|@file|-> [flags]
```

Creates a rule that runs on mail arriving in the mailbox, with no API call involved once it exists. It is appended after every existing rule and is enabled unless you pass `enabled: false`. There is no `position` on create: move the rule afterwards with `reorder`, the only call that guarantees an exact sequence. Creating it disabled and calling `test` first is the safe order.

Conditions are a flat list. `match: 'all'` is AND, `match: 'any'` is OR, and `negate: true` inverts a single condition, so `(A and B) or C` takes two rules. `value` is always a string. `has_attachment` and `spam` take only `equals` with `'true'` or `'false'`. `attachment_size`, `message_size`, `hour` and `weekday` take a number with `gt`, `lt` or `equals`, and `gt` or `lt` on any other field is refused. Text comparisons ignore case, and `matches` is a whole value glob over `*` and `?` that needs at least two letters or digits. A `header` condition must name the header it reads in `header`.

Actions apply in order. `label` and `remove_label` take a label id, `forward` takes an email address and `reply` takes a template id or slug. A forward is only delivered once the destination has confirmed it accepts mail forwarded from your domain, and a reply goes to each sender at most once per 24 hours and never to bounces, auto-responders or mailing lists. A rule with `reject` must also test `envelope_from`, otherwise it is a 422 `reject_needs_envelope`, because refusing mail on the strength of a `From` header bounces the mailing list rather than the author.

- Scopes: `rules:write`.
- Needs a sign-in.
- Aliases: `new`, `add`.

**Flags**

- `--name <value>`: Display name, 1 to 100 characters after trimming and unique per mailbox. Required, here or in `--data`.
- `--description <value>`: Free text note, at most 500 characters.
- `--enabled` (default `true`): Whether the rule runs on arriving mail. Defaults to true.
- `--match <value>` (default `"all"`): Whether every condition or any single one must hold. Defaults to `all`.
- `--conditions <json|@file|->`: 1 to 20 conditions, each `{ field, op, value }` with optional `header` (at most 128 characters) and `negate`. `value` is at most 512 characters. JSON shaped as `Array<RuleConditionInput>`, inline or from a file with @path. Required, here or in `--data`.
- `--actions <json|@file|->`: 1 to 10 actions, each `{ type, value }`, with `value` at most 320 characters. JSON shaped as `Array<RuleAction>`, inline or from a file with @path. Required, here or in `--data`.
- `--stop-processing` (default `false`): When true, no later rule runs on a message this rule matched. Defaults to false.
- `--data <json|@file|->`: The whole `body` as JSON, inline, from a file with @path, or - for standard input. Flags override its keys.

**Examples**

The required values only

```bash
openemail rules create --name 'Receipts to their own label' --conditions @conditions.json --actions @actions.json
```

With optional flags

```bash
openemail rules create --name 'Receipts to their own label' --conditions @conditions.json --actions @actions.json --no-enabled --stop-processing
```

Read the whole body from a JSON file

```bash
openemail rules create --data @rule.json
```

Also available in: API [`POST /rules`](https://openemail.uk/docs/api/reference/rules#post-rules); SDK [`rules.create()`](https://openemail.uk/docs/sdk/reference/rules#create).

### `openemail rules update`

Change a rule, replacing conditions or actions whole

```bash
openemail rules update <id> [flags]
```

Merges the patch onto the stored rule and validates the whole result, so an omitted field keeps its stored value instead of collecting a default. A patch that only renames a rule cannot switch a disabled one back on, and a patch that adds a `reject` action is checked against the conditions already stored.

`conditions` and `actions` replace the entire array. There is no way to add or remove a single element: read the rule, change the array, and send all of it. `match` and `--stop-processing` read across the whole set, which is why element level edits are not offered.

`position` moves this one rule without renumbering the others. Positions are not unique and ties break by `createdAt` then `id`, so landing on an occupied position puts the older rule first. Use `reorder` when the exact sequence matters. Setting `enabled: false` is the reversible alternative to `delete`, and a disabled rule keeps its place in the order.

- Scopes: `rules:write`.
- Needs a sign-in.
- Aliases: `edit`.

**Arguments**

- `<id>` (required): The rule's `rul_` id.

**Flags**

- `--name <value>`: Replacement name, 1 to 100 characters and unique per mailbox.
- `--description <value>`: Replacement note of at most 500 characters, or null to clear it.
- `--enabled` (default `true`): Turns the rule on or off without moving it.
- `--match <value>` (default `"all"`): Whether every condition or any single one must hold.
- `--conditions <json|@file|->`: Complete replacement list of 1 to 20 conditions. JSON shaped as `Array<RuleConditionInput>`, inline or from a file with @path.
- `--actions <json|@file|->`: Complete replacement list of 1 to 10 actions. JSON shaped as `Array<RuleAction>`, inline or from a file with @path.
- `--stop-processing` (default `false`): When true, no later rule runs on a message this rule matched.
- `--position <n>`: New position, a whole number from 0 to 1,000,000. Other rules keep theirs.
- `--data <json|@file|->`: The whole `patch` as JSON, inline, from a file with @path, or - for standard input. Flags override its keys.

**Examples**

With optional flags

```bash
openemail rules update rul_4f1c9a2b7d3e8f6a0b5c1d2e --enabled
```

Print the raw JSON

```bash
openemail rules update rul_4f1c9a2b7d3e8f6a0b5c1d2e --enabled --json
```

Also available in: API [`PATCH /rules/{id}`](https://openemail.uk/docs/api/reference/rules#patch-rules-id); SDK [`rules.update()`](https://openemail.uk/docs/sdk/reference/rules#update).

### `openemail rules delete`

Delete a mail rule

```bash
openemail rules delete <id> [flags]
```

Permanently removes a rule. There is no undo, and mail it already filed stays where it put it. If you might want the rule back, `update(id, { enabled: false })` switches it off and keeps its place in the order.

The run history survives. `listRuns({ ruleId })` still returns what the rule did, under the name it had at the time. The other rules are not renumbered, so a gap in `position` is normal afterwards.

- Scopes: `rules:write`.
- Needs a sign-in.
- Asks you to confirm.
- Aliases: `rm`, `del`, `remove`.

**Arguments**

- `<id>` (required): The rule's `rul_` id.

**Examples**

```bash
openemail rules delete rul_4f1c9a2b7d3e8f6a0b5c1d2e
```

Skip the confirmation, for scripts

```bash
openemail rules delete rul_4f1c9a2b7d3e8f6a0b5c1d2e --yes
```

Also available in: API [`DELETE /rules/{id}`](https://openemail.uk/docs/api/reference/rules#delete-rules-id); SDK [`rules.delete()`](https://openemail.uk/docs/sdk/reference/rules#delete).

### `openemail rules reorder`

Set the evaluation order of every rule

```bash
openemail rules reorder <rule-ids...> [flags]
```

Replaces the whole order in one transaction. `ruleIds` must name every rule on the mailbox exactly once, and each rule's `position` becomes its index in the array, starting at 0. Either the whole order lands or nothing moves.

A list that leaves a rule out or names one twice is a 422 `incomplete_order`, because an omitted rule would have to go somewhere and there is no right answer for where. Build the list from `listAll()` without the `enabled` filter so disabled rules are included. The new order applies to the next message that arrives, and mail already filed is not revisited.

- Scopes: `rules:write`.
- Needs a sign-in.

**Arguments**

- `<rule-ids...>` (required): Every rule id on the mailbox in the order they should run, 1 to 100 entries.

**Examples**

```bash
openemail rules reorder rul_4f1c9a2b7d3e8f6a0b5c1d2e
```

Print the raw JSON

```bash
openemail rules reorder rul_4f1c9a2b7d3e8f6a0b5c1d2e --json
```

Also available in: API [`POST /rules/reorder`](https://openemail.uk/docs/api/reference/rules#post-rules-reorder); SDK [`rules.reorder()`](https://openemail.uk/docs/sdk/reference/rules#reorder).

### `openemail rules test`

Dry run a rule against mail already in the mailbox

```bash
openemail rules test <id> [flags]
```

Evaluates one rule against stored mail with the same matcher delivery uses and reports what it would have caught and what it would do. It changes nothing, sends nothing and files nothing, which is why it needs only `rules:read`. It works on a disabled rule, so the intended sequence is create with `enabled: false`, test, then enable.

By default it reads up to 50 inbox threads from the last 30 days. For each thread it tests the newest message the mailbox received rather than sent, and threads that fail to load or hold only your own messages are skipped, so `scanned` can come in below `limit`. Pass `--thread-ids` to test specific threads from any folder instead, and `days` and `limit` are then ignored. A message an existing rule already moved out of the inbox is not in the default sample, and `listRuns` is the record of what really happened.

Read `warnings` before `matched`. `field_unevaluable` means a condition reads something stored mail no longer carries (`envelope_from`, `header`, `list_id`, `delivered_to` or `message_size`), so it never held here, and under `negate` it holds on everything. `field_approximate` flags attachment, `hour` and `weekday` conditions answered from stored data that can differ from delivery. `body_encrypted` means a `body` condition met mail whose body is sealed. `forward_loop` means a forward target leads back to this mailbox, and `forward_unverified` means the target is not hosted here, so the forwarded copy is rebuilt and loses its original DKIM signature.

- Scopes: `rules:read`.
- Needs a sign-in.

**Arguments**

- `<id>` (required): The rule's `rul_` id. Disabled rules can be tested.

**Flags**

- `--thread-ids <a,b>` (repeatable): Up to 50 specific thread ids to test instead of the recent window. Ids that fail to load are skipped.
- `--days <n>` (default `30`): How far back the default window reaches, 1 to 365. Defaults to 30.
- `--limit <n>` (default `50`): How many recent inbox threads to read, 1 to 200. Defaults to 50.
- `--data <json|@file|->`: The whole `body` as JSON, inline, from a file with @path, or - for standard input. Flags override its keys.

**Examples**

The required values only

```bash
openemail rules test rul_4f1c9a2b7d3e8f6a0b5c1d2e
```

With optional flags

```bash
openemail rules test rul_4f1c9a2b7d3e8f6a0b5c1d2e --days 30 --limit 100
```

Also available in: API [`POST /rules/{id}/test`](https://openemail.uk/docs/api/reference/rules#post-rules-id-test); SDK [`rules.test()`](https://openemail.uk/docs/sdk/reference/rules#test).

### `openemail rules list-runs`

List what rules actually did to arriving mail

```bash
openemail rules list-runs [flags]
```

Returns one page of the rule audit log, newest first. Each row is one rule matching one arriving message, with the actions that took effect and the ones that were refused.

Each row copies the rule's name at the moment it fired, so renamed and deleted rules still read correctly, and `--rule-id` works for a rule that no longer exists. `--thread-id` answers the other common question: why a particular message ended up where it did.

`actions` lists the action types that were applied, and `failures` lists refusals as `type: reason`. A non empty `failures` means the rule matched but the mailbox declined part of it, for example a `reply` suppressed because that sender was already answered in the last day, a `forward` to an address that has not confirmed, or a `reject` that could not be refused at SMTP and was filed as spam instead.

Add `--all` to walk every page: a table on a terminal, one JSON object per line when piped or with `--ndjson`, and one `{ items, hasMore, nextCursor }` document with `--json`. `--max <n>` stops after that many items.

- Scopes: `rules:read`.
- Needs a sign-in.

**Flags**

- `--rule-id <value>`: Only runs of this rule, including a rule that has since been deleted.
- `--thread-id <value>`: Only runs recorded against this thread.
- `--limit <n>` (default `25`): Rows per page, a whole number from 1 to 100. The server defaults to 25.
- `--cursor <value>`: The `nextCursor` from the previous page. Never build one yourself.
- `--all`: Fetch every page and stream the items as they arrive.
- `--max <n>`: Stop after this many items. Implies `--all`.
- `--ndjson`: Print every item as one JSON object per line. Implies `--all`

**Examples**

```bash
openemail rules list-runs
```

With optional flags

```bash
openemail rules list-runs --rule-id rul_4f1c9a2b7d3e8f6a0b5c1d2e --limit 50
```

Walk every page and stop after 100 items

```bash
openemail rules list-runs --all --max 100
```

One JSON object per line when piped

```bash
openemail rules list-runs --all > rules.ndjson
```

Also available in: API [`GET /rules/runs`](https://openemail.uk/docs/api/reference/rules#get-rules-runs); SDK [`rules.listRuns()`](https://openemail.uk/docs/sdk/reference/rules#listRuns).
