Query language

The filter DSL the API and MCP tools accept, clause by clause.

The REST API and the MCP tools accept a small filter language on their read endpoints. It's deliberately small — enough to ask a precise question, not enough to need its own manual.

The app's own filter bar builds the same queries under the hood, and the same evaluator runs on both sides, so a filter means exactly one thing whether the browser or the server answers it.

Shape

Clauses of the form field op value, joined by and. There is no or, and no parentheses.

category eq 'Groceries' and debit ge 50 and transactionDate ge 20240101

String values are single-quoted. A literal apostrophe inside one is written by doubling it: description contains 'Trader Joe''s'. Numbers are unquoted.

Fields

Field Operators Value
description eq, contains, startswith, endswith quoted string
source eq quoted string, e.g. 'Chase'
category eq, startswith, in quoted string, or a parenthesized list for in
effectiveCategorySource eq 'override', 'rules', or 'none'
rules.status eq 'matched', 'none', or 'conflict'
transactionDate eq, ne, gt, ge, lt, le YYYYMMDD, unquoted
debit eq, ne, gt, ge, lt, le number, unquoted
credit eq, ne, gt, ge, lt, le number, unquoted

category means the transaction's effective category — the override if one is set, otherwise whatever the rules concluded. An uncategorized transaction has the empty category, so category eq '' finds them.

balance is not a queryable field.

Two rules to remember

At most one category clause per query. Two of them would either contradict each other or be redundant, so the parser rejects it rather than guessing. Use in for a set:

category in ('Groceries','Restaurants','Transportation')

There's no size limit on that list — a long one is simply evaluated differently behind the scenes.

Results are capped. A response carrying truncated: true means the cap was reached and there is more matching data than you were given. Narrow the query — usually with a date range — rather than assuming the result is complete.

Examples

Everything you have hand-corrected, which is the list of merchants that probably deserve a rule:

effectiveCategorySource eq 'override'

Transactions no rule covers:

rules.status eq 'none'

Transactions where rules disagree and nothing could be applied:

rules.status eq 'conflict'

Large restaurant charges last year:

category eq 'Restaurants' and debit ge 100 and transactionDate ge 20240101 and transactionDate le 20241231

Everything from one card in one month:

source eq 'Chase' and transactionDate ge 20250301 and transactionDate le 20250331

Anything Amazon-shaped, however it's categorized:

description contains 'AMZN'

An older name

categoryStatus was renamed to rules.status. The old name is rejected with a message pointing at the new one rather than silently ignored.