Most integrations between business systems start life as an export. Someone downloads a CSV from one system and imports it into another. Then someone writes a small script to do it automatically. A year later the script is doing ten things, nobody remembers how, and the numbers in two systems no longer agree.

Two-way sync promises to fix this: change something in one place and it shows up everywhere it should. Done well, it removes hours of re-keying and gives every team the same numbers. Done badly, it creates duplicates, overwrites good data with bad, and fails silently. Here is how we approach it.

One-way exports versus two-way sync

A one-way export copies data from a source to a destination. It is simple and fine for reporting, where the destination is read-only. It breaks down when people start editing the copy, because those edits never travel back.

Two-way sync means changes can originate in either system and flow to the other. That is far more useful, and far easier to get wrong. The hard questions are all about ownership: if a value changes in both places, which one wins?

Decide the source of truth for every field, not every record

The most common mistake is deciding that one system “owns” customers or jobs as a whole. In practice, ownership is per field.

For a customer record, for example:

Field Owned by
Name, address, contacts CRM or job management system
Credit limit, payment terms Accounting system
Outstanding balance Accounting system
Pricing tier Inventory or ERP system

Write this table down for every entity you sync. Each system then only writes the fields it owns, and reads the rest. Most sync conflicts disappear once ownership is explicit.

Make every operation safe to repeat

Integrations get interrupted. A network drops, an API times out, a job restarts halfway through. If running the same sync twice creates two invoices or doubles a stock adjustment, you have a problem waiting to happen.

The fix is idempotency: designing each operation so that repeating it has the same effect as running it once. In practice that means:

  • Storing a stable reference to the matching record in the other system, and checking it before creating anything.
  • Using “create or update” logic rather than blind inserts.
  • Tagging each change with a unique key so a repeated message is recognised and skipped.

Expect failure, and retry sensibly

APIs go down, and most enforce rate limits. Xero, for example, publishes the API limits apps must work within. A sync that assumes every call succeeds will eventually lose data.

Good integrations retry temporary failures with increasing delays between attempts. They do not retry permanent failures, such as a validation error, because those will fail every time. Anything that still fails after a few attempts goes into a queue that a person can review, rather than disappearing.

Log every outcome

Every sync operation should leave a record: what it tried to do, whether it succeeded, was skipped or failed, and why. This sounds like overhead until the first time someone asks why an invoice is missing.

When we built a cashflow model that writes planned payment dates back to Xero, logging the outcome of every update was part of the design from day one. The finance team can see what changed in Xero and when, without asking a developer.

When a change made in Xero doesn’t flow back

A pattern we see often: a job management system pushes invoices and costs into Xero. Later, someone makes an adjustment directly in Xero, perhaps a credit note or a cost correction. In many setups, that change never flows back to the job system. The job’s margin in one system no longer matches the accounts in the other, and month-end margin reports disagree.

There are a few ways to handle it, and the right one depends on the business:

  • Set a process rule. Adjustments are always made in the source system, never in the destination. Simple, but it relies on people remembering.
  • Detect and sync back. Watch the destination for changes, using webhooks where the system offers them or a scheduled comparison where it doesn’t, and push the relevant fields back to the source.
  • Report the exceptions. Run a regular reconciliation that lists every record where the two systems disagree, so someone can resolve it deliberately.

Often the answer is a mix: a clear rule, backed by an exceptions report that catches the cases where the rule wasn’t followed.

Monitor it like a system, not a script

An integration your business relies on deserves the same care as any other system:

  • Alerts when failures exceed a threshold, sent to someone who will act on them.
  • A simple health view: records synced today, failures, items waiting for review.
  • A regular reconciliation of key totals between systems.
  • A named owner, and documentation that someone other than the original developer can follow.

Before you build an integration

  1. List every entity you need to sync, and why.
  2. For each one, write down which system owns each field.
  3. Decide how matching records are identified in both systems.
  4. Define what happens on conflict, failure and retry.
  5. Design the outcome log before writing the sync logic.
  6. Decide who gets alerted, and how quickly they need to respond.
  7. Plan a reconciliation that proves the systems agree.

If an integration you already have can’t answer these questions, it is worth reviewing before it causes a month-end surprise.

Where to start

Start with the integration that causes the most re-keying or the most arguments about whose numbers are right, and write down the field ownership table for it. If it has an API or an export, we can connect it properly. See how we approach integration and automation, or get in touch to talk through the systems you want working as one.