npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

quickbooks-api

v0.11.0

Published

A modular TypeScript SDK for seamless integration with Intuit QuickBooks APIs. Provides robust authentication handling and future-ready foundation for accounting, payments, and commerce operations.

Downloads

774

Readme

QuickBooks API SDK

Version Downloads GitHub Actions Workflow Status

A modular TypeScript SDK for seamless integration with Intuit QuickBooks APIs. Provides robust authentication handling and a future-ready foundation for accounting, payments, and commerce operations.

Key Features

  • OAuth 2.0 Authentication: Simplified and secure authentication flow.
  • Single Sign-On (SSO): Built-in OpenID Connect support for QuickBooks Single Sign-On with automatic ID token validation and user profile management.
  • Token Management: Automatic refresh, rotation, and secure serialization/deserialization.
  • API Coverage:
    • Invoices
    • Estimates
    • Customers
    • Payments
    • Accounts
    • CompanyInfo
    • Bills
    • Preferences
    • Credit Memos
  • Entity Management: Full CRUD operations with class-based entity methods:
    • Create, update, and save entities
    • Send entities via email (Invoice, Estimate, CreditMemo)
    • Download entities as PDF (Invoice, Estimate, CreditMemo, Bill, Payment)
    • Void transactions (Invoice, CreditMemo, Payment)
    • Soft delete entities (Account, Customer)
  • Type-Safe API: Full TypeScript declarations for all requests and responses.
  • Pagination: Automatic handling of paginated responses.
  • Environment Support: Supports both Production and Sandbox environments.
  • Bun & Node.js Compatible: Optimized builds for both runtimes.

Installation

bun add quickbooks-api
# or
npm install quickbooks-api

Getting Started

1. Initialize the Auth Provider

import { AuthProvider, Environment, AuthScopes } from 'quickbooks-api';

const authProvider = new AuthProvider('YOUR_CLIENT_ID', 'YOUR_CLIENT_SECRET', 'YOUR_REDIRECT_URI', [
  AuthScopes.Accounting,
  AuthScopes.OpenId,
]);

2. Generate the Authorization URL

const authUrl = authProvider.generateAuthUrl();
// Redirect user to authUrl.toString()

3. Handle the Callback

import type { UserAuthResponse } from 'quickbooks-api';

async function handleCallback(query: UserAuthResponse) {
  try {
    const token = await authProvider.exchangeCode(query.code, query.realmId);
    console.log('Access Token:', token.accessToken);
  } catch (error) {
    console.error('Authentication failed:', error);
  }
}

4. Initialize the API Client

import { ApiClient, Environment } from 'quickbooks-api';

const apiClient = new ApiClient(authProvider, Environment.Sandbox);

Single Sign-On (SSO) Support

The SDK includes built-in support for QuickBooks Single Sign-On using OpenID Connect, making it incredibly simple to authenticate users with their Intuit credentials.

Enable SSO

Simply include the openid scope (and optionally profile, email, phone, address) when initializing the AuthProvider:

import { AuthProvider, AuthScopes } from 'quickbooks-api';

const authProvider = new AuthProvider('YOUR_CLIENT_ID', 'YOUR_CLIENT_SECRET', 'YOUR_REDIRECT_URI', [
  AuthScopes.Accounting,
  AuthScopes.OpenId, // Required for SSO
  AuthScopes.Profile, // Optional: Get user profile info
  AuthScopes.Email, // Optional: Get user email
  AuthScopes.Phone, // Optional: Get user phone
  AuthScopes.Address, // Optional: Get user address
]);

Automatic SSO Handling

When you exchange the authorization code, the SDK automatically:

  • Decodes and validates the ID token
  • Fetches the user profile (if profile scope is included)
  • Stores everything in the token for easy access
// Exchange code - SSO is handled automatically!
const token = await authProvider.exchangeCode(code, realmId);

// Access user profile
if (authProvider.isSsoEnabled()) {
  const userProfile = await authProvider.getCurrentUserProfile();
  console.log('User:', userProfile?.name);
  console.log('Email:', userProfile?.email);
  console.log('Email Verified:', userProfile?.emailVerified);
}

