Settling up
A jar’s balance isn’t a column — it’s the running difference between everything one member paid for others minus everything those others paid back. When the trip ends, you settle: each person makes one or two transfers and the balances zero out.
How balances are computed
Section titled “How balances are computed”The unit of obligation is receipt_splits. Each receipt with N people on it produces N split rows: who owes whom, how much, in the jar’s base currency. The pre-settlement view of the jar is just the sum of those rows, grouped by debtor → creditor pair.
settlements then subtract from that view. A settlement row is “X paid Y the amount Z on date D” — same shape as a split row, opposite sign. The net balance is splits minus settlements per pair.
The aggregation is a pure function:
balance(jar) = group_by_pair(receipt_splits) - group_by_pair(settlements)Both sides live in src/lib/balances.ts and are derived live; nothing is denormalised.
Recording a settlement
Section titled “Recording a settlement”Settlements come from the user, not the system — Splitjar doesn’t move money. You log that a transfer happened: who paid, who received, how much, when.
The endpoint is POST /api/groups/:idOrSlug/settlements with { from, to, amount, currency, paidAt, note? }. Both members must be in the jar; the amount is in the jar’s base currency.
The oversettlement guard
Section titled “The oversettlement guard”If you try to settle more than the outstanding balance between two people, the request is refused with a 400 oversettlement. Reasoning:
- A settlement should reduce the outstanding obligation, not invert it
- Logging “I paid Steve $200” when the actual debt was $50 silently flips Steve into owing you $150
- That’s almost always a typo, and it’s caught most often when a settle-up tour breaks down (“wait, why does Steve owe me money now?”)
A small grace band exists so a round-to-the-nearest-dollar settlement on a $42.73 debt isn’t blocked. The guard kicks in when the overshoot is meaningful.
Closing the jar
Section titled “Closing the jar”When every pair-balance is zero (or close enough that the per-pair grace allows it), the jar is “settled”. The owner can mark it closed from the jar header. Closed jars are read-only — no new receipts, no new splits — but exports, balances, and history stay accessible forever.
Why we don’t compute “minimum transfers”
Section titled “Why we don’t compute “minimum transfers””A correctly-played jar has at most N-1 settlements between N people. Some products try to compute the minimum number of transfers to balance everyone — solving the cyclic-debt graph for the smallest spanning set.
We don’t, because:
- It’s a graph theory problem with multiple equally-valid answers; any choice is opinionated
- People settle along social lines, not optimal-transfer-graph lines (“I’ll pay Steve, he’ll cover the others”)
- The few-percent reduction in transfers isn’t worth the surprise of telling someone “actually pay this random person, not the one you owe”
Splitjar shows the per-pair balance and lets you settle in whatever order makes sense to your group.