> ## Documentation Index
> Fetch the complete documentation index at: https://breadbox-mintlify-7401d007.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Rule write MCP reference

> Create (single or batch), update, and delete transaction rules. Apply rules retroactively.

Transaction rules are condition trees that fire during sync to auto-categorize, tag, or annotate new transactions. See [Rules (read)](/mcp/reference/rules) for `list_transaction_rules` and `preview_rule`, and [Categorization](/mcp/reference/categorization#apply_rules) for `apply_rules` (retroactive application).

The full DSL grammar is available via `get_reference(kind="rule-dsl")`. Key concepts recapped below under [Pipeline stages](#pipeline-stages-and-priority).

All tools on this page are **Write** scope.

***

## create\_transaction\_rule

Create one or more transaction rules in a single call. Pass a `rules` array of 1–100 rule specs — a single rule is just a one-element array. Rules match condition trees against transactions during sync and fire in pipeline-stage order (priority ASC — lower runs first). Set `apply_retroactively: true` on any item to immediately back-fill it against existing transactions.

This tool absorbs the retired `batch_create_rules` — there is no separate batch tool. Earlier-stage rules' tag and category mutations feed later-stage rules' conditions in the same pass, so composable pipelines (rule A tags `coffee` → rule B reads that tag → rule C sets a category) should be authored as a single multi-rule call.

### Parameters

<ParamField path="rules" type="array of objects" required>
  Array of rule specs, 1–100 entries. Each spec follows the shape below.
</ParamField>

### Rule spec shape

<ParamField path="name" type="string" required>
  Human-readable description. Convention: `"<pattern-type>: <match> → <category>"`.
</ParamField>

<ParamField path="conditions" type="object">
  Condition tree. Omit or pass `{}` to match every transaction. See [Condition grammar](#condition-grammar).
</ParamField>

<ParamField path="actions" type="array of objects">
  Array of typed actions. Shapes:

  * `{"type": "set_category", "category_slug": "..."}`
  * `{"type": "add_tag", "tag_slug": "..."}`
  * `{"type": "remove_tag", "tag_slug": "..."}`
  * `{"type": "add_comment", "content": "..."}`

  Actions compose — a rule can set a category, add a tag, and add a comment on the same match. `add_comment` fires only at sync time (not on retroactive apply). If omitted, supply `category_slug` as a shorthand.
</ParamField>

<ParamField path="category_slug" type="string">
  Shorthand for `[{"type": "set_category", "category_slug": "<slug>"}]`. Either `actions` or `category_slug` is required.
</ParamField>

<ParamField path="trigger" type="string" default="on_create">
  When the rule fires: `on_create` (default — first-synced transactions), `on_change` (existing transactions that change on re-sync), or `always` (both). `on_update` is accepted as a legacy alias for `on_change`. Retroactive apply ignores `trigger`.
</ParamField>

<ParamField path="stage" type="string">
  Semantic pipeline stage. Preferred over raw priority. One of `baseline` (priority 0), `standard` (10, default), `refinement` (50), `override` (100). If both `stage` and `priority` are supplied, `priority` wins.
</ParamField>

<ParamField path="priority" type="integer" default="10">
  Raw pipeline-stage integer, 0–1000. Lower runs first. Prefer `stage` for shared vocabulary.
</ParamField>

<ParamField path="expires_in" type="string">
  Optional expiry duration: `24h`, `30d`, `1w`. Rule auto-disables after this period.
</ParamField>

<ParamField path="apply_retroactively" type="boolean" default="false">
  If `true`, immediately apply this rule to existing transactions after creation. Materializes `set_category`, `add_tag`, `remove_tag`; skips `add_comment` (sync-only).
</ParamField>

### Example — single rule

```json theme={null}
{
  "rules": [
    {
      "name": "name: Starbucks → food_and_drink_coffee",
      "conditions": {
        "field": "merchant_name",
        "op": "contains",
        "value": "starbucks"
      },
      "actions": [
        { "type": "set_category", "category_slug": "food_and_drink_coffee" }
      ],
      "stage": "standard",
      "trigger": "on_create"
    }
  ]
}
```

### Example — composable pipeline

Earlier stages' tag and category mutations are visible to later stages in the same sync pass, so a three-rule pipeline can be authored atomically:

```json theme={null}
{
  "rules": [
    {
      "name": "Tag coffee shops",
      "stage": "baseline",
      "conditions": {
        "field": "merchant_name",
        "op": "contains",
        "value": "starbucks"
      },
      "actions": [{ "type": "add_tag", "tag_slug": "coffee" }]
    },
    {
      "name": "Categorize coffee-tagged",
      "stage": "standard",
      "conditions": { "field": "tags", "op": "contains", "value": "coffee" },
      "actions": [
        { "type": "set_category", "category_slug": "food_and_drink_coffee" }
      ]
    },
    {
      "name": "Flag expensive coffee",
      "stage": "refinement",
      "conditions": {
        "and": [
          { "field": "tags", "op": "contains", "value": "coffee" },
          { "field": "amount", "op": "gt", "value": 15 }
        ]
      },
      "actions": [{ "type": "add_tag", "tag_slug": "expensive" }]
    }
  ]
}
```

### Example output

```json theme={null}
{
  "created": 3,
  "failed": 0,
  "rules": [
    { "id": "r9Xm2pQr", "name": "Tag coffee shops", "priority": 0 },
    { "id": "rAYn0qR3", "name": "Categorize coffee-tagged", "priority": 10 },
    { "id": "rBZo1rS4", "name": "Flag expensive coffee", "priority": 50 }
  ],
  "errors": []
}
```

The response returns the created rules plus any per-item errors so a partial batch is recoverable. If `apply_retroactively: true` was set on a rule, that rule's entry also carries `retroactive_matches: <count>` (or `retroactive_error: "<msg>"` on failure).

***

## update\_transaction\_rule

Update one rule. Every field is optional — omit to leave unchanged. `conditions={}` explicitly clears conditions (match-all). `actions=[...]` replaces the entire action set. `expires_at=""` clears expiry.

### Parameters

<ParamField path="id" type="string" required>
  Rule UUID or short ID.
</ParamField>

<ParamField path="name" type="string" />

<ParamField path="conditions" type="object">
  New condition tree. Pass `{}` to explicitly change to match-all. Omit entirely to leave conditions unchanged.
</ParamField>

<ParamField path="actions" type="array of objects">
  Replace the entire actions array. Pass an empty array to reject (rules must have at least one action).
</ParamField>

<ParamField path="category_slug" type="string">
  Shorthand: replace only the `set_category` action. Other action types on the rule are preserved.
</ParamField>

<ParamField path="trigger" type="string" />

<ParamField path="stage" type="string" />

<ParamField path="priority" type="integer" />

<ParamField path="enabled" type="boolean">
  Disabled rules are excluded from sync and retroactive apply.
</ParamField>

<ParamField path="expires_at" type="string">
  RFC3339 timestamp, or empty string to clear expiry.
</ParamField>

### Example input

```json theme={null}
{
  "id": "r9Xm2pQr",
  "enabled": false
}
```

### Example output

```json theme={null}
{
  "id": "r9Xm2pQr",
  "name": "name: Starbucks → food_and_drink_coffee",
  "enabled": false,
  "priority": 10,
  "hit_count": 47,
  "last_hit_at": "2026-04-12T14:32:00Z"
}
```

***

## delete\_transaction\_rule

Delete a rule by ID. System-seeded rules (the `needs-review` auto-tagger) cannot be deleted — disable them via `update_transaction_rule` instead.

### Parameters

<ParamField path="id" type="string" required>
  Rule UUID or short ID.
</ParamField>

### Example input

```json theme={null}
{
  "id": "r9Xm2pQr"
}
```

### Example output

```json theme={null}
{
  "deleted": true,
  "id": "r9Xm2pQr"
}
```

***

## Pipeline stages and priority

Rules fire in priority-ASC order during each sync pass, and within a single pass each rule observes mutations from earlier-stage rules. That makes rules composable — rule A can add a tag, rule B's condition can react to that tag, rule C can set a category based on the combined state.

| Stage        | Priority     | Typical use                                                                  |
| ------------ | ------------ | ---------------------------------------------------------------------------- |
| `baseline`   | 0            | Broad defaults — tagging coffee shops, marking credit card payments          |
| `standard`   | 10 (default) | Most categorization rules                                                    |
| `refinement` | 50           | Rules that react to earlier-stage tags or categories                         |
| `override`   | 100          | Rules that forcibly win `set_category` regardless of what earlier stages did |

Rules of thumb:

* Per-merchant rules (priority 20–30 or `refinement`) > name-pattern rules (`standard`) > `category_primary` rules (`baseline`).
* Prefer `contains` over exact match — bank feeds format merchant names inconsistently.
* Always use `category_slug`, not `category_id`, when authoring actions or filters.

## Condition grammar

Same grammar used by [`preview_rule`](/mcp/reference/rules#preview_rule).

* **Fields** — `name`, `merchant_name`, `amount`, `category_primary`, `category_detailed`, `category` (assigned slug, live-updated by earlier-stage rules), `pending`, `provider`, `account_id`, `account_name`, `user_id`, `user_name`, `tags`.
* **Operators** —
  * String/category: `eq`, `neq`, `contains`, `not_contains`, `matches` (RE2), `in`.
  * Numeric: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`.
  * Bool: `eq`, `neq`.
  * Tags: `contains`, `not_contains`, `in`.
* **Combinators** — `and`, `or`, `not` (nest freely, max depth 10).

Nested example:

```json theme={null}
{
  "or": [
    {
      "and": [
        { "field": "merchant_name", "op": "contains", "value": "starbucks" },
        { "field": "amount", "op": "gte", "value": 5 }
      ]
    },
    { "field": "tags", "op": "contains", "value": "coffee" }
  ]
}
```
