Skip to content
Desktop Accounting API

Incremental sync

Most integrations copy part of a customer’s QuickBooks data into their own database and keep it current. QuickBooks Desktop has no change feed, but every record carries an updatedAt timestamp and QuickBooks keeps a list of recent deletions. Together they give you a reliable incremental sync.

  1. First sync. List each object type you need without a date filter, or in date windows for large files (see below). Store each record by its QuickBooks id, with its revisionNumber and updatedAt.
  2. Remember a watermark per end user and object type: the time you started the sync, as QuickBooks sees it.
  3. Each later sync. List with updatedAfter set to the last watermark, minus a small overlap. Upsert by id.
  4. Deletions. Call GET /v1/quickbooks-desktop/deleted-transactions and GET /v1/quickbooks-desktop/deleted-list-objects with deletedAfter set to the same watermark, and remove those records.
import { DesktopAccountingApi } from "@desktopaccountingapi/quickbooks-desktop";
const qb = new DesktopAccountingApi().forEndUser("eu_01j9...");
const lastWatermark = "2026-10-04T00:00:00"; // from your database
for await (const invoice of qb.qbd.invoices.list({ updatedAfter: lastWatermark })) {
// upsert by invoice.id
console.log(invoice.id, invoice.revisionNumber);
}
const deleted = await qb.qbd.deletedTransactions.list({
transactionTypes: ["invoice"],
deletedAfter: lastWatermark,
});
for (const d of deleted.data) {
// delete your copy of d.id
console.log(d.id);
}

QuickBooks timestamps use the clock and time zone of the customer’s computer, for example 2026-10-05T09:14:03-07:00. Filters work on the same clock:

  • updatedAfter and updatedBefore accept a date (2026-10-04, meaning the start or end of that day), a local date-time without offset (2026-10-04T09:00:00, read in the computer’s time zone), or a date-time with offset (2026-10-04T16:00:00Z, converted to the computer’s time zone).
  • Use the largest updatedAt you received as the next watermark, or the time the sync started minus a few minutes. Overlap a little and deduplicate by id; re-reading a record is harmless, missing one is not.
  • revisionNumber changes on every edit. If the stored revision equals the new one, you can skip the record.
Endpoint Covers Filter
GET /v1/quickbooks-desktop/deleted-transactions Deleted invoices, bills, checks and other transactions transactionTypes (required), deletedAfter, deletedBefore
GET /v1/quickbooks-desktop/deleted-list-objects Deleted customers, vendors, items, accounts and other list objects objectTypes (required), deletedAfter, deletedBefore

QuickBooks keeps deletions for 90 days. Run incremental syncs more often than that, and do a full comparison by ID if a customer has been offline longer.

List objects (customers, items, vendors) are usually deactivated rather than deleted. Include inactive records with status: "all" so a deactivation shows up as isActive: false.

A single paged query must finish inside one QuickBooks session (see Pagination). For a first sync of a large file, split the range into windows:

  1. Choose a window, such as one month of updatedAfter / updatedBefore, or one month of transactionDateFrom / transactionDateTo for transactions.
  2. Read each window completely before moving on.
  3. If a window fails with CURSOR_EXPIRED, retry that window, or split it in half.

For several end users, run their syncs in parallel; each company file has its own queue.

GET /v1/quickbooks-desktop/transactions returns a summary of every transaction type in one paged list, filtered by date, account, entity or type. Use it to find what changed across all types, then fetch full records from the type-specific endpoints.

Customer-started syncs (“Sync with QuickBooks” buttons) give the best experience, because a person is there to fix a connection problem. Scheduled background syncs work too: run them with async requests, record failures per customer, and show the last sync result in your product. Avoid tight polling loops; one sync every few minutes is plenty for most products, and each request waits its turn in the company file’s single queue.