LedgerBridge: two-way invoice sync that survives the real world
Keeps an internal invoicing system and QuickBooks Online in lockstep through duplicate, out-of-order and dropped webhooks: every write idempotent, every conflict held for a human, every event replayable.
TL;DR
- Two-way invoice sync between an internal system and QuickBooks Online, built to survive duplicate, out-of-order and dropped webhooks.
- Every write is idempotent: a create that times out after it actually landed is adopted, never duplicated.
- 87 backend + 15 web tests, all deterministic against a real Postgres schema, no DB mocks.
Problem
A webhook is a ping, not the truth.
Real integrations never deliver clean events. Webhooks arrive duplicated, out of order, with half a payload, or never at all. Trust the payload and you double-charge, drop an edit, or wedge two ledgers out of sync. LedgerBridge treats every event as a hint to go refetch the real state, then applies it exactly once.

Architecture
internal system ─webhook─▶ ingest (verify · dedupe · enqueue)
│
durable outbox ─▶ leased worker: refetch → map → resolve → apply
│ │
reconciler sweeps drift ◀────┘ idempotent write ─▶ QuickBooks Online ┘
Two ideas carry the whole design: refetch, don't trust the payload, and idempotent by external id. A Postgres outbox drained by a single leased worker (FOR UPDATE SKIP LOCKED) means duplicate or out-of-order events can never double-apply.

Key decisions
Refetch on every event, not the webhook body
Chose to refetch current state from the source before applying, instead of acting on the payload. Trade-off: an extra API round-trip per event, but out-of-order and partial webhooks stop being a problem in one move.
Flag-and-hold conflicts, not last-writer-wins
When both sides edit the same field, chose to hold both directions and queue the conflict for a human, instead of silently picking a winner. Trade-off: someone has to resolve it, but blind last-writer-wins on unsynchronized clocks quietly loses real money.

A durable outbox over a hosted queue
Chose a Postgres outbox table plus a leased worker over SQS or Inngest. Trade-off: I own the retry and backoff logic, but the entire pipeline is one queryable, replayable table, and the write-up names the hosted queue as the production upgrade.
Idempotent by external id
Before creating in QuickBooks, chose to check by document number first. Trade-off: an extra read, but a create that timed out after it actually landed gets adopted instead of duplicated. That is the one failure that would corrupt a ledger silently.

A webhook is a ping, not the truth. Once every event just meant "go refetch and converge," idempotency and conflict handling stopped being special cases and became the default.
the idea the whole engine rests on
Harder than expected
Loop prevention. An outbound write to QuickBooks fires a webhook straight back; without echo detection (a state hash plus the QuickBooks SyncToken) the two ledgers ping-pong forever. Proving the echo is dropped in both directions, internal to QBO and back, took the most careful test design in the project.
Results
- 87 + 15 deterministic tests, backend + web, real Postgres, no DB mocks
- 10 reproducible end-to-end flows, each pinned to a test
- 9 findings from an independent security review, all fixed
Live + source
The live dashboard runs on seeded data, no login: watch events flow, open a conflict, replay a dead-letter. The demo panel drives a real QuickBooks sandbox.