← Back to The Mixing Bowl
building in publicpaymentsproduct

The Nightly Sync That Kept Un-Paying My Customers

By Chad Holdorf·July 23, 2026·5 min read

Bakery Buddy pulls invoices in from FreshBooks every night. It's a real sync, not a demo. It runs on its own schedule before anyone's awake, so a baker's order list is caught up by the time they check their phone in the morning.

For a while, that sync had a bug I didn't catch until I went looking for it.

What the sync was actually doing

Every night, it read each invoice's status out of FreshBooks and used it to recompute the matching order's status inside Bakery Buddy. Sent became "invoice_sent." Paid became "scheduled" or "completed," depending on the due date. Then it wrote that status, every single run, no matter what was already sitting there.

That's a fine plan as long as FreshBooks is the only place a payment can happen. It isn't. Bakery Buddy has its own payment link sitting right on the customer's invoice page, so a customer can pay their balance without ever touching FreshBooks.

Here's the sequence that broke:

  • A customer opens their invoice link and pays their balance in full, right there in Bakery Buddy.
  • Bakery Buddy marks the order "scheduled" and drops the outstanding balance to zero.
  • FreshBooks has no idea any of that happened. As far as it knows, the invoice is still "sent."
  • The next morning's sync reads FreshBooks, sees "sent," and writes "invoice_sent" straight back over the order.

A fully paid order would sit there reading "Invoice Sent" indefinitely, until someone happened to open FreshBooks itself and notice the money had actually landed. The same unconditional write had a second failure mode too. If a baker marked an order "cancelled," the next sync would drag it right back onto the active pipeline, because FreshBooks never learned about that decision either.

Why "just trust the source system" stops working once there are two

The normal instinct when you build a sync is to trust the system you're importing from. That's correct advice right up until there are two systems that can both change the same field, and neither one can see what the other just did. FreshBooks wasn't wrong about what it knew. It just didn't know everything, and the sync treated its partial view as the final word every single night.

The fix

I gave every status in the order's lifecycle a rank: inquiry, then invoice_sent, then payment, then scheduled, then completed. The sync now only applies the status it read from FreshBooks if that status would move the order forward in that ranking. If Bakery Buddy already has the order further along than FreshBooks thinks it is, the sync leaves it alone. If a baker put an order into a terminal state like cancelled or declined, the sync doesn't touch it at all, no matter what FreshBooks says.

Nothing about the actual payment math changed. The balance and the amount paid were already being handled correctly. The whole fix was one rule: an external sync can move a status forward, never backward, and it can never overrule a decision a human already made.

I ran it against seven scenarios before it went live, covering both failure modes plus the normal forward-moving cases, to make sure real syncs still worked and the clobbering didn't. No live order had actually hit the broken state yet when I fixed it. That's the best outcome you can hope for with a bug like this: caught in the code, not in a customer's inbox.

The part that generalizes past bakeries

If you're building anything that pulls data in from an outside system, ask yourself one question before the sync ever ships: can that outside system see everything mine already knows? If the answer is no, an unconditional overwrite will eventually erase something true. The fix is almost never "sync less often." It's deciding, explicitly, which direction information is allowed to travel, and writing the code so it physically can't run the other way, even on the night you forget to think about it.

Frequently Asked Questions

What was actually broken?

A nightly sync from FreshBooks was rewriting an order's status unconditionally. If a customer paid their balance directly through Bakery Buddy, the next sync could still overwrite that order's status back to something like "invoice sent," because FreshBooks never learned the payment had happened.

Did this actually happen to a real customer's order?

No. It was caught and fixed before any live order hit the broken state. The bug existed in the code, but no paid order was ever actually flipped back to unpaid in production.

What's the actual fix?

The sync can now only move an order's status forward through its lifecycle (inquiry, invoice sent, payment, scheduled, completed), never backward, and it can't touch an order a baker has already put into a terminal state like cancelled or declined.

Why does this matter if I'm not running two systems like FreshBooks and Bakery Buddy?

Any integration that pulls data in from an outside system needs the same rule. If that outside system can't see everything your own system already knows, letting it overwrite unconditionally will eventually erase something true.

Written by
Chad Holdorf
Founder, Bakery Buddy

I build Bakery Buddy for my wife Lindsay's cake studio, Marin Cake Studio, and I read every line a sync like this touches before it runs against her real orders.


Ready to put this into practice?

Join the Waitlist