MCP App Store
by KeenEthics
Wallet by BudgetBakers icon

Wallet by BudgetBakers

By JAN MULLER

Get New App Alerts

Overview

Wallet by BudgetBakers helps users review transactions, accounts, categories, labels, budgets, and related financial data, analyze spending, and create, update, or delete bookkeeping data through ChatGPT.

Tools

create_account

ChatGPT
Create a new account in the user's Wallet app — a bookkeeping container for records (cash, current, savings, etc.). It does not open or connect a real bank account. Required: - name: Account display name (e.g., "Main Checking"). NOT unique — duplicates are allowed (matching the mobile apps); the account id is the only identity, so never resolve accounts by name. - accountType: One of: General, Cash, CurrentAccount, SavingAccount, or Insurance. (See the accountType enum + field description for the creatable set; other types are rejected.) - currencyCode: Any ISO-4217 fiat currency code (e.g., "USD", "EUR") — not limited to usedCurrencies in get_client_profile - initialBalance: Number (e.g., 1000.50). Input only — read it back as balance.initial (with balance.currencyCode alongside). Optional: - color: Hex color code from the 16 universal colors (e.g. "#1565c0", "#d32f2f"). Randomly assigned if omitted. - excludeFromStats: Exclude from statistics. Default: false - bankAccountNumber: Optional free-text reference for the account (max 80 chars), such as a bank account number, an IBAN or a short note the user gives. Include it only when the user explicitly provides it; never ask for it. A masked value containing "" (as get_accounts returns) is rejected. A non-base currency is added to the user's currencies automatically. Defaults: color=random from 16 universal colors, excludeFromStats=false, archived=false, isBankSync=false. Response:* {summary: {total, succeeded, clientErrors, serverErrors}, results: [{inputIndex, id?, success, error?, errorType?, account?}]} — id is the created account's id at the result's top level (success only), so you can chain from results[0].id without reaching into the nested account. inputIndex is always 0 on this single-item tool; it exists so the same correlation key works across single- and multi-item write tools. On success: results[0] has success=true + full account object (same fields as get_accounts). On failure: success=false + error string + errorType. Single-item tool — the batch envelope is for response-shape consistency; total is always 1.

create_budget

ChatGPT
Create, add, or set up a new budget for tracking spending or income. How the resulting spending numbers are produced — periods, effectiveLimit, income netting, exclusions — is the budgets guide (`get_client_profile` help "budgets", or `reference://budgets`). Required: - name: Budget display name (e.g., "Monthly Groceries") - currencyCode: Any ISO-4217 fiat currency code (e.g., "USD", "EUR") — not limited to usedCurrencies in get_client_profile - type: Budget interval type - BUDGET_INTERVAL_WEEK: Weekly recurring budget (resets each week) - BUDGET_INTERVAL_MONTH: Monthly recurring budget (resets each month) - BUDGET_INTERVAL_YEAR: Yearly recurring budget (resets each year) - BUDGET_CUSTOM: Custom date range — requires both startDate and endDate. - NOTE: BUDGET_ALL is no longer accepted on create. Legacy budgets with this type remain readable. - limit: Spending limit as number (e.g., 500.00). Must be > 0. Conditionally required: - startDate: YYYY-MM-DD. REQUIRED when type is BUDGET_CUSTOM. NOT ACCEPTED for any other type. - endDate: YYYY-MM-DD. REQUIRED when type is BUDGET_CUSTOM. NOT ACCEPTED for any other type. Must be on or after startDate. Optional: - accountIds: Array of account IDs to track (max 50). If empty, tracks all accounts. - categoryIds: Array of category IDs to track (max 50). If empty, tracks all categories. - labelIds: Array of label IDs to filter transactions (max 50). Name uniqueness: name must be unique across open budgets (case-insensitive); a collision error names the existing budget and its id. The guard applies to writes through this API only — other clients can create duplicates, so resolve budgets by id, never by name. Immutable after create: - type is not patchable. Create a new budget to use a different type. - closed: Always false on create. closedDate is derived (auto-set when closed=true, cleared when closed=false). All relationship fields (categoryIds, labelIds, accountIds) use the same flat ID array format as GET responses. Defaults: closed=false, closedDate=null, accountIds=[] (all accounts), categoryIds=[] (all categories), labelIds=[] (no label filter). Response: {summary: {total, succeeded, clientErrors, serverErrors}, results: [{inputIndex, id?, success, error?, errorType?, budget?}]} — id is the created budget's id at the result's top level (success only). inputIndex is always 0 on this single-item tool; it exists so the same correlation key works across single- and multi-item write tools. On success: results[0] has success=true + full budget object (same fields as get_budgets, closed=false). On failure: success=false + error string + errorType. Single-item tool — the batch envelope is for response-shape consistency; total is always 1.

create_category

ChatGPT
Create or add a new custom category derived from an existing base category. Custom categories always have a parent base category. They inherit the parent's group, and optionally its color and cardinality. Use this when no existing base category fits the user's needs. ⚠️ USE WITH CAUTION: Custom categories exist for cases the ~88 base categories (visible via get_categories) do not cover, which is rare — they cover the vast majority of use cases. A custom category is appropriate when the user explicitly asks for one that does not already exist; otherwise an existing base category is the better fit. Required: - name: Custom category name (e.g., "Weekend Dining"). Must be unique across all categories (case-insensitive) and cannot match any base category display name. The guard applies to writes through this API only — other clients can create duplicates, so resolve categories by id. - parentId: Parent base category ID. Use get_categories to find available parent IDs. Optional: - color: Hex color code from the 16 writable colors (e.g. "#1565c0", "#d32f2f"). Default: inherits from the parent base category, whose shipped default is often outside the writable set — inheriting it is fine, re-sending it is not. reset: ["color"] restores a base category default exactly, including those values. - cardinality: must/need/want. Default: inherits from parent base category Defaults: color=inherited from parent, cardinality=inherited from parent, archived=false, enabled=true, customCategory=true, customName=true. Response: {summary: {total, succeeded, clientErrors, serverErrors}, results: [{inputIndex, id?, success, error?, errorType?, category?}]} — id is the created category's id at the result's top level (success only). inputIndex is always 0 on this single-item tool; it exists so the same correlation key works across single- and multi-item write tools. On success: results[0] has success=true + full category object. On failure: success=false + error string + errorType ("client_error" or "server_error"). Single-item tool — the batch envelope is for response-shape consistency; total is always 1. Limits: Maximum 50 custom categories per client.

