create_transaction
Add new transactions to YNAB budgets with specified amounts, dates, and accounts. Supports split transactions by setting category to null and providing subtransactions.
Instructions
[1 API call] Create a new transaction. Amounts are in dollars (positive for inflows, negative for outflows). For split transactions, set category_id to null and provide subtransactions.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| budget_id | No | Budget ID or 'last-used' | last-used |
| account_id | Yes | Account ID for the transaction | |
| date | Yes | Transaction date (YYYY-MM-DD) | |
| amount | Yes | Amount in dollars (negative for outflows, e.g., -25.50) | |
| payee_id | No | Payee ID (if known) | |
| payee_name | No | Payee name (will match or create payee) | |
| category_id | No | Category ID (omit for split transactions) | |
| memo | No | Transaction memo | |
| cleared | No | Cleared status | |
| approved | No | Whether the transaction is approved (default: false) | |
| flag_color | No | Flag color | |
| subtransactions | No | Split transaction parts (amounts must sum to the total) |
Implementation Reference
- src/tools/transactions.ts:82-140 (handler)The `create_transaction` tool is registered and implemented here, handling input validation through zod and executing the API call using the YNAB client.
server.registerTool("create_transaction", { title: "Create Transaction", description: "[1 API call] Create a new transaction. Amounts are in dollars (positive for inflows, negative for outflows). For split transactions, set category_id to null and provide subtransactions.", inputSchema: { budget_id: z.string().default("last-used").describe("Budget ID or 'last-used'"), account_id: z.string().describe("Account ID for the transaction"), date: z.string().describe("Transaction date (YYYY-MM-DD)"), amount: z.number().describe("Amount in dollars (negative for outflows, e.g., -25.50)"), payee_id: z.string().optional().describe("Payee ID (if known)"), payee_name: z.string().optional().describe("Payee name (will match or create payee)"), category_id: z.string().optional().describe("Category ID (omit for split transactions)"), memo: z.string().optional().describe("Transaction memo"), cleared: z.enum(CLEARED_VALUES).optional().describe("Cleared status"), approved: z.boolean().optional().describe("Whether the transaction is approved (default: false)"), flag_color: z.enum(FLAG_COLORS).optional().describe("Flag color"), subtransactions: z.array(z.object({ amount: z.number().describe("Subtransaction amount in dollars"), payee_id: z.string().optional(), payee_name: z.string().optional(), category_id: z.string().optional(), memo: z.string().optional(), })).optional().describe("Split transaction parts (amounts must sum to the total)"), }, annotations: { readOnlyHint: false }, }, async ({ budget_id, account_id, date, amount, payee_id, payee_name, category_id, memo, cleared, approved, flag_color, subtransactions }) => { try { const response = await getClient().transactions.createTransaction(budget_id, { transaction: { account_id, date, amount: dollarsToMilliunits(amount), payee_id, payee_name, category_id, memo, cleared, approved, flag_color: flag_color ?? null, subtransactions: subtransactions?.map((s) => ({ amount: dollarsToMilliunits(s.amount), payee_id: s.payee_id, payee_name: s.payee_name, category_id: s.category_id, memo: s.memo, })), }, }); const t = response.data.transaction; if (t) { return textResult( `Created transaction: ${t.date} | ${formatCurrency(t.amount)} | ${t.payee_name ?? "No payee"} | ${t.category_name ?? "Uncategorized"}\nID: ${t.id}` ); } const txns = response.data.transactions; return textResult(`Created ${txns?.length ?? 0} transaction(s).`); } catch (e: any) { return errorResult(e.message); } });