Skip to main content
Transaction rules are condition trees that fire during sync to auto-categorize, tag, or annotate new transactions. See Rules (read) for list_transaction_rules and preview_rule, and Categorization for apply_rules (retroactive application). The full DSL grammar is available via get_reference(kind="rule-dsl"). Key concepts recapped below under Pipeline stages. 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

rules
array of objects
required
Array of rule specs, 1–100 entries. Each spec follows the shape below.

Rule spec shape

name
string
required
Human-readable description. Convention: "<pattern-type>: <match> → <category>".
conditions
object
Condition tree. Omit or pass {} to match every transaction. See Condition grammar.
actions
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.
category_slug
string
Shorthand for [{"type": "set_category", "category_slug": "<slug>"}]. Either actions or category_slug is required.
trigger
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.
stage
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.
priority
integer
default:"10"
Raw pipeline-stage integer, 0–1000. Lower runs first. Prefer stage for shared vocabulary.
expires_in
string
Optional expiry duration: 24h, 30d, 1w. Rule auto-disables after this period.
apply_retroactively
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).

Example — single rule

{
  "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:
{
  "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

{
  "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

id
string
required
Rule UUID or short ID.
name
string
conditions
object
New condition tree. Pass {} to explicitly change to match-all. Omit entirely to leave conditions unchanged.
actions
array of objects
Replace the entire actions array. Pass an empty array to reject (rules must have at least one action).
category_slug
string
Shorthand: replace only the set_category action. Other action types on the rule are preserved.
trigger
string
stage
string
priority
integer
enabled
boolean
Disabled rules are excluded from sync and retroactive apply.
expires_at
string
RFC3339 timestamp, or empty string to clear expiry.

Example input

{
  "id": "r9Xm2pQr",
  "enabled": false
}

Example output

{
  "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

id
string
required
Rule UUID or short ID.

Example input

{
  "id": "r9Xm2pQr"
}

Example output

{
  "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.
StagePriorityTypical use
baseline0Broad defaults — tagging coffee shops, marking credit card payments
standard10 (default)Most categorization rules
refinement50Rules that react to earlier-stage tags or categories
override100Rules 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.
  • Fieldsname, 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.
  • Combinatorsand, or, not (nest freely, max depth 10).
Nested example:
{
  "or": [
    {
      "and": [
        { "field": "merchant_name", "op": "contains", "value": "starbucks" },
        { "field": "amount", "op": "gte", "value": 5 }
      ]
    },
    { "field": "tags", "op": "contains", "value": "coffee" }
  ]
}
Last modified on June 25, 2026