create_label

ChatGPT
Create or add a custom label (hashtag) for tagging transactions. Fields: - name (required): Label name (max 80 chars). Must be unique across all labels (case-insensitive, whitespace-normalized); a collision error names the existing label and its id. The guard applies to writes through this API only — other clients can create duplicates, so resolve labels by id. - color (optional): Hex color code from the 16 universal colors (e.g. "#1565c0", "#d32f2f"). See enum for valid values. Randomly assigned if omitted. Response: results: [{inputIndex, id?, success, error?, errorType?, label?}] — id is the created label's id at the result's top level (success only). Single-item tool — the {summary, results} envelope is for response-shape consistency with the batch tools; total is always 1, and inputIndex is always 0 (it is present so one correlation rule covers every write tool, not because this call can return more than one row).

create_records

ChatGPT
Create, add, or insert financial transaction records in batch (up to 50 at once). Key rules: - Omitting categoryId defaults to Unknown — always provide one - transfer object auto-sets category to Transfer — do not provide categoryId with it - Bank-synced accounts: records cannot be created on them Required fields per record: - accountId: Account ID (use get_accounts to discover valid IDs) - amount: Transaction amount as number (e.g., -50.00 = fifty dollars expense). Negative=expense, positive=income - recordDate: ISO 8601. Accepts "2024-11-15T10:30:00Z", "2024-11-15T10:30:00", or date-only "2024-11-15". A date-only value is STORED at 12:00:00Z (noon UTC), deliberately — noon keeps the calendar day stable across ±12h timezones. (Filters differ: there, date-only maps to UTC midnight.) Optional fields per record: - categoryId: Category to assign — any ID from get_categories or create_category; omitting it defaults to Unknown (avoid: always assign a real category). Must not be provided when using the transfer object (auto-set to Transfer). Resolution ladder, base vs custom, and change rules: the categories guide (get_client_profile help "categories"). System IDs: Unknown income: 5c5c32c8-0082-8000-8000-000000000000 · Unknown expense: 5c5c32c9-0082-8000-8000-000000000000 · Uncategorized: 5c5c4e23-00c8-8000-8000-000000000000 · Transfer: 5c5c4e21-00c8-8000-8000-000000000000 (auto-managed — never assign it yourself; it is set/cleared via the transfer object and $clear:["transfer"]). - note: Transaction note/memo - labelIds: Array of label IDs to attach (read back as labels, expanded to [{id, name, color}]) - counterParty: Payee/merchant name - recordState: Record state. Valid values: reconciled, cleared, uncleared. Default: cleared. - place: Location data: {address (max 255), latitude (-90 to 90), longitude (-180 to 180), name (max 255)}. Lat/lng must both be present or both omitted. - transfer: Account movement settings (see below). Omit entirely for regular records. The transfer object models money moving between accounts — category forced to Transfer, excluded from budget spend. You send pairingMode, read back transfer.type. Fields: - transfer.accountId: target account (required for pairingMode "new") - transfer.recordId: existing record to bind (required for pairingMode "existing") - transfer.counterAmount: {value, currencyCode} — exact side-B amount for cross-currency "new" pairs (omit to let the server FX-convert) - pairingMode (required): "new" creates a mirror on the target account; "existing" binds to an existing unpaired record; "unpaired" creates one record with no mirror. | pairingMode (input) | result | transfer.type (output) | |---|---|---| | new | mirror record created on the target account | paired (this record + its mirror) | | existing | binds to an existing record; rejected if that record is already paired | paired (both records) | | unpaired | one transfer record, no mirror | unpaired | Full transfer mechanics — cross-currency FX, side-B inheritance, orphaning/re-bind, mirror propagation, examples — are in the transfers guide (get_client_profile help "transfers"). Defaults: recordState=cleared, recordType=expense (amount<0) or income (amount>0), currencyCode=account's currency, source="mcp", categoryId=Unknown expense or Unknown income (if omitted). Records always use the account's currency. Maximum 50 per batch. Partial success possible — check summary and per-item results. Mixed batches (regular + transfers) are allowed. ⚠️ Error-layer asymmetry (see the server instructions): a SCHEMA violation on any item (wrong type, bad enum value) fails the whole 50-item call before anything is written — the isError text names the failing item path (e.g. /records/1/recordState); a SEMANTIC violation (unknown accountId, category not found) fails only that item. Batch-write semantics — error layers, the {summary, results} envelope, inputIndex, fields, returnData — see the batch-writes guide (get_client_profile help "batch-writes"). Response: results carry record on success, …

delete_documents