// Access ID token claims
if (token.idToken) {
  console.log('User ID:', token.idToken.claims.sub);
  console.log('Issuer:', token.idToken.claims.iss);
}

Manual User Profile Retrieval

You can also manually fetch the user profile at any time:

const userProfile = await authProvider.getUserProfile();
console.log('User Profile:', userProfile);

ID Token Validation

The SDK automatically validates ID tokens, but you can also validate them manually:

// Decode and validate an ID token
const idToken = await authProvider.decodeIdToken(idTokenString);
const isValid = await authProvider.validateIdToken(idToken, nonce);

Check SSO Status

if (authProvider.isSsoEnabled()) {
  console.log('SSO is enabled');
}

That's it! SSO is fully integrated and works seamlessly with the existing OAuth flow.

5. Make API Calls (with Invoice Options)

import { ApiClient, Environment, InvoiceStatus, InvoiceOptions } from 'quickbooks-api';

// Example: Get all invoices (with search options and pagination)
let hasNextPage = true;
let page = 1;
const paginatedInvoices = [];

while (hasNextPage) {
  // Setup the Invoice
  const invoiceOptions: InvoiceOptions = {
    searchOptions: {
      maxResults: 10,
      page: page,
      orderBy: { field: 'Id', direction: 'DESC' },
    },
  };
  // Get the Invoices
  const searchResponse = await apiClient.invoices.getAllInvoices(invoiceOptions);

  // Add the Invoices to the List
  paginatedInvoices.push(...searchResponse.results);

  // Check if there is a next page
  hasNextPage = searchResponse.hasNextPage;

  // Log the Intuit Transaction ID for tracking
  console.log('Transaction ID:', searchResponse.intuitTID);

  // Increment the Page
  page++;
}

// Get the list of Paid Invoice
const paidInvoices = await apiClient.invoices.getAllInvoices({ status: InvoiceStatus.Paid });
console.log('Transaction ID:', paidInvoices.intuitTID);

Error Handling

The SDK uses a custom QuickbooksError class that extends the standard Error class and provides structured access to Intuit API error details.

QuickbooksError Structure

import { QuickbooksError } from 'quickbooks-api';

try {
  const { invoice, intuitTID } = await apiClient.invoices.getInvoiceById('invalid-id');
  if (invoice) {
    console.log('Invoice ID:', invoice.Id);
    console.log('Transaction ID:', intuitTID);
  }
} catch (error) {
  if (error instanceof QuickbooksError) {
    // Access the error message
    console.error('Error:', error.message);

    // Access structured error details
    console.error('Status Code:', error.details.statusCode);
    console.error('Transaction ID:', error.details.intuitTID);

    // Access Intuit-specific error information
    error.details.intuitError.forEach((err) => {
      console.error('Error Code:', err.code);
      console.error('Error Message:', err.message);
      console.error('Error Detail:', err.detail);
    });
  }
}

Error Details

The QuickbooksError includes the following properties:

  • message: string - Human-readable error message
  • details.statusCode: number - HTTP status code from the API response
  • details.intuitError: Array<{message: string, detail: string, code: string}> - Array of Intuit API error objects
  • details.intuitTID: string - Intuit transaction ID for support tracking

Example: Handling API Errors

import { ApiClient, QuickbooksError } from 'quickbooks-api';

async function fetchAccount(accountId: string) {
  try {
    const { account, intuitTID } = await apiClient.accounts.getAccountById(accountId);
    if (account) {
      console.log('Account retrieved:', account.Name);
      console.log('Transaction ID:', intuitTID);
      return account;
    }
    return null;
  } catch (error) {
    if (error instanceof QuickbooksError) {
      // Handle specific error cases
      if (error.details.statusCode === 404) {
        console.error('Account not found');
      } else if (error.details.statusCode === 401) {
        console.error('Authentication failed - token may be expired');
      } else {
        // Log full error details for debugging
        console.error('QuickBooks API Error:', {
          message: error.message,
          statusCode: error.details.statusCode,
          intuitErrors: error.details.intuitError,
          transactionId: error.details.intuitTID,
        });
      }
    } else {
      // Handle unexpected errors
      console.error('Unexpected error:', error);
    }
    throw error;
  }
}

