Skip to content

QuickBooks Two-Way Sync

Two-way QuickBooks Online sync that stops on a conflict instead of guessing.

The short answer

QuickBooks Two-Way Sync moves twenty QuickBooks Online objects between Odoo and QuickBooks in the directions you switch on, one object at a time. When the same record was edited on both sides it does not pick a winner: it stops, compares the two versions field by field, and asks. It is for a business keeping its books in QuickBooks while running operations in Odoo.

What it is for

One-way sync is easy. Two-way is where books get quietly wrong. Someone in Odoo edits the address on an invoice while someone in QuickBooks changes the amount on the same one. Most connectors resolve that by writing whichever version they happen to read second, and nobody finds out until a month-end that will not tie. The other half is what happens when a write cannot be made honestly: a tax nobody has mapped, an item QuickBooks will not accept, a payment the platform has already locked. Guessing there is a wrong number in somebody's filing.

Platform
Odoo
Category
Connectors
Works with
Odoo 18 · Odoo 19
Technical name
qbo_sync
Status
Published on the Odoo App Store
On the store
Odoo 19
Pricing
Paid

What is inside it

  1. 01

    Both sides changed

    The record lands in a Conflicts queue with both payloads flattened to the same field paths and compared row by row. Keep Odoo, keep QuickBooks or ignore, and keeping QuickBooks re-reads the record live first.

  2. 02

    Proving it agrees

    It reads QuickBooks' own reports, puts the two receivable balances side by side, and then lists the documents behind any gap. There is a button on that screen to send the missing ones.

  3. 03

    Echo suppression

    Every successful write stores the QuickBooks SyncToken, the QuickBooks timestamp and the Odoo write date. The change feed is read against those three values, so your own write coming back is recognised and dropped.

  4. 04

    When it refuses

    A job that cannot be made correct stops in a Blocked state carrying a sentence that names the record and the fix, rather than a code, a stack trace or a silent skip.

  5. 05

    The board

    Eleven numbers, the first of which is how stale the change feed is, because a connector that has quietly stopped talking looks exactly like one with nothing to do.

  6. 06

    What moves

    Twenty objects, each a switch per direction on the connection, and every switch starts off. Customers, vendors, items, invoices, bills, payments and journal entries go both ways; estimates, purchase orders and time entries go out only.

  7. 07

    Mapping

    Accounts, tax codes, classes and locations are read from your company and cached, so you pick from a validated list. A code QuickBooks stops returning is archived rather than deleted, which would orphan the mapping.

  8. 08

    Tokens

    You register your own Intuit app, so the client id, secret and API quota are yours. A connection remembers which database authorised it and refuses to run from a restored copy.

What it looks like in practice

  1. The same invoice changed in Odoo and in QuickBooks

    Someone edited the address in Odoo. Someone else changed the amount on that invoice in QuickBooks minutes earlier, and neither knows about the other.

    Nothing is written. The invoice lands in the Conflicts queue with both payloads flattened to the same field paths, down to Line[0].SalesItemLineDetail.UnitPrice, and the choices are keep Odoo, keep QuickBooks or ignore.

    Behaviour of the shipped code.

    Both sides changedEcho suppression

  2. An invoice line carries a tax with no QuickBooks code

    The line has an Odoo tax nobody has mapped to a QuickBooks tax code. Guessing a rate would put a wrong number in a filing.

    The job stops in a Blocked state and prints the instruction itself: map Odoo tax GST 5% to a QuickBooks tax code. Nothing is skipped and nothing is approximated.

    Behaviour of the shipped code.

    When it refusesMapping

  3. Both receivable balances read the same and still disagree

    The Odoo receivable and the QuickBooks receivable come out level, which is usually taken as agreement and signed off.

    The reconciliation screen lists the documents behind the gap, so two invoices that never arrived and one document existing only in QuickBooks appear as named rows instead of netting off inside a total. A button sends the missing ones.

    Behaviour of the shipped code.

    Proving it agreesThe board

What it deliberately does not do

QuickBooks Desktop is a different product
A different API as well. This is QuickBooks Online only.
No payroll
The Accounting API has no payroll entity at all. Intuit's own advice is to post third-party payroll as journal entries, which this does sync.
No bank feeds
No entity exists. Downloaded transactions and the queue for review stay inside the QuickBooks interface.
No VAT or GST return filing
There is no filing endpoint. Intuit engineering has confirmed the Australian BAS report is not exposed either.
No combo or bundle products
QuickBooks has no single item for an invoice line to point at, so invoice the components. The export blocks with a reason rather than sending one component quietly.

Questions

Can I install this on Odoo Online?
No. Odoo Online cannot install modules that contain Python, so this runs on Odoo.sh or a self-hosted instance. Check the versions listed above for the series and edition it is verified against.
Does this work with QuickBooks Desktop?
No. QuickBooks Desktop is a different product with a different API, and none of this reaches it. The connector is QuickBooks Online only. If your books are on Desktop, nothing here will help until they move.
Will it sync my payroll or my bank feeds?
No, and neither exists to sync. The Accounting API has no payroll entity, so Intuit's own advice is to post third-party payroll as journal entries, which this does move. Bank feeds have no entity either; downloaded transactions and the for-review queue stay inside the QuickBooks interface.
What happens if I restore a production database onto staging?
The connection refuses to run. It remembers which database it was authorised from, so a restored copy stops rather than pushing test invoices into your live QuickBooks company. The same guard catches a connection whose QuickBooks company has been swapped underneath it.

Have something to build?

Tell us the problem. We'll come back with a plan, a price, and who'd actually build it.

  • Free scoping call
  • Reply within 1 business day
  • No lock-in