ChatGPT
Delete, remove, or destroy documents permanently by ID. Supported types: records, accounts, budgets, standing-orders, record-rules, categories, labels. ⚠️ DESTRUCTIVE OPERATION — CANNOT BE UNDONE. Always confirm with the user before deleting. Parameters: - documentType (required): Type of documents to delete. Supported: "records", "accounts", "budgets", "standing-orders", "record-rules", "categories", "labels". - ids (required): Array of document IDs to delete. Maximum 20 per request. A repeated id is NOT an error here — a delete is idempotent, so repeats collapse to one deletion and every occurrence reports that id's outcome ([X, X] → two success rows, one document removed). Deduplicate your input if summary.succeeded must equal the number of documents deleted. Supported document types: - records: Transaction records. Account stats auto-recalculate. Records on bank-synced accounts cannot be deleted. Paired transfers delete as a pair: both record IDs must be in the same call, otherwise the delete is rejected and the error names the missing leg. Use get_records and read transfer.mirrorRecord.id to find it. Apart from that pair rule there is no cascade — deleting a record affects no other documents. - accounts: Bank accounts and wallets. Deletion is blocked if the account has any references (records, budgets, standing orders, or record rules). Delete or reassign all referencing entities first. Use get_references with type "account" to check before deleting. - budgets: Budget documents. No other entities reference budgets — safe to delete. - standing-orders: Standing order (recurring transaction) documents. No other entities reference standing orders — safe to delete. Requires the standingOrders.delete scope — check grantedScopes in get_client_profile; if missing, the user must enable it at web.budgetbakers.com/settings/mcp-server. - record-rules: Record rule (auto-categorization) documents. No other entities reference record rules — safe to delete. Requires the recordRules.delete scope — check grantedScopes in get_client_profile; if missing, the user must enable it at web.budgetbakers.com/settings/mcp-server. - categories: Custom categories only. Base categories cannot be deleted. Category must have no references (records, standing orders, budgets, record rules). Templates are automatically migrated to the parent base category. - labels: Hashtag labels. Deletion is blocked if the label is referenced by records, budgets, standing orders, or record rules, or by documents not managed by the API (Filter/Order/Template). Use get_references with type "label" to check before deleting. Batch-write semantics — error layers, the {summary, results} envelope, inputIndex, fields, returnData — see the batch-writes guide (get_client_profile help "batch-writes"). Response: {documentType, summary: {total, succeeded, clientErrors, serverErrors, documentsWritten}, results: [{id, success, error?, errorType?}]}. errorType is "client_error" (bad input/not found) or "server_error" (internal failure). documentsWritten counts the DISTINCT documents this call actually removed: repeats collapse, and a retried delete of an already-deleted document succeeds with 0 — nothing changed.

get_accounts

ChatGPT
List, read, and look up bank accounts and wallets linked to the user. Pagination: limit default 10, max 20 — deliberately the tightest cap in the API (records allow 400, categories 200). An account object is large (balance, recordStats, bank-sync state) and a client rarely has more than a handful, so the cap is sized for the whole set rather than for paging through one. Reviewers keep asking why, so: that is why. ⚠️ Account names are NOT unique — duplicates are allowed (the mobile apps permit them, and no API guard exists). The id is the only identity; never resolve an account by name. Response Fields: - id, name, accountType, currencyCode, color, archived, excludeFromStats, isInvestmentAccount, bankAccountNumber, createdAt, updatedAt - currencyCode: The account's currency (ISO 4217). Also repeated inside balance so amounts are self-describing — the two values are always identical. "" when the currency could not be resolved. - accountType: Always present; General is the default type. - color: The 16 writable colors are the ones every client renders for every entity type. Responses may carry other hex values, written by the apps — return them as-is, do not correct them. - isBankSync: Whether account is/was connected to bank sync (read-only, once true stays true) - bankAccountNumber: Masked by default — "**" plus the last 4 characters, or a bare "" when the stored value is shorter than 8 characters. Pass revealBankAccountNumber: true only when the user explicitly asks for the account number or IBAN; create_account/patch_accounts responses always return the masked form. NEVER send a masked value back on a write: it is rejected, and it is not the account number. - balance: Account balance object: - currentBalance: Computed balance in account currency (initialBalance + totalIncomes - totalExpenses for standard accounts) - rawCurrentBalance: Pre-adjustment balance (same as currentBalance for non-credit accounts) - initial: Opening balance value - balanceMode: standard, creditCardBank, creditCardManual, overdraftBank, overdraftManual, etc. - formula: Human-readable computation formula - (credit/overdraft only) creditLimit, creditBalance, availableCredit, balanceDisplayOption, balanceModeFormula - recordStats: {recordCount, totalExpenses, totalIncomes, lastUpdatedAt, recordDate: {min, max}, createdAt: {min, max}, error, errorAt}. recordDate, createdAt, and lastUpdatedAt are present only once the account has records — a recordCount: 0 account carries only the zeroed totals. - nextOffset: (when more results exist) Pass as offset to fetch the next page - total: Total number of matching accounts (before pagination) - _meta: Response metadata (syncedAt, lastResourceChange, rateLimit) — see server instructions. Sorting:** Allowed: name, createdAt, updatedAt, accountType, initialBalance. Default: name ASC.

get_budgets

ChatGPT
List, read, and query budget plans with spending limits and pre-computed spending data. The spending model — periods, effective limits, income netting, exclusions — is a separate document: the budgets guide (`get_client_profile` help "budgets", or `reference://budgets`). Fetch it once and cache it. What follows is the call-time contract only. All relationship fields (categoryIds, labelIds, accountIds) are flat ID arrays in both GET and WRITE responses. No format conversion needed. Names are not unique. create_budget and patch_budgets reject a duplicate name among open budgets, but that guard covers writes through this API only — other clients do not enforce it, so same-named budgets can appear. A name filter may return more than one. Resolve budgets by id. Response Fields: - id, name, currencyCode, closed, closedDate, createdAt, updatedAt - limit: Spending limit (JSON number). Null for legacy zero-limit budgets. The limit in force for a given period is spending.*.effectiveLimit, which overrides can make differ from this one. - type: BUDGET_INTERVAL_WEEK | BUDGET_INTERVAL_MONTH | BUDGET_INTERVAL_YEAR | BUDGET_CUSTOM (uses startDate/endDate) | BUDGET_ALL (legacy, readable, not accepted on create). - startDate, endDate: Only set on BUDGET_CUSTOM budgets. - categoryIds, labelIds, accountIds: Flat ID arrays. The budget's SCOPE — combined with AND, so a record must match all of them to count. See the budgets guide. - limitOverrides: Overrides in force now or later, each {limit, period, periodStart, periodCount?, createdAt}. EFFECTIVE-FROM semantics — see the budgets guide. Empty array means the budget uses limit for every period. - pastLimitOverrides: Overrides a later entry has already superseded. History; they affect nothing. - spending: Pre-computed spending (see spending parameter; default current+2), as {current, past: [...], computedAt}. Each period object carries periodStart, periodEnd, period, effectiveLimit, spent, remaining, overspent, progress, totalExpenses, totalIncomes, recordCount, excluded, convertedCurrencies, incomplete. spent = max(0, totalExpenses − totalIncomes), so it does NOT equal totalExpenses and a period with real expenses can report spent: 0. That, what excluded counts, and which records are in scope at all are the budgets guide's subject — read it before interpreting any of these numbers to a user. Spending Parameter: Controls spending data depth. Values: none, current, current+2, current+5, current+10, current+25. Default: current+2. Use 'none' to skip spending computation for faster responses. Sorting: Allowed: createdAt, updatedAt. Default: createdAt ASC. - _meta: Response metadata (syncedAt, lastResourceChange, rateLimit) — see server instructions.

