Flusso: Syncing MoneyMoney to YNAB with a Bash Script
I use MoneyMoney for banking and YNAB for budgeting. They don't talk to each other, and for three years that meant the same Monday ritual: open both apps, copy transactions across by hand. Date, payee, amount, category, save. Fifteen times. Close the laptop. Promise to do it more often.
The promise never held. The friction was small, the resentment was big. Skip a weekend. Skip a month. Guilt catches up...
If you'd told me at the start that the fix would take a weekend of bash and a YNAB API token, I wouldn't have believed you. But here we are.
Why not just use YNAB's sync?
YNAB does offer transaction sync. The catch: only a handful of banks connect directly. For everyone else, most of Europe included, it routes through a third-party provider that needs read access to your entire bank account to do its job. You log in to your bank from inside their interface and hand over the keys.
Maybe that's fine. It probably is, technically. But I can't get past it. We're already at a point where most people grant blanket access to anything that asks, and I'd rather not put my bank login on top of that pile. MoneyMoney already has my transactions, on my own machine, with credentials I gave directly to my bank's app. Adding a third party in between two things I already trust felt like the wrong direction. Honestly, that's the real reason I kept retyping by hand instead of clicking "connect."
I miss the old YNAB
There was a stretch, years ago, when YNAB shipped as a standalone Mac app and MoneyMoney could just hand it a file. Export, double-click, imported. When YNAB became web-only, that path quietly disappeared, and I never got over it. Some part of my brain has been waiting for it to come back ever since.
It hasn't.
Looking at the problem properly
A few weeks ago I sat down with it instead of complaining about it. Two things turned out to be true:
- YNAB has an HTTP API. You generate a personal access token in settings, POST a JSON body to
/v1/budgets/{id}/transactions, done. - MoneyMoney is fully scriptable through AppleScript. It can export transactions as a plist and assign categories from the outside.
So nothing was actually missing. I just hadn't bothered to look.
What I wanted
The constraints I cared about:
- Stay local. The whole reason I'm not using YNAB's built-in sync is to avoid a third party in the loop, so the tool can't quietly become one.
- No database. Every "personal sync tool" I've seen grows a state file and the state file rots.
- I wanted to open MoneyMoney and see which transactions had been synced, not look it up somewhere else.
- I wanted a dry run. The thought of pushing a hundred wrong transactions into YNAB on the first try and then deleting them by hand was enough to stop me from starting.
The design that fell out was deliberately boring: use a MoneyMoney category as the bookkeeping layer. If a transaction carries the "synced" category, it's done. If not, it's a candidate. No SQLite, no JSON ledger. The state lives on the transaction itself, where I can see it.
That left three commands worth writing: a setup wizard for first-time config, a --dry-run flag for previewing, and a sync command that pushes new transactions to YNAB and tags them in MoneyMoney as it goes.
Setup
flusso setup is a one-time wizard. You give it a handful of details and it writes a single config file at ~/.flusso/config.json:
- a YNAB API token (you generate one in YNAB's developer settings)
- which YNAB budget to sync into
- a start date, so you don't accidentally backfill years of history
- for each MoneyMoney account you want to sync, the matching YNAB account
- the MoneyMoney category that will mark synced transactions
$ flusso setup
Flusso Setup
MoneyMoney → YNAB Sync Configuration
── 1/6 Directories ──
✓ Created /Users/you/.flusso
── 2/6 YNAB API Token ──
Create your token at: https://app.ynab.com/settings/developer
YNAB API Token: your-token-here
Validating token...
✓ Token valid
── 3/6 Select YNAB Budget ──
Available budgets:
[1] My Budget
[2] Shared Budget
Select budget [1-2]: 1
✓ Budget: My Budget
── 4/6 Start Date ──
From which date should transactions be synced?
Start date [2026-04-05]: 2026-04-01
✓ Start date: 2026-04-01
── 5/6 Map Accounts ──
Account mapping #1
MoneyMoney account name: Checking Account
MoneyMoney account number (IBAN or card number): DE00000000000000000000
Custom start date? (empty = global) [2026-04-01]:
YNAB accounts in My Budget:
[1] Checking 1250.00 €
[2] Credit Card -367.85 €
Sync to YNAB account [1-2]: 1
✓ Checking Account (DE00000000000000000000) → Checking
Add another account? [y/N]: n
── 6/6 MoneyMoney Category ──
Available categories:
[1] Uncategorized
[2] ✔ YNAB Synced
Select sync category [1-2]: 2
✓ Category: ✔ YNAB Synced
── Save Configuration ──
✓ Config written: /Users/you/.flusso/config.json
✓ Flusso is ready!
Dry run
Before anything goes near YNAB, the dry run shows me what's about to happen:
$ flusso sync --dry-run
[dry run] No transactions will be sent to YNAB.
── Checking Account ── (DE00000000000000000000)
5 new transaction(s) found.
DATE AMOUNT PAYEE MEMO
---------- ---------- ---------------------------- ----------------------------
2026-04-03 -42.50 Supermarket GmbH Groceries
2026-04-02 -9.99 Streaming Service Monthly subscription
2026-04-01 -850.00 Landlord Rent April 2026
2026-04-01 -29.99 Phone Provider Mobile plan
2026-04-01 3500.00 Employer Inc Salary April
Dry run complete. 5 transaction(s) would be synced.
It shows exactly what will land in YNAB if I run it for real. Mostly I use it as a sanity check after touching the config: did the account mapping take, is the start date right, do the amounts look the way I expect.
The real thing
$ flusso sync
── Checking Account ── (DE00000000000000000000)
5 new transaction(s) found.
✓ 5 created, 0 duplicates skipped.
✓ 5 transaction(s) categorized as "✔ YNAB Synced".
✓ Sync complete. 5 transaction(s) synced.
Phew. The first "Sync complete." felt better than it had any right to.
Run it again right after, and there's nothing left to do:
$ flusso sync
── Checking Account ── (DE00000000000000000000)
✓ No new transactions.
✓ Sync complete. 0 transaction(s) synced.
The five new transactions now carry the sync category in MoneyMoney, so they're invisible to the next pass. No state to clean up, no ledger to corrupt, nothing to re-bootstrap when I switch machines. If I want to know what's been synced, I open MoneyMoney and look.
Three years of pretending this wasn't bothering me. Now: one command, fifteen seconds, done.
Source: github.com/johannesbraeunig/flusso. If you also live in the MoneyMoney + YNAB intersection, try it and tell me what breaks.