Skip to main content
Glama

prepare_split_for_matching

Create an unapproved split transaction to match with an imported bank transaction in YNAB. This prepares the split for manual matching in the YNAB interface, allowing you to categorize different portions of a single transaction.

Instructions

Prepare a split transaction to match with an existing imported transaction.

This tool fetches an existing transaction's details and creates a new UNAPPROVED split transaction with the same date, amount, account, and payee. You can then manually match them together in the YNAB web or mobile UI. Use this when you want to split an imported bank transaction - the new split will be created as unapproved so you can match it with the original in YNAB's UI. Args: budget_id: The ID of the budget (use 'last-used' for default budget) transaction_id: The ID of the existing transaction to split subtransactions: JSON string containing array of subtransactions. Each subtransaction should have: - amount (required): The subtransaction amount - category_id (optional): Category ID for this split - payee_id (optional): Payee ID for this split - memo (optional): Memo for this split Example: '[{"amount": -50.00, "category_id": "cat1", "memo": "Groceries"}, {"amount": -30.00, "category_id": "cat2", "memo": "Gas"}]' Returns: JSON string with original transaction details, new split transaction details, and instructions Workflow: 1. This tool fetches the existing transaction details 2. Creates a new unapproved split transaction with those details 3. You manually match them in the YNAB UI 4. YNAB merges them into one split transaction Note: - The new split is created as UNAPPROVED for manual matching - The sum of subtransaction amounts should equal the original transaction amount - After matching in YNAB UI, the original transaction will become a split transaction

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
budget_idYes
subtransactionsYes
transaction_idYes

Implementation Reference

  • Core implementation of prepare_split_for_matching: fetches the original transaction, creates a duplicate unapproved split transaction with provided subtransactions for manual matching in YNAB UI, returns details of both with instructions.
    async def prepare_split_for_matching( self, budget_id: str, transaction_id: str, subtransactions: list[dict[str, Any]], ) -> dict[str, Any]: """Prepare a split transaction to match with an existing imported transaction. This fetches an existing transaction's details and creates a new unapproved split transaction with the same date, amount, account, and payee. You can then manually match them in the YNAB UI. Args: budget_id: The budget ID or 'last-used' transaction_id: The ID of the existing transaction to base the split on subtransactions: List of subtransaction dictionaries, each containing: - amount (float, required): Subtransaction amount - category_id (str, optional): Category ID for this subtransaction - payee_id (str, optional): Payee ID for this subtransaction - memo (str, optional): Memo for this subtransaction Returns: Dictionary with original transaction details and newly created split transaction Note: - The new split transaction is created as unapproved - You must manually match them in the YNAB UI - The sum of subtransaction amounts should equal the original transaction amount """ try: # Fetch the original transaction details original = await self.get_transaction(budget_id, transaction_id) # Create a new split transaction with the same details but unapproved new_split = await self.create_split_transaction( budget_id=budget_id, account_id=original["account_id"], date=original["date"], amount=original["amount"], subtransactions=subtransactions, payee_name=original.get("payee_name"), memo=original.get("memo"), cleared=original.get("cleared", "uncleared"), approved=False, # Always create as unapproved for manual matching ) return { "original_transaction": { "id": original["id"], "date": original["date"], "amount": original["amount"], "payee_name": original.get("payee_name"), "account_name": original.get("account_name"), }, "new_split_transaction": new_split, "instructions": ( "A new unapproved split transaction has been created. " "Go to YNAB and manually match these two transactions together. " "Look for the match indicator in the YNAB UI." ), } except Exception as e: raise Exception(f"Failed to prepare split for matching: {e}") from e
  • MCP registration and wrapper handler: @mcp.tool() decorator registers the tool, function handles JSON input/output serialization and delegates to YNABClient method.
    @mcp.tool() async def prepare_split_for_matching( budget_id: str, transaction_id: str, subtransactions: str, ) -> str: """Prepare a split transaction to match with an existing imported transaction. This tool fetches an existing transaction's details and creates a new UNAPPROVED split transaction with the same date, amount, account, and payee. You can then manually match them together in the YNAB web or mobile UI. Use this when you want to split an imported bank transaction - the new split will be created as unapproved so you can match it with the original in YNAB's UI. Args: budget_id: The ID of the budget (use 'last-used' for default budget) transaction_id: The ID of the existing transaction to split subtransactions: JSON string containing array of subtransactions. Each subtransaction should have: - amount (required): The subtransaction amount - category_id (optional): Category ID for this split - payee_id (optional): Payee ID for this split - memo (optional): Memo for this split Example: '[{"amount": -50.00, "category_id": "cat1", "memo": "Groceries"}, {"amount": -30.00, "category_id": "cat2", "memo": "Gas"}]' Returns: JSON string with original transaction details, new split transaction details, and instructions Workflow: 1. This tool fetches the existing transaction details 2. Creates a new unapproved split transaction with those details 3. You manually match them in the YNAB UI 4. YNAB merges them into one split transaction Note: - The new split is created as UNAPPROVED for manual matching - The sum of subtransaction amounts should equal the original transaction amount - After matching in YNAB UI, the original transaction will become a split transaction """ client = get_ynab_client() # Parse subtransactions JSON string try: subtransactions_list = json.loads(subtransactions) except json.JSONDecodeError as e: raise YNABValidationError(f"Invalid subtransactions JSON: {e}") from e result = await client.prepare_split_for_matching( budget_id, transaction_id, subtransactions_list ) return json.dumps(result, indent=2)
  • Supporting helper: create_split_transaction - used by prepare_split_for_matching to create the new split txn.
    async def create_split_transaction( self, budget_id: str, account_id: str, date: str, amount: float, subtransactions: list[dict[str, Any]], payee_name: str | None = None, memo: str | None = None, cleared: str = "uncleared", approved: bool = False, ) -> dict[str, Any]: """Create a new split transaction with subtransactions. Args: budget_id: The budget ID or 'last-used' account_id: The account ID for this transaction date: Transaction date in YYYY-MM-DD format amount: Total transaction amount (positive for inflow, negative for outflow) subtransactions: List of subtransaction dictionaries, each containing: - amount (float, required): Subtransaction amount - category_id (str, optional): Category ID for this subtransaction - payee_id (str, optional): Payee ID for this subtransaction - memo (str, optional): Memo for this subtransaction payee_name: Name of the payee for the main transaction (optional) memo: Transaction memo (optional) cleared: Cleared status - 'cleared', 'uncleared', or 'reconciled' (default: 'uncleared') approved: Whether the transaction is approved (default: False) Returns: JSON string with the created split transaction Note: - The sum of subtransaction amounts should equal the total transaction amount - category_id on the main transaction will be set to null automatically for split transactions """ try: url = f"{self.api_base_url}/budgets/{budget_id}/transactions" # Format subtransactions formatted_subtransactions = [] for sub in subtransactions: sub_data = { "amount": int(sub["amount"] * MILLIUNITS_FACTOR), } if sub.get("category_id"): sub_data["category_id"] = sub["category_id"] if sub.get("payee_id"): sub_data["payee_id"] = sub["payee_id"] if sub.get("memo"): sub_data["memo"] = sub["memo"] formatted_subtransactions.append(sub_data) # Build transaction data with subtransactions transaction_data = { "account_id": account_id, "date": date, "amount": int(amount * MILLIUNITS_FACTOR), "category_id": None, # Must be null for split transactions "subtransactions": formatted_subtransactions, "cleared": cleared, "approved": approved, } if payee_name is not None: transaction_data["payee_name"] = payee_name if memo is not None: transaction_data["memo"] = memo data = {"transaction": transaction_data} result = await self._make_request_with_retry("post", url, json=data) txn = result["data"]["transaction"] # Format subtransactions in response subtransactions_response = [] if txn.get("subtransactions"): for sub in txn["subtransactions"]: subtransactions_response.append( { "id": sub.get("id"), "amount": sub["amount"] / MILLIUNITS_FACTOR if sub.get("amount") else 0, "memo": sub.get("memo"), "payee_id": sub.get("payee_id"), "payee_name": sub.get("payee_name"), "category_id": sub.get("category_id"), "category_name": sub.get("category_name"), } ) return { "id": txn["id"], "date": txn["date"], "amount": txn["amount"] / MILLIUNITS_FACTOR if txn.get("amount") else 0, "memo": txn.get("memo"), "cleared": txn.get("cleared"), "approved": txn.get("approved"), "account_id": txn.get("account_id"), "account_name": txn.get("account_name"), "payee_id": txn.get("payee_id"), "payee_name": txn.get("payee_name"), "category_id": txn.get("category_id"), "category_name": txn.get("category_name"), "subtransactions": subtransactions_response, } except Exception as e: raise Exception(f"Failed to create split transaction: {e}") from e
  • Supporting helper: get_transaction - used by prepare_split_for_matching to fetch original transaction details.
    async def get_transaction( self, budget_id: str, transaction_id: str, ) -> dict[str, Any]: """Get a single transaction with all details including subtransactions. Args: budget_id: The budget ID or 'last-used' transaction_id: The transaction ID to retrieve Returns: Transaction dictionary with full details """ try: url = f"{self.api_base_url}/budgets/{budget_id}/transactions/{transaction_id}" result = await self._make_request_with_retry("get", url) txn = result["data"]["transaction"] # Format subtransactions if present subtransactions = [] if txn.get("subtransactions"): for sub in txn["subtransactions"]: subtransactions.append( { "id": sub.get("id"), "amount": sub["amount"] / MILLIUNITS_FACTOR if sub.get("amount") else 0, "memo": sub.get("memo"), "payee_id": sub.get("payee_id"), "payee_name": sub.get("payee_name"), "category_id": sub.get("category_id"), "category_name": sub.get("category_name"), } ) return { "id": txn["id"], "date": txn["date"], "amount": txn["amount"] / MILLIUNITS_FACTOR if txn.get("amount") else 0, "memo": txn.get("memo"), "cleared": txn.get("cleared"), "approved": txn.get("approved"), "account_id": txn.get("account_id"), "account_name": txn.get("account_name"), "payee_id": txn.get("payee_id"), "payee_name": txn.get("payee_name"), "category_id": txn.get("category_id"), "category_name": txn.get("category_name"), "transfer_account_id": txn.get("transfer_account_id"), "subtransactions": subtransactions if subtransactions else None, } except Exception as e:

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dgalarza/ynab-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server