get_categories

ChatGPT
List, browse, and look up transaction categories and their IDs. Returns ALL base categories (~88) plus user's custom categories. Two words that are easy to confuse, used precisely here and in the apps: - Base category — a root-level parent, the thing subcategories hang off and the thing you normally assign. parentId on create_category takes one of these, and systemId identifies one. - System category — one of the few TECHNICAL categories with special restrictions: Transfer (auto-managed by the transfer object), Uncategorized, and the app-internal Debt and Shopping list. They are not ordinary spending categories and most cannot be assigned through this API. The mobile apps use exactly this definition. (Two more once existed — One-click widget and Bank statement — and are DEPRECATED: their documents are not synced and their records are remapped to Uncategorized, so they never appear in a response. They were listed here until a reviewer went looking for them and found the group serves four.) So "the unknown bucket" is a BASE category (you can assign it, and records default into it), while Transfer is a SYSTEM category (you cannot). The two unknown buckets appearing beside Transfer in the fixed-ID list above is a convenience, not a statement that they are the same kind of thing. There are two kinds of categories: - Base categories: Built-in categories (e.g. "Groceries", "Rent"). ~88 built-in. Identified by UUIDs. - Custom categories: User-created categories that derive from a base category (parent). Created via create_category. Identified by UUIDs. Fixed category IDs (constant, generated from the registry): Unknown income: 5c5c32c8-0082-8000-8000-000000000000 · Unknown expense: 5c5c32c9-0082-8000-8000-000000000000 · Uncategorized: 5c5c4e23-00c8-8000-8000-000000000000 · Transfer: 5c5c4e21-00c8-8000-8000-000000000000 (auto-managed — never assign it yourself; it is set/cleared via the transfer object and $clear:["transfer"]) Full category model (resolution ladder, change rules): the categories guide (get_client_profile help "categories"). Response Fields: - id, name, group: {id, name}, systemId, color, cardinality, archived, enabled, customCategory, customName, createdAt, updatedAt - systemId: The base category's immutable identifier (envelope slug, e.g. "food_and_drinks__groceries"). Present for base categories only; custom subcategories have none. Named systemId for historical reasons — it identifies a BASE category, not a system category in the sense below. - color: The 16 writable colors are the ones every client renders for every entity type. Responses may carry other hex values, written by the apps — return them as-is, do not correct them. - parentId: (custom categories only) UUID of the parent base category - parentName: (custom categories only) the parent's display name exactly as this tool would return it — no extra lookup needed to tell same-named categories apart - nextOffset: (when more results exist) Pass as offset to fetch the next page - total: Total number of matching categories (before pagination) - _meta: Response metadata (syncedAt, lastResourceChange, rateLimit) — see server instructions. Base vs custom: - Base: stable UUID, customCategory=false, no parentId, has systemId - Custom: random UUID, customCategory=true, parentId/parentName identify the parent base category, no systemId cardinality describes a category's spending nature: must/need/want, or "none". A category with no cardinality of its own inherits its base category's value, so most categories report a real value rather than "none". cardinality filter: matches the value shown in the response, including inherited ones — filtering by an inherited value also returns categories that merely inherit it from their base category. cardinality=none returns categories explicitly set to none plus any with no value to inherit. To split spending by cardinality instead of listing categories, use get_records_aggregation with groupBy ["category:cardinality"]. Sorting: Allowed: name, createdAt, updatedAt. Defau…

get_client_profile

