We’re excited to announce a major update to conductor-node that brings full support for our new QuickBooks Desktop API v2.0. This new version introduces significant improvements in usability, consistency, and functionality.

While the old version of conductor-node will continue to work, it will not receive any further updates. We strongly recommend upgrading to take advantage of the new features and improvements.

Key improvements

Our new QuickBooks Desktop API v2.0 has been completely redesigned from the ground up. We’ve carefully chosen every field name, parameter, and method description to be significantly more intuitive and clearer than QuickBooks’s original documentation. The API structure has been simplified while maintaining full functionality.

Major improvements include:

  • Improved parameter and fields names,
  • Completely rewritten inline documentation
  • Automatic pagination support
  • Automatic retries with exponential backoff
  • Configurable timeouts
  • New methods like .retrieve() and .delete()
  • All fields included in responses
  • Consistent array handling

How to upgrade

For now, we ask that you install the new SDK from a git repo instead of npm because we are not ready to prompt all existing users of our npm package to upgrade. Any new Conductor users should absolutely use this new version!

⚠️ Breaking changes

While this update includes several breaking changes, they only affect naming conventions and API structure. Your core QuickBooks business logic remains unchanged. We’ve simply made the interface cleaner, more intuitive, and more robust, while adding new features like automatic pagination and retries.

1. Constructor

Old:

import Conductor from "conductor-node";

const conductor = new Conductor("{{YOUR_SECRET_KEY}}");

New: You can now automatically load your secret key from the CONDUCTOR_SECRET_KEY environment variable (or pass it to the constructor as an argument like before):

import Conductor from "conductor-node";

const conductor = new Conductor({
  apiKey: process.env["CONDUCTOR_SECRET_KEY"], // This is the default and can be omitted
});

2. Resource names

Old: Singular resource names.

await conductor.qbd.bill.*
await conductor.qbd.invoice.*
await conductor.qbd.itemInventory.*

New: Plural resource names and clearer naming.

await conductor.qbd.bills.*
await conductor.qbd.invoices.*
await conductor.qbd.inventoryItems.*

3. Method names

Old: Odd naming conventions.

await conductor.qbd.bill.query(...)
await conductor.qbd.bill.add(...)
await conductor.qbd.bill.mod(...)

New: Intuitive naming following REST conventions.

await conductor.qbd.bills.list(...)
await conductor.qbd.bills.create(...)
await conductor.qbd.bills.update(...)
await conductor.qbd.bills.retrieve(...) // New!
await conductor.qbd.bills.delete(...) // New!

4. End-user ID parameter

Old: Required as its own argument.

await conductor.qbd.bill.query("end_usr_1234abcd", { ... });

New: Required as a conductorEndUserId parameter in the parameters object.

await conductor.qbd.bills.list({
  conductorEndUserId: "end_usr_1234abcd",
  // ... other params
});

5. Request parameters

All request parameters are now in camelCase and have been renamed for clarity and consistency. Most of the changes are straightforward that you can find using autocomplete in your IDE or checking the APIs in the sidebar.

Old: Odd naming conventions.

await conductor.qbd.bill.add("end_usr_1234abcd", {
  VendorRef: { ListID: "..." },
  RefNumber: "...",
  ExpenseLineAdd: [
    {
      Amount: "1.00",
    },
  ],
});

New: camelCase and clearer naming.

await conductor.qbd.bills.create({
  conductorEndUserId: "end_usr_1234abcd",
  vendorId: "...",
  refNumber: "...",
  expenseLines: [
    {
      amount: "1.00",
    },
  ],
});

6. Response field names

All output fields are now in camelCase and have been renamed for clarity and consistency. Most of the changes are straightforward that you can find using autocomplete in your IDE or checking the APIs in the sidebar.

Here are some key renamed fields:

  • TxnID and ListIDid
  • TimeCreated and TimeModifiedcreatedAt and updatedAt
  • EditSequencerevisionNumber
  • *Ref fields (e.g., CustomerRef) → simplified (e.g., customer)

Old: Odd naming conventions.

const invoice = await conductor.qbd.invoice.query("end_usr_1234abcd", "...");
// {
//   TxnID: "...",
//   TimeCreated: "...",
//   TimeModified: "...",
//   EditSequence: "...",
//   TxnNumber: "...",
//   CustomerRef: {
//     ListID: "...",
//     FullName: "..."
//   }
// }

New: camelCase and clearer naming.

const invoice = await conductor.qbd.invoices.retrieve({
  conductorEndUserId: "end_usr_1234abcd",
  id: "...",
});
// {
//   id: "...",
//   objectType: "qbd_invoice",
//   createdAt: "...",
//   updatedAt: "...",
//   revisionNumber: "...",
//   transactionNumber: "...",
//   customer: {
//     id: "...",
//     fullName: "..."
//   }
// }

7. Query/list request structure

Old: Odd naming conventions and complex structure.

const invoices = await conductor.qbd.invoice.query("end_usr_1234abcd", {
  ModifiedDateRangeFilter: {
    FromModifiedDate: "...",
    ToModifiedDate: "...",
  },
  AccountFilter: {
    ListID: "...",
  },
  RefNumberFilter: "...",
  RefNumberRangeFilter: {
    FromRefNumber: "...",
    ToRefNumber: "...",
  },
  MaxReturned: 100,
});

New: Significantly simplified structure and clearer naming.

const invoices = await conductor.qbd.invoices.list({
  conductorEndUserId: "end_usr_1234abcd",
  updatedAfter: "...",
  updatedBefore: "...",
  accountId: "...",
  refNumber: "...",
  refNumberFrom: "...",
  refNumberTo: "...",
  limit: 100,
});

8. Query/list response structure

To support automatic pagination, the query/list method now returns results in an object wrapper with properties for pagination metadata.

Old: Returns an array of results directly.

const invoices = await conductor.qbd.invoice.query("end_usr_1234abcd");
// [
//   {
//     TxnID: "...",
//     TimeCreated: "...",
//     ...
//   }
// ]

New: Returns results in an object wrapper with properties for pagination metadata.

const response = await conductor.qbd.invoices.list({
  conductorEndUserId: "end_usr_1234abcd",
});

console.log(response);
// {
//   nextCursor: "...",
//   hasMore: true,
//   remainingCount: 100,
//   data: [
//     {
//       id: "...",
//       objectType: "qbd_invoice",
//       createdAt: "...",
//       ...
//     }
//   ]
// }

9. Enforced limits

Previously, the API would return all matching items by default. This could lead to performance issues when unintentionally retrieving thousands of records, potentially causing long response times, overloading QuickBooks Desktop, and consuming excessive memory.

To prevent these issues, we now limit responses to a maximum of 150 items by default. This limit was carefully determined through extensive testing across different scenarios and system configurations to optimize performance while avoiding timeouts and resource constraints.

With the new automatic pagination, you can efficiently retrieve all records when needed, without worrying about these performance concerns.

10. Consistent arrays

Fields that can have multiple values are now always arrays, even when there’s only one item:

const response = await conductor.qbd.invoices.list({
  conductorEndUserId: "end_usr_1234abcd",
});

// All responses with potential multiple items use arrays consistently
console.log(response.data[0].lines); // Always an array, even if there's only one line

New features

Automatic pagination

You can now easily paginate through all results. See the automatic pagination section of the README for more details.

async function fetchAllQbdInvoices(params) {
  const allQbdInvoices = [];
  // Automatically fetches more pages as needed
  for await (const invoice of conductor.qbd.invoices.list({
    conductorEndUserId: "{{YOUR_END_USER_ID}}",
    ...params,
  })) {
    allQbdInvoices.push(invoice);
  }
  return allQbdInvoices;
}

Automatic retries with exponential backoff

Intelligently retries requests, but only those requests that can potentially succeed when retried (such as network errors).

New retrieve and delete methods

All objects now have a retrieve method and all transaction objects have a delete method.

await conductor.qbd.bills.retrieve({ conductorEndUserId: "...", id: "..." });
await conductor.qbd.bills.delete({ conductorEndUserId: "...", id: "..." });

Complete field returns

The new API now returns a consistent response structure with all possible fields, making it easier to work with the data. Fields that have no value are explicitly set to null rather than being omitted, and empty array fields return [] instead of being excluded. This means you can reliably access any field without checking if it exists first, and array operations will work consistently without special handling for missing fields.

Missing QuickBooks Desktop types

Several obscure QuickBooks Desktop types/endpoints from the previous version of conductor-node are not yet available in the new SDK. You can verify this by examining the list of object types in the sidebar. Please contact support if you need a specific type, and we’ll add it to the next release.

For more detailed information about the SDK, please check our GitHub repository.