QuickBooks Desktop Integration: The Web Connector and qbXML

Field software rarely talks to QuickBooks Desktop. How the Web Connector and qbXML work, and how we built a connector that cannot invoice a job twice.

Written by Serg Litvinov

Reporting basis: A QuickBooks Desktop connector build and Intuit documents linked below.

. 8 min read

If you searched something like “does Jobber integrate with QuickBooks Desktop,” you already have the answer that matters: mostly, no. Jobber, Housecall Pro and almost every modern field-service platform built its QuickBooks connection for QuickBooks Online, a cloud company file with a proper REST API. QuickBooks Desktop is a different animal, and most SaaS vendors quietly decided it wasn’t worth building for.

We built one anyway, for a real client. This is what it took, and why “just move to Online” is a poor thing to say to a contractor whose bookkeeper has run Desktop for fifteen years.

Why QuickBooks Desktop doesn’t get the API treatment

QuickBooks Online has a public, documented REST API. You authenticate, you send JSON, you get JSON back. Software written in the last decade assumes accounting integrations work that way, and for QBO they do.

QuickBooks Desktop comes from an older world. It’s a Windows application that owns a local company file, a .QBW file sitting on somebody’s PC or a small office server. No public web API sits in front of it, because there’s no always-on server process to host one. The company file is only live while QuickBooks Desktop is open on that specific Windows machine, or while its background services are running there.

Intuit’s answer to external software reaching that file comes in two pieces, both purpose-built for Desktop and both meaningfully older in design than a modern REST API:

qbXML is the request and response language, and it is XML. An integration builds a qbXML request document (“add this customer,” “add this invoice,” “query invoices modified since this timestamp”), and QuickBooks Desktop answers with a qbXML response document holding the real record it created, including the actual invoice number and internal transaction ID it assigned.

The Web Connector delivers it. It’s a small Windows application Intuit ships alongside Desktop. You register a .qwc file with it (an XML file naming your integration and its endpoint), and the Web Connector then polls that endpoint over SOAP on a schedule set in the .qwc file itself, commonly every few to tens of minutes and fully configurable, asking whether you have any qbXML work for it. When you do, it fetches the request, hands it to the open QuickBooks Desktop company file on that machine, and posts the qbXML response back to you on its next poll.

So your web application never talks to QuickBooks Desktop. The Windows Web Connector talks to QuickBooks Desktop, on a machine you don’t control, on a schedule you don’t fully control, and your web application talks to the Web Connector over SOAP from outside. That path is slow, it fights anything asynchronous, and every step in it can fail on its own: the Windows PC can be off, QuickBooks Desktop can be closed, somebody can have the company file open in single-user mode, or the Web Connector session can time out mid-sync. A field-service platform selling thousands of customers a predictable, cloud-only QBO integration has no reason to build and support all of that for the shrinking slice of customers still on Desktop. That’s the honest, unglamorous reason most of them don’t.

What we built

For a real HVAC contractor in southern Ontario (three to four technicians, one dispatch office, QuickBooks Desktop running on their own Windows PC and never leaving it), the accounting integration sat at the centre of the build from the first week. Scheduling, the technician mobile flow and office pricing review are covered in the case study on this engagement. This guide sticks to the accounting connector.

QuickBooks Desktop is the single ledger of record. The web application keeps no invoice or payment table of its own. It builds a reviewed, priced work order on its side, hands that to QuickBooks Desktop through the Web Connector, and QuickBooks assigns the real invoice number. An earlier design also kept a local invoice record in case QuickBooks was unreachable, and we deleted it on purpose. A shadow copy of “what was invoiced” sitting outside the accounting system drifts from the real one over months, and reconciling that drift a year later is worse than the problem you were solving.

The connector runs as its own module, separate from the field-service application logic. The whole build comes to roughly 10,100 lines of application code, split cleanly between the scheduling and work-order domain and the QuickBooks connector boundary, so qbXML plumbing never leaks into the code that prices a job or renders a work order. That split also let us test the connector against recorded qbXML fixtures (real captured request and response pairs) without a live company file to run the suite. Roughly 70 automated test files cover the build overall, and a meaningful slice of those exercise the connector boundary against those fixtures.

The three safety rails that carry the weight

An accounting integration lives or dies on one question: can this system write something wrong to the ledger, and does it fail loudly when it tries. Three rules did the real work here.

1. No double-invoicing

The failure mode is easy to trigger by accident. A sync retries because the Web Connector session dropped mid-poll, or someone clicks “send to QuickBooks” a second time because the first click looked dead, and the same job gets invoiced twice in the real company file. Then somebody has to spot it and manually void or delete a duplicate transaction inside QuickBooks itself, a bookkeeper’s afternoon gone to a bug that should never have shipped.