ChatGPT
Read the current client profile: permissions, sync state, tool inventory, and system type. Also serves the help/reference documents via the optional help parameter. Parameters (all optional): - help: Array of help-document flags to fetch alongside the profile — combine freely (["transfers"], ["agent-hints"], or ["agent-hints","transfers"] for everything in one call; a bare string is also accepted for a single document). The response then includes helpDocuments (array of {name, uri, mimeType, text}, one entry per requested flag). Flags: - agent-hints: Machine-readable registry of all agentHint types (JSON): type string, description, severity values, data payload fields, and which tools emit each hint. Fetch once and cache — use to branch on hint types programmatically. Same content as resource reference://agent-hints. - transfers: Full transfer mechanics for create_records and patch_records (markdown): pairingMode semantics, cross-currency counterAmount/FX, orphaning and re-bind, target conversion, and mirror propagation. Same content as resource reference://transfers. - categories: The category model (markdown): category-resolution ladder for create_records/patch_records, base vs custom semantics, the fixed category IDs, sign and cardinality notes, and category-change rules. Same content as resource reference://categories. Response Fields: - grantedScopes: Array of permission scopes granted to this client. Format: "entity.permission" (e.g., "records.read", "accounts.create", "budgets.delete"). Only scopes backed by an actual tool are advertised, so every scope listed here maps to something callable. Coverage differs by entity because not every entity is fully editable over this API: - records, accounts, categories, budgets, labels — read, create, update, delete, each via its dedicated tool (delete via delete_documents). - goals — read only, via get_entity. Goals are created and edited in the Wallet apps. - standingOrders, recordRules — read (get_entity) and delete (delete_documents) only. They are created and edited in the Wallet apps, so no create/update scope is offered. The delete scopes (standingOrders.delete, recordRules.delete) are granted separately like any other scope — supported by the server does NOT mean granted to this client; check grantedScopes before attempting the delete. A missing scope therefore means either the user has not granted it, or the operation is not supported for that entity at all. If a required scope is missing, the user can enable it at web.budgetbakers.com/settings/mcp-server. - mcpTools: Array of ALL tool names exposed by this MCP server (regardless of permissions). This is the complete tool inventory. A tool not in this list does not exist. - toolCount: Total number of tools exposed (matches mcpTools length) - system: Application type — always "Wallet" for this server - syncState: Current sync status - "syncing" - "complete" - "error" - syncError: (only when syncState is "error") - code: Error code ("missing_base_currency", "couch_limit_exceeded", etc.) - message: Human-readable error description - budgetSettings: Budget-related settings for this client - firstDayOfMonth: Day of month (1-31) when monthly budget periods start (default: 1) - baseCurrency: The client's base/reference currency code (e.g., "USD"). Empty string if not set. - usedCurrencies: Object with currencies currently in use by this user. - message: Explanatory text (this is NOT a restrictive list) - list: Array of currency codes (e.g., ["CZK", "EUR", "USD"]) Note: Any ISO-4217 fiat currency code is accepted when creating accounts or budgets — not limited to this list. - helpDocuments: Present only when the help parameter was sent — the requested help document(s), one entry of {name, uri, mimeType, text} per requested flag. Usage: - Call this tool at session start to understand what operations are available; add help:["agent-hints","transfers"] to bootstrap the reference documents in the same call - Check grantedScopes to know what you're allowed to do - If a scope like "records.create" is missing,…

get_entity

ChatGPT
List, read, and query supplementary entity types: standing orders, standing order items, goals, and record rules. Required: documentType — one of: standing-orders, standing-order-items, goals, record-rules Filters vary by documentType: - standing-orders: id, name, currencyCode, labelId, createdAt, updatedAt - standing-order-items: id, currencyCode, labelId, standingOrderId, recordId - goals: id, name, createdAt, updatedAt - record-rules: id, name, labelId, createdAt, updatedAt Passing a filter not supported by the selected documentType returns a validation error. These entity types are read-only via MCP — no create or update tools are available. Use the mobile app or web client to manage them. Deletion is supported via delete_documents for standing-orders and record-rules. Response: { documentType, results: [...], agentHints, _meta, limit, offset } Pagination: limit (default: 50, max: 200), offset

get_labels

ChatGPT
List, read, and look up custom labels (hashtags) for tagging transactions. Names are not unique. create_label and patch_labels reject a duplicate name, but that guard covers writes through this API only — other clients do not enforce it, so two labels with the same name can appear. A name filter may therefore return more than one, and which one is "the" label is not a question this API can answer. Resolve labels by id. Response Fields: - id, name, color, archived, createdAt, updatedAt - color: The 16 writable colors are the ones every client renders for every entity type. Responses may carry other hex values, written by the apps — return them as-is, do not correct them. - _meta: Response metadata (syncedAt, lastResourceChange, rateLimit) — see server instructions.

get_records

ChatGPT
List, filter, and look up financial transaction records (expenses, income, and transfers). Sorting: Allowed: amount, recordDate, createdAt, updatedAt. Default: -recordDate. Prefix with - for DESC. Sorting by amount compares raw values regardless of currency. Tip: For sums, averages, and group-by queries, use get_records_aggregation instead. Currency Conversion: Use convertTo parameter to convert record amounts on-demand: - Any ISO-4217 code (e.g. "EUR", "CZK", "USD") - "base" = user's profile currency - "account" = each record's account currency When set, each record includes convertedAmount: {value, ratio, currencyCode, conversionPair}. Uses recordDate for the forex rate. On error: convertedAmount: {error, currencyCode}. Response Fields: - id, accountId, note, counterParty, source - amount: {currencyCode, value} - amount in transaction currency - convertedAmount: (only with convertTo param) {value, ratio, currencyCode, conversionPair, error} - recordDate, recordState (reconciled/cleared/uncleared), recordType (expense/income — derived from the amount sign: amount < 0 = expense; the recordType filter and grouping also match on the sign) - category: {id, name, group: {id, name}, color, parentId}, labels: [{id, name, color}] — category.parentId is present only for a custom category: its parent base category's id (the same value get_categories returns as parentId) - createdAt, updatedAt - accountName: Display name of the record's account - place: (optional) Location data object: {address, latitude, longitude, name} - photos: Array of photo objects [{createdAt, temporaryUrl}] - accountIsBankSync: Whether the record's account is/was connected to bank sync (always present, boolean). This is a property of the account, not the record — to filter by bank-sync status, filter by accountId instead. - nextOffset: (when more results exist) Pass as offset to fetch the next page - total: Total number of matching records (before pagination) - transfer: Transfer state. Null for regular records. Object {type, transferId, mirrorRecord} where type is 'paired' or 'unpaired'. paired means a valid linked mirror record exists between two of the user's own accounts — mirrorRecord holds the other side's details: {id, amount: {currencyCode, value}, accountId, note, counterParty}. Use transfer.mirrorRecord.id with get_records to look up the mirror. unpaired means the record is a transfer but has no linked mirror. This covers two distinct cases: (1) a transfer to/from an external/foreign account that the user doesn't track — there is no counterpart record at all; (2) a transfer between the user's own accounts where the mirror record exists but the link was severed or never established (e.g. after $clear, failed pairing, or legacy data). In both cases mirrorRecord is absent. transferId is the primary transfer identity and is present on every transfer: no transferId means the record is not a transfer, a transferId held by exactly one record means an unpaired transfer, and a transferId shared by two records means a paired transfer. Pairing is decided by that cardinality, not by any flag. Pass a transferId to the transferId filter to fetch every leg sharing it — both sides of a pair in one call. A record whose pair was severed (by a re-bind or $clear:["transfer"]) keeps its transferId as a singleton and is simply an unpaired transfer, identical in every respect to one created that way. Note the distinction on a re-bind: the severed PARTNER keeps the old transferId; the record you actively re-bound takes its NEW pair's transferId, because it is entering a new pairing — observing its id change is not the singleton rule being violated. Read-only: transferId is always server-assigned and never accepted as input. - _meta: Response metadata (syncedAt, lastResourceChange, rateLimit) — see server instructions.