API Response Structure

All API methods now include the Intuit Transaction ID (intuitTID) in their responses for better request tracking and support debugging.

Search/List Methods

Methods that return multiple results (like getAllInvoices(), getAllAccounts(), etc.) return a SearchResponse object:

interface SearchResponse<T> {
  results: Array<T>;
  hasNextPage?: boolean;
  intuitTID: string; // Intuit Transaction ID
}

Example:

const response = await apiClient.invoices.getAllInvoices();
console.log('Invoices:', response.results);
console.log('Has next page:', response.hasNextPage);
console.log('Transaction ID:', response.intuitTID);

Single Item Methods

Methods that return a single item (like getInvoiceById(), getAccountById(), etc.) return an object with the item and intuitTID:

// For most entities
const { invoice, intuitTID } = await apiClient.invoices.getInvoiceById('123');
if (invoice) {
  console.log('Invoice ID:', invoice.Id);
  console.log('Transaction ID:', intuitTID);
}

// For Company Info
const { companyInfo, intuitTID } = await apiClient.companyInfo.getCompanyInfo();
console.log('Company Name:', companyInfo.CompanyName);
console.log('Transaction ID:', intuitTID);

Affected Methods:

  • getAccountById()
  • getBillById()
  • getCreditMemoById()
  • getCustomerById()
  • getEstimateById()
  • getInvoiceById()
  • getPaymentById()
  • getCompanyInfo()
  • rawCompanyInfoQuery()

Using Transaction IDs

The intuitTID is useful for:

  • Support Requests: Provide the transaction ID when contacting Intuit support
  • Debugging: Track specific API requests in logs
  • Audit Trails: Maintain records of API interactions

Entity Management

All entity classes provide instance methods for managing QuickBooks entities. After retrieving an entity using get*ById() methods, you can perform various operations on the entity instance.

Saving Entities

The save() method handles both creating new entities and updating existing ones:

// Get an invoice
const { invoice, intuitTID } = await apiClient.invoices.getInvoiceById('123');
if (invoice) {
  // Modify the invoice
  invoice.PrivateNote = 'Updated note';

  // Save changes (update)
  await invoice.save();

  // Or create a new invoice
  const newInvoice = new Invoice(apiClient, {
    Line: [...],
    CustomerRef: { value: '456' },
    // ... other required fields
  });
  await newInvoice.save(); // Creates new invoice
}

Sending Entities via Email

Send invoices, estimates, and credit memos directly via email:

// Send an invoice via email
const { invoice } = await apiClient.invoices.getInvoiceById('123');
if (invoice) {
  await invoice.send();
  console.log('Invoice sent successfully');
}

// Send an estimate
const { estimate } = await apiClient.estimates.getEstimateById('456');
if (estimate) {
  await estimate.send();
  console.log('Estimate sent successfully');
}

// Send a credit memo
const { creditMemo } = await apiClient.creditMemos.getCreditMemoById('789');
if (creditMemo) {
  await creditMemo.send();
  console.log('Credit memo sent successfully');
}

Downloading PDFs

Download invoices, estimates, credit memos, bills, and payments as PDF files:

// Download invoice PDF
const { invoice } = await apiClient.invoices.getInvoiceById('123');
if (invoice) {
  const pdfBlob = await invoice.downloadPDF();

  // Save to file (Node.js example)
  const fs = require('fs').promises;
  await fs.writeFile('invoice.pdf', Buffer.from(await pdfBlob.arrayBuffer()));

  // Or use in browser
  const url = URL.createObjectURL(pdfBlob);
  const link = document.createElement('a');
  link.href = url;
  link.download = 'invoice.pdf';
  link.click();
}

// Download estimate PDF
const { estimate } = await apiClient.estimates.getEstimateById('456');
if (estimate) {
  const pdfBlob = await estimate.downloadPDF();
  // Handle PDF blob...
}

// Download credit memo, bill, or payment PDF
const { creditMemo } = await apiClient.creditMemos.getCreditMemoById('789');
if (creditMemo) {
  const pdfBlob = await creditMemo.downloadPDF();
  // Handle PDF blob...
}

Voiding Transactions

Void invoices, credit memos, and payments:

// Void an invoice
const { invoice } = await apiClient.invoices.getInvoiceById('123');
if (invoice && invoice.Balance > 0) {
  await invoice.void();
  console.log('Invoice voided successfully');
}

// Void a credit memo
const { creditMemo } = await apiClient.creditMemos.getCreditMemoById('456');
if (creditMemo && creditMemo.Balance > 0) {
  await creditMemo.void();
  console.log('Credit memo voided successfully');
}

// Void a payment
const { payment } = await apiClient.payments.getPaymentById('789');
if (payment && payment.TotalAmt > 0) {
  await payment.void();
  console.log('Payment voided successfully');
}

Soft Deleting Entities

Soft delete accounts and customers by setting Active=false:

// Soft delete an account
const { account } = await apiClient.accounts.getAccountById('123');
if (account && account.Active) {
  await account.delete(); // Sets Active=false
  console.log('Account deactivated');

  // Reactivate if needed
  account.Active = true;
  await account.save();
}

// Soft delete a customer
const { customer } = await apiClient.customers.getCustomerById('456');
if (customer && customer.Active) {
  await customer.delete(); // Sets Active=false
  console.log('Customer deactivated');

  // Reactivate if needed
  customer.Active = true;
  await customer.save();
}

Reloading Entity Data

Refresh entity data from the API:

// Reload invoice data
const { invoice } = await apiClient.invoices.getInvoiceById('123');
if (invoice) {
  // Make local changes
  invoice.PrivateNote = 'Local change';

  // Reload from API to get latest data
  await invoice.reload();
  console.log('Invoice data refreshed');
}

Error Handling with Entity Methods

All entity methods throw QuickbooksError with detailed information:

try {
  const { invoice } = await apiClient.invoices.getInvoiceById('123');
  if (invoice) {
    // This will throw if invoice doesn't have an ID
    await invoice.downloadPDF();
  }
} catch (error) {
  if (error instanceof QuickbooksError) {
    console.error('PDF download failed:', error.message);
    console.error('Error details:', error.details);
  }
}

Token Management

The AuthProvider handles token refresh, revocation, and secure storage. Use the following methods:

  • refresh(): Refreshes the access token.
  • revoke(): Revokes the access token.
  • serializeToken(secretKey: string): Securely encrypts and serializes the token for storage.
  • deserializeToken(serialized: string, secretKey: string): Decrypts and restores the serialized token.

Important: Store tokens securely using serializeToken and deserializeToken.


API Reference

The ApiClient provides access to the following APIs:

  • apiClient.invoices: Invoices API
  • apiClient.estimates: Estimates API
  • apiClient.customers: Customers API
  • apiClient.payments: Payments API
  • apiClient.accounts: Accounts API
  • apiClient.preferences: Preferences API
  • apiClient.creditMemos: Credit Memos API
  • apiClient.companyInfo: Company Info API
  • apiClient.bills: Bills API

Refer to the individual API documentation for available methods and options.


Support

Join our Discord server for community support and discussions.


Security Considerations

  • Secure Token Storage: Never store tokens in client-side storage. Use secure server-side storage and the provided serializeToken and deserializeToken methods.
  • Strong Secret Key: Use a strong, randomly generated secret key (minimum 32 characters) for token serialization. Rotate the key regularly.
  • HTTPS: Always use HTTPS in production environments.

Contributing

See CONTRIBUTING.md


Legal Disclaimer

This project is not affiliated with, endorsed by, or in any way officially connected with Intuit Inc., QuickBooks®, or any related subsidiaries. All trademarks and registered trademarks are the property of their respective owners.

QuickBooks® is a registered trademark of Intuit Inc., registered in the United States and other countries.