I use MoneyMoney for banking and YNAB for budgeting. They don't talk to each other, and I did it by hand for years — keeping up for a week, then skipping a weekend, then spending twenty minutes every Monday catching up. Flusso is my fix:

flusso sync
── Checking Account ── (DE89370400440532013000)
5 new transaction(s) found.
✓ 5 created, 0 duplicates skipped.
✓ 5 transaction(s) categorized as "YNAB Synced".

✓ Sync complete. 5 transaction(s) synced.

I run it when I feel like it. No daemon, no cron, no web UI to log into.

No state files

Flusso doesn't track anything itself. No SQLite, no JSON ledger. MoneyMoney already lets you put a category on every transaction, so I use that: if a transaction has the sync category, it's done; if it doesn't, it goes to YNAB. That's the whole bookkeeping layer.

Running it twice in a row does nothing the second time. Running it after a crash mid-sync picks up where it left off. YNAB also deduplicates on its end via import_id, so even if Flusso loses track somewhere, YNAB won't double up. And because the state lives on the transaction itself, I can open MoneyMoney and see at a glance which transactions are synced, instead of trusting that some database knows.

Dry run

Before anything goes near YNAB, I can preview it:

$ 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.

This is the flag I use most often after sync. When I add a new account or change the start date, it tells me what's about to land in YNAB and, just as importantly, what's being filtered out. It's also the sanity check before the first real sync of a new account, when getting it wrong would mean cleaning up a hundred transactions in YNAB by hand.

How it works

flusso sync
    |
    |-- For each configured account:
    |
    |-- osascript: ask MoneyMoney to export transactions as plist
    |
    |-- plutil: convert plist XML to JSON
    |
    |-- jq: filter (booked, not in sync category = not yet synced)
    |
    |-- curl: POST to YNAB API (/v1/budgets/{id}/transactions)
    |
    +-- osascript: assign sync category to each synced transaction

Everything in there ships with macOS except jq. AppleScript talks to MoneyMoney, plutil turns the plist into JSON, jq filters it, curl posts to YNAB, and then AppleScript goes back to MoneyMoney to tag each synced transaction. The only thing to install is jq.

The filter is one jq call:

jq --arg cat_uuid "$SYNC_CATEGORY_UUID" '
  (.transactions // .)
  | if type == "array" then . else [.] end
  | map(select(
      .booked == true
      and (.categoryUuid != $cat_uuid)
  ))
'

Two conditions: the transaction is booked (so pending entries are skipped), and it doesn't already carry the sync category. The osascript calls on either end are the only macOS-specific bits.

Keeping the API token out of ps

The config file holds a YNAB Personal Access Token, so the config directory is 700 and the file itself is 600. The less obvious bit: API calls write the POST body to a temp file instead of passing it as a -d argument to curl. Pass the token on the command line and anyone running ps can read it. Temp file avoids that.

Inputs also get validated before they reach AppleScript, because AppleScript injection is a real thing and not one I want to debug.

Install

brew tap johannesbraeunig/flusso
brew install flusso
flusso setup

flusso setup is an interactive wizard: YNAB token, budget, the MoneyMoney-to-YNAB account mapping for each thing you want to sync, and the category to use for tagging. Takes about two minutes. After that it's flusso sync, or flusso sync --dry-run if you want to look before you leap.

The only dependency that isn't already on your Mac is jq.

Source: github.com/johannesbraeunig/flusso.