get_records_aggregation

ChatGPT
Aggregate, summarize, and analyze financial records with grouping, compute, and sorting. Use this tool for analytics queries like monthly spending by category, top categories by spending, yearly totals, etc. More efficient than fetching all records and computing aggregations client-side. Tip: For individual record details, use get_records instead. PARAMETERS: - groupBy (optional): Array of fields to group by. Omit or pass [] for grand-total mode (single row with no grouping). - A grand total over zero matching records answers `null`, not `0` — e.g. {"baseAmount:sum": null, "count": 0}. The two are different facts: null with count: 0 means NOTHING MATCHED, while 0 is a computed total that happens to be zero (income cancelling expense, or a zero-amount record). Never render a null sum as "you spent 0" — say the query matched nothing, widen it, or check the filters. A result.empty hint accompanies it, carrying the filters that narrowed the query to nothing; count is the field to branch on programmatically. - Time periods: month, week, day, year, budgetMonth (output as ISO date string: "2024-01-01"). All use UTC: week = ISO week (Monday start), day = UTC day, month = calendar month, year = calendar year. - budgetMonth: Groups by the user's budget month period based on firstDayOfMonth from get_client_profile → budgetSettings.firstDayOfMonth (default: 1). Auto-includes two output fields: budgetMonth (YYYY-MM, e.g. "2025-02") and budgetMonthStart (YYYY-MM-DD period start date, e.g. "2025-02-15"). When firstDayOfMonth=15, a March 10 record falls in the February budget month. For months with fewer days than firstDayOfMonth, the day is clamped to the last day of the month (e.g. firstDayOfMonth=31 in February → 28th or 29th). Cannot be combined with month, week, or year (can combine with day). - Category: category:id, category:name, category:customCategory, category:parentId, category:group, category:cardinality (colon notation, output keys match input). When category:id is used, category:name, category:customCategory, and category:parentId are auto-included. category:cardinality groups by the category's spending nature (must/need/want). Use it for discretionary-vs-essential questions ("where can I cut back?") in a single call instead of joining categories client-side. A category with no cardinality set falls back to its system category's value; records with no resolvable category (and categories explicitly set to none) group under "none". - Other: accountId, recordType (expense/income, derived from the amount sign — a group labelled expense only ever contains negative amounts), recordState (reconciled/cleared/uncleared), currency, source (android/ios/web/rest/mcp/backend/missing) - counterParty: Groups by payee/merchant — answers "where do I spend the most?" by shop rather than by category. Free text exactly as stored, with no normalisation ("Tesco" and "TESCO" are separate buckets); records with no counterParty group under null. Combine with the counterParty filter to drill into one merchant. - compute (optional): Flat array of additional compute expressions (colon notation). count is always included automatically. - Amount aggregations (native currency): "amount:sum", "amount:avg", "amount:min", "amount:max", "amount:median" (currency auto-added to groupBy) - amount:absSum: Sum of absolute values — always positive. Use for "top spenders" queries where sort direction should be intuitive (-amount:absSum = largest first). recordType is auto-added to groupBy (unless already present or filtered) so income and expense magnitudes don't mix. ⚠️ That auto-add makes the query GROUPED, so it no longer answers in grand-total shape: a grouped query matching nothing returns results: [], where a grand-total query returns one row of nulls beside count: 0. If you need the null-vs-zero distinction described below, filter recordType yourself (or group by it explicitly) and the absSum stays in whatever mode you asked for. - Base-currency aggregations: "baseAmount:sum", "baseAmount:avg", "baseAmount:min", "baseAmount:…

get_references

ChatGPT
Check, look up, and query which entities reference given entities. Batch query — supply up to 20 IDs at once. Use this before deleting a category, record, account, or label to see what would be affected. Parameters: - type (required): Entity type to check. Supported: "category", "record", "account", "label" - ids (required): Array of entity IDs to check (1–20 UUIDs) Response: Map keyed by entity ID. Each reference type has: {ids, total, limit, hasMore, field}. IDs capped at 50 per type. Only applicable reference types are returned: - type=category: records, standingOrders, budgets, recordRules - type=record: standingOrders - type=account: records, budgets, recordRules, standingOrders, plus other (documents not managed by the API; other.types is always present, empty [] when none) — use as pre-delete check; account deletion is blocked when references exist - type=label: records, standingOrders, budgets, recordRules, plus other (Filter/Order/Template documents that reference the label; other.types is always present, empty [] when none) — use as a pre-delete check; label deletion is blocked when references exist Per-ID errors: when an ID can't be resolved, its entry is an error object instead of references: - error="not found": no entity has that ID (typo or already deleted). - error="type_mismatch" (with actualType + message): the ID exists but is a different entity type than type (e.g. an account ID queried with type="category"). Re-query using actualType (when it is one of the supported reference types), or correct the ID. - error="not found" may carry a message when the id is not UUID-shaped — usually a pasted fragment or a name; ids are the bare values returned by the get_* tools. (Legacy non-UUID ids are still accepted.)

patch_accounts

ChatGPT
Update, modify, or edit bank accounts and wallets. Patchable fields: - name: Display name (max 80 chars) - archived: Archive status (true/false) - color: Hex color code from the 16 universal colors (e.g. "#1565c0", "#d32f2f"). See enum for valid values. - excludeFromStats: Exclude from statistics (true/false) - initialBalance: Initial balance amount (flat numeric input). Auto-recalculates reference amount. Read it back as balance.initial. - bankAccountNumber: Optional free-text reference for the account (max 80 chars), such as a bank account number, an IBAN or a short note the user gives. Set it only when the user explicitly provides it; never ask for it. Empty string clears the value; omit the field to leave it unchanged. A masked value containing "" (as get_accounts returns) is rejected — never echo it back. Only `bankAccountNumber` supports empty string to clear the value. Other text fields (`name`, `color`) reject empty values. Constraints: - currencyCode is immutable — sending it fails the item with guidance (create a new account with the desired currency) - Bank-synced accounts cannot be modified via patch_accounts. (Records on bank-synced accounts can still be patched — see patch_records.) - Each id at most once per call — if an id repeats, EVERY occurrence fails; merge the changes into one item - Maximum 20 items per request Batch-write semantics — error layers, the {summary, results} envelope, `inputIndex`, `fields`, `returnData` — see the batch-writes guide (`get_client_profile` help "batch-writes"). Response:* each result carries account (typed AccountData) on success.

patch_budgets

ChatGPT
Update, modify, or edit budget plans. The budget's limit is patched via resetLimit (or per-period limitOverrides), NOT via limit — limit is the read field and is rejected. The EFFECTIVE-FROM override model and the spending numbers a patch moves are the budgets guide (`get_client_profile` help "budgets", or `reference://budgets`). What follows is this tool's own contract. Limit changes: - resetLimit: Plain number. Clears ALL period overrides (past + future) and sets a uniform baseline limit. Works on ALL budget types including BUDGET_CUSTOM. Can be combined with limitOverrides (resetLimit applies first, then limitOverrides adds new entries). - limitOverrides: Array of limit overrides (maximum 50 entries), EFFECTIVE-FROM: each entry sets the limit for its period and every period after it, until the next entry takes over. An entry with no `periodCount` is open-ended — [{period:"2026-08", limit:600}] raises August and every month after it. To bound it, give it periodCount. Example — raise only 2026-08: [{period:"2026-08", limit:600, periodCount:1}]. Same on a weekly budget, week 33 alone: [{period:"2026-W33", limit:150, periodCount:1}]. A quarter: [{period:"2026-08", limit:600, periodCount:3}]. Replaces all current+future overrides — an override you do not re-send is DELETED, not merged. If a stored override runs 2026-09..2027-08 and you send a window for 2026-06, the stored one is gone in full, including the part after your window; re-send it in the same array if you want it kept. Past overrides are preserved. Not allowed on BUDGET_CUSTOM. Each entry: - limit (number, > 0): New spending limit. Mutually exclusive with setBaseline. - period (required string): Starting period. Format: YYYY-MM (monthly), YYYY-Www (weekly), YYYY (yearly). Must be >= current period. - periodCount (integer >= 1, optional): How many periods the override lasts, counting its own — 1 is "this period only", 3 is "this period and the next two". Omit for open-ended. Requires limit; rejected with setBaseline, and rejected if another entry in the same call starts inside the span. The server writes the entry that ends the override, holding the limit that was actually in force at that period — which is why this is safer than writing the closing entry by hand. Bounded overrides in one call may sit back to back ([{period:"2026-06", limit:800, periodCount:3}, {period:"2026-09", limit:900, periodCount:3}]); once both end, the limit returns to whatever preceded them, never to the earlier window. - setBaseline (boolean): If true, writes an entry holding the budget's current limit, ending any earlier override from this period onward. Because entries carry a fixed value, a later change to the budget's limit does not move this entry — use resetLimit to clear all overrides instead. Mutually exclusive with limit and with periodCount. Prefer periodCount when you know how long the override should last: setBaseline writes the budget's BASE limit, which is not the same as the limit in force if an older override was still carrying forward. Scope changes: accountIds, categoryIds and labelIds decide WHICH records the budget counts, so changing one moves the spending numbers without any record changing. A patch that sends any of the three answers a budget.scope_changed info hint per budget. The spending in that same response is already computed against the NEW scope — do not report the move to the user as a change in spending without saying the filter changed. Other patchable fields: - name: Budget display name (max 80 chars) - accountIds: Account IDs. Either a plain array of UUIDs (replaces all existing) or a {add:[...], remove:[...]} object for incremental changes. - categoryIds: Category references. Either a plain array of UUIDs (replaces all existing) or a {add:[...], remove:[...]} object for incremental changes. - labelIds: Label IDs. Either a plain array of UUIDs (replaces all existing) or a {add:[...], remove:[...]} object for incremental changes. - closed: true to close (aut…

patch_categories

ChatGPT
Update, modify, or rename transaction categories. Editing a base category creates a private, per-account override — it is not shared with other users and does not change a global template. Your change applies wherever that category already appears in your own data, and is reversible via reset. To keep the base category untouched, create a custom category instead. Patchable fields: - name: Display name. Sets customName=true. (customCategory is unchanged for base categories — only custom subcategories get customCategory=true.) - color: Hex color code from the 16 universal colors (e.g. "#1565c0", "#d32f2f"). - cardinality: must/need/want. Use "none" to clear. - reset: Restore fields to the base (system) category default — an array of any of "name", "cardinality", "color" (base categories only; custom subcategories have no defaults). Use it to undo an accidental base-category edit. A field cannot be both set and reset in the same call. reset:["color"] restores THE SHIPPED DEFAULT — the colour of the category's envelope group, shared by every category in that group. It does not restore whatever colour the category had before you changed it. Two different colours are involved and only one comes back: the shipped default is restored exactly, INCLUDING when it lies outside the 16 writable colours; a colour you or an app wrote onto the category is overwritten and cannot be written back, because the color field only accepts the 16. That is what "reset to default" means everywhere — read the current colour first if you may want it again. Constraints: - Maximum 20 items per request; each id at most once per call — if an id repeats, EVERY occurrence fails - Name must be unique across all categories (case-insensitive, whitespace-normalized). Cannot match any base category display name, except when resetting a base category back to its default name. A collision error names the existing category, its id, and whether it is a base or custom category. Batch-write semantics — error layers, the {summary, results} envelope, inputIndex, fields, returnData — see the batch-writes guide (get_client_profile help "batch-writes"). Response: each result carries category (typed CategoryData, same shape as get_categories) on success.

patch_labels

ChatGPT
Update, modify, or edit one or more custom labels. Each item targets a label by id; at least one of name, color, or archived must be provided. Per-item fields: - id (required): Label id. Each id at most once per call — if an id repeats, EVERY occurrence of it fails (the intended result would be ambiguous); merge the changes into one item. - name (optional): New name (max 80 chars). Must be unique across all labels (case-insensitive, whitespace-normalized); a collision error names the existing label and its id. Re-sending a label's own name is a no-op, not a conflict. Uniqueness is checked WITHIN the batch too (D9, worth knowing because nothing said so): the existing-name check sees the pre-batch state, so two items renaming DIFFERENT labels to the same name would both pass it — the second one instead fails with "another item in this batch (id: X) also sets the name 'Y'". A batch cannot create a collision the next call would reject. - color (optional): Hex color code from the 16 universal colors (e.g. "#1565c0", "#d32f2f"). See enum for valid values. - archived (optional): Archive/unarchive the label. An unknown label id is reported as not found. Batch-write semantics — error layers, the {summary, results} envelope, inputIndex, fields, returnData — see the batch-writes guide (get_client_profile help "batch-writes").

patch_records

ChatGPT
Update, modify, or edit transaction records. Each item must have 'id' and at least one field to update. Patchable fields: - categoryId: Category to assign. ID (from get_categories or create_category) targets any category directly — use ID for custom categories. Category model + change rules: the categories guide (get_client_profile help "categories"). - labelIds: Either a plain array of label IDs (replaces all existing), or a {add:[...], remove:[...]} object to add/remove specific labels without sending the full set. Set semantics, both directions: adding a label the record already has and removing one it does not have are silent no-ops, not errors — unlike a repeated id in the batch, which fails loudly. Read back as labels ([{id, name, color}]); map labels[].id back into labelIds for a read-modify-write. - note: Note text (max 255 chars). - counterParty: Counter party/payee name (max 255 chars). - recordState: reconciled, cleared, uncleared - recordDate: ISO 8601. Accepts "2024-01-15T10:30:00Z", "2024-01-15T10:30:00", or date-only "2024-01-15"; a date-only value is stored at 12:00:00Z (noon UTC) so the calendar day is stable across timezones. - amount: Negative=expense, positive=income. Auto-updates recordType. ⚠️ Patching amount ALWAYS sets the record's currency to the account's currency, regardless of the record's original currency (e.g. record was 100 EUR on a USD account → patch amount to 90 → record becomes 90 USD). If amount is omitted from the patch, the original currency is preserved. - place: Location data. Omit=no change. Full replace: {address, latitude, longitude, name}. Lat/lng must both be present or both omitted. - accountId: Move record to a different account (UUID). Both source and target accounts must not be bank-synced. Target account must have the same currency. - transfer: Convert a record into a transfer, or re-bind an existing transfer. Requires pairingMode: "new" (create mirror on target account, requires accountId), "existing" (bind to an existing record, requires recordId), or "unpaired". "new" cannot create the mirror in a bank-synced account. Cannot be combined with categoryId (auto-set to Transfer). Re-bind (orphaning the old mirror), cross-currency counterAmount, target conversion, and mirror propagation — see the transfers guide (get_client_profile help "transfers"). $clear: array of fields to erase — categoryId (resets to Unknown), labelIds, note, counterParty, place, transfer. A field cannot be both cleared and set in the same item (except transfer+categoryId). The valid set is validated per ITEM, not by the schema, deliberately — see "Where a value is listed in a schema ONLY so that sending it fails one item" in the batch-writes guide. $clear: "transfer" — cancels transfer state; category auto-set to Unknown unless a categoryId is provided in the same patch. No-op on non-transfer records. Mirror-severing/orphaning detail — see the transfers guide (get_client_profile help "transfers"). Constraints: - ⚠️ Bank-synced accounts (isBankSync=true): amount, recordDate, recordState, and accountId cannot be patched (bank-authoritative data). All other fields (categoryId, labelIds, note, counterParty) can be patched normally, and the record CAN be marked as a transfer with any pairingMode: "existing" (link the two synced legs of a self-transfer), "unpaired", or "new" (the new counter-record must go to an account that is not bank-synced). - ⚠️ Transfer records (any record with transfer.type == "paired" or "unpaired"): categoryId cannot be changed (Transfer is a locked system category). To change category, first cancel the transfer with $clear:["transfer"], or include $clear:["transfer"] with a categoryId in the same patch. - ⚠️ Paired transfer records (those with transfer.type == "paired" and a transfer.mirrorRecord) — the ONE mirror-propagation rule: only same-currency amount and recordDate changes auto-propagate to the mirror (sign-flipped); every other field and ALL cross-currency changes do not, leaving the pair out of sync until you patch the mirror too (the transfe…

Capabilities

Writes

Example Prompts

Click any prompt to copy it.

Similar Apps