Skip to main content
← Back to work

Miyo

Solo · 2026

Budgeting for couples, private by default

Miyo: Budgeting for couples, private by default
Account privacy
3 statesPrivate, shared, or hidden
CSV formats
4 banksImported with duplicate protection
Privacy boundary
Server-sideEnforced beyond the interface

Overview

Budgeting for life together: one shared household plan for the bills and goals you share, a privacy dial on every account you each keep, and a weekly Money Date. Free to start for one person.

What I built

  • →Per-account privacy states—private, shared, or hidden—enforced before data reaches the interface
  • →Two-member households with expiring invites, ownership transfer, and partner views
  • →Plaid sync with encrypted tokens, cursor-based updates, and webhook deduplication
  • →CSV import for four banks with duplicate and formula-injection protection
  • →Debt payoff comparisons across avalanche, snowball, and hybrid strategies

How it works

Before

Two people, two banks, one spreadsheet nobody updates. Every budgeting app assumed either one wallet or two strangers: combine everything, or keep separate apps and talk about money from memory. The first question a couple asks, who can see this, had no answer in the product.

The flow

  1. 01

    Link a bank through Plaid or import a CSV from one of four banks.

  2. 02

    Set each account's dial: private, shared or hidden.

  3. 03

    The server resolves what the partner may fetch for this household and returns only that.

    the boundary
  4. 04

    The partner's screen renders what came back. There is nothing hidden on it to find.

  5. 05

    Shared bills and goals roll into one household plan: the month bar.

  6. 06

    A weekly Money Date reviews the plan together.

Turn the dial

The setting on an account changes what the server sends your partner, not what their screen hides. Try it: Sam is the partner here.

Your account

Chase checking

$4,210.18

Who can see it

Sam sees the balance and the transactions.

What Sam sees

  • Joint savings$12,400.00
  • Chase checking$4,210.18

2 accounts in Sam's list

GET /api/household/accounts, as Sam

[
  { "name": "Joint savings", "balance": 12400 },
  { "name": "Chase checking", "balance": 4210.18 }
]

Both accounts come back in full. The server, not the screen, decided that.

One decision I reversed

Was

Privacy as a flag the interface respects: the account query returned the whole household and the client hid the rows marked private.

Now

Privacy as a boundary the server enforces: account queries, transaction lists, totals, exports and mutations all start from the verified member and household, and a hidden account is never returned at all.

Why. The toggle version looked correct on screen while leaving the data available to any call that skipped the screen. Moving the promise into the domain layer meant one forgotten endpoint could no longer undo it, and it made the rule testable without a browser: two members, one account state, one expected answer.

Key product decision

Treat privacy as a server-side data boundary—not a visibility toggle—so a partner can never retrieve an account the interface says is hidden.

Case study notes

01

Context

Couples merging money face an all-or-nothing choice — fully combine accounts or stay siloed — and neither respects that some things are shared and some are still “mine.”

02

Implementation

I started with the household and account-permission model, enforced privacy on the server, then added bank sync, CSV import, budgeting, debt planning, and net-worth views around that boundary.

03

Current state

Live at joinmiyo.com with an interactive demo household. Free for one person; the couple plan starts when a partner is invited. Bank sync and onboarding are still being completed.

Roadmap

In progress
  • Complete and monitor the real-bank Plaid Transactions lifecycle
  • Deliver partner invites and guided household onboarding
  • Add operations views for sync, webhook, billing, and deletion health

Source is private — want a walkthrough or a deeper look? Email me.