One rule guards it, enforced before any invoice-creation request is even built: refuse outright when the job already carries a QuickBooks transaction ID. No warning with an override, refuse. A job has never been invoiced and is eligible, or it has been invoiced and the only move left is opening the existing QuickBooks invoice. That transaction ID comes from QuickBooks itself, in the qbXML response after a successful invoice write, which is also why the system never invoices speculatively before that response arrives.

2. Cent-match reconciliation

QuickBooks Desktop does the math when it applies tax codes, rounds and totals a transaction. If the web application’s computed total for a job disagrees with what QuickBooks reports back for the transaction it just created, something is broken, a tax code mismatch, a rounding difference, a pricing bug, and it has to surface before anything downstream treats that invoice as real.

The rule: check the computed total against QuickBooks’ own returned total, to the cent, before marking the sync complete. A mismatch aborts. Nothing gets quietly recorded under a “close enough” tolerance, because close enough on money is exactly how a receivables sheet ends up with duplicate and hidden rows nobody can explain a year later, and an integration exists to stop that drift, not add to it.

3. Non-destructive customer sync

Customer records import from the real company file, and the field-service side can create or update them, never delete them. QuickBooks owns who a customer is and what they owe. A field-service application that can silently wipe a customer record because somebody archived it on the other side is a liability. Retiring a customer is a decision made in QuickBooks itself, by the person who owns the ledger.

Why augment the ledger instead of replacing it

The instinct from a software vendor’s side is to treat “the client is still on QuickBooks Desktop” as a problem you solve by moving them off it. Sell a subscription platform with its own invoicing, mention that it “also does QuickBooks Online sync” as a bonus feature, and let the legacy Desktop install fade out.

That pitch holds up in a shop with no years of habit invested in Desktop. It falls apart in a shop where the bookkeeper has run Desktop for a decade, knows every one of its quirks, and would have to relearn payroll, tax filing and year-end close on a different product because a field-service tool wanted it that way. Telling that shop to switch accounting software to get a better dispatch board hands the vendor’s integration problem to the client as a migration problem.

The other route costs more up front and looks less impressive on a slide. Treat QuickBooks Desktop as the permanent, authoritative ledger, and build the field-service layer to feed it correctly. In practice that means:

  • The field-service app never invents its own invoice numbers. QuickBooks assigns them.
  • The field-service app never assumes real-time connectivity. The Web Connector polls on its own schedule, so a sync queue and an honest “pending” state matter more here than they would against a cloud API answering in milliseconds.
  • Every write to QuickBooks is idempotent in effect, even when the trigger fires twice, which is what the no-double-invoicing rule above buys you.
  • Tax rules and jurisdiction-specific logic (in this build, Ontario HST and the Employment Standards Act’s overtime threshold) live as explicit, visible constants in the code where anyone can find them, because a QuickBooks-Desktop shop is very often a single-province, single-jurisdiction shop, and pretending otherwise adds complexity nobody asked for.

What this costs to build, honestly

This costs more than a modern API integration. A REST API with JSON and OAuth is a day or two of integration work for a competent developer. A Web Connector and qbXML integration is a materially bigger piece of engineering: registering the .qwc file, handling the SOAP session lifecycle, building and parsing qbXML request and response documents by hand since there’s no equivalent of a clean SDK client library the way there is for most REST APIs, plus the reconciliation and idempotency rules above. If your bookkeeper is willing to move to QuickBooks Online, that path is real, it’s faster to build against, and it deserves serious thought before you commit to a Desktop connector.

Making the field-service vendor’s integration easier is a poor reason for a shop to change accounting software. If the constraint is “this bookkeeper runs Desktop and that isn’t changing,” build for the constraint. The quiet alternative is pushing the client onto an accounting product they never asked to use.

When a general-purpose platform is honestly the better call

If your shop is already on QuickBooks Online, or you don’t mind moving to it, a platform like Jobber or Housecall Pro will connect to your accounting software in an afternoon, with a support team behind the integration and no custom engineering bill. A custom Desktop connector cannot match that. There’s no vendor support line for a one-off build, and every bug in it belongs to the build team instead of being spread across thousands of paying customers.

The case for a custom connector is narrow and specific. QuickBooks Desktop is staying, the office wants pricing rules or scheduling logic a generic platform doesn’t offer out of the box, and the cost of a purpose-built integration gets weighed honestly against a subscription that would require switching accounting software to use at all. That describes a real minority of shops. Work out which one you are before you commit budget either way.

Common Questions

Want an answer on your own workflow?

Send us a rate card or a sample tender. We'll tell you whether a build fits.

Send a sample
Talk through a job