Skip to main content
Glama
dgalarza

YNAB MCP Server

by dgalarza

create_split_transaction

Create a new transaction split across multiple budget categories to track spending allocations for a single purchase or payment.

Instructions

Create a NEW split transaction with multiple category allocations.

This tool creates a brand new transaction that is split across multiple categories.
It CANNOT be used to add splits to an existing transaction - the YNAB API does not support that.

Args:
    budget_id: The ID of the budget (use 'last-used' for default budget)
    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: 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"}]'
    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

Important Notes:
    - This creates a NEW transaction only - cannot modify existing transactions to add splits
    - The sum of subtransaction amounts should equal the total transaction amount
    - Once created, subtransactions cannot be modified via the API (YNAB limitation)
    - To "split" an existing transaction, you must delete it and create a new split transaction

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
account_idYes
amountYes
approvedNo
budget_idYes
clearedNouncleared
dateYes
memoNo
payee_nameNo
subtransactionsYes

Implementation Reference

  • MCP tool handler: parses subtransactions JSON, calls YNABClient.create_split_transaction, serializes response to JSON. Registered via @mcp.tool() decorator which also defines input schema via type hints and docstring.
    @mcp.tool()
    async def create_split_transaction(
        budget_id: str,
        account_id: str,
        date: str,
        amount: float,
        subtransactions: str,
        payee_name: str = None,
        memo: str = None,
        cleared: str = "uncleared",
        approved: bool = False,
    ) -> str:
        """Create a NEW split transaction with multiple category allocations.
    
        This tool creates a brand new transaction that is split across multiple categories.
        It CANNOT be used to add splits to an existing transaction - the YNAB API does not support that.
    
        Args:
            budget_id: The ID of the budget (use 'last-used' for default budget)
            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: 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"}]'
            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
    
        Important Notes:
            - This creates a NEW transaction only - cannot modify existing transactions to add splits
            - The sum of subtransaction amounts should equal the total transaction amount
            - Once created, subtransactions cannot be modified via the API (YNAB limitation)
            - To "split" an existing transaction, you must delete it and create a new 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.create_split_transaction(
            budget_id,
            account_id,
            date,
            amount,
            subtransactions_list,
            payee_name,
            memo,
            cleared,
            approved,
        )
        return json.dumps(result, indent=2)
  • Core implementation: formats subtransactions payload (converts amounts to milliunits, sets category_id=None on parent), POSTs to YNAB /transactions endpoint, formats response including subtransactions.
    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

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