Earlier this week, we introduced Continuous Accounting to the Modern Treasury platform. Our first integration is to QuickBooks and enables our customers to seamlessly keep their general ledger in sync with their payments.
In this Journal Entry, we’re going to give a behind the scenes look into how we built this integration using LedgerSync, which is an open-source library for accounting systems. We've been working with others over the past months to build this library, and are introducing in the hopes that it takes on a life of its own.
Our first integration is to QuickBooks, but we are building integrations with other accounting systems like Xero, NetSuite, and Workday over time. Although these systems have different APIs, the underlying concepts in them are the same. An invoice is an invoice. A vendor is a vendor.
Yet, as engineers building a scalable system, we don’t want to alter our code for every new integration we build. Once we have a function in our application that marks as an invoice as paid, for example, we want that to work for all accounting systems.
We are excited to share LedgerSync, an open-source library for any accounting system. LedgerSync provides a universal interface for accounting systems and abstracts away the implementation details behind their underlying APIs.
The library’s functionality can be broken down into two categories:
- Handling authentication and authorization
- Create-read-update-delete (CRUD) actions on API resources
To illustrate an example, the following code creates a customer named “Jane Doe” in QuickBooks:
adaptor = LedgerSync::Adaptors::QuickBooksOnline::Adaptor.new(
resource = LedgerSync::Customer.new(
name: "Jane Doe",
operation = LedgerSync::Adaptors::QuickBooksOnline::Customer::Operations::Create.new(
The library separates the concerns of representing a resource (LedgerSync::Customer) from the ledger where the data is synced (LedgerSync::Adaptors::QuickBooksOnline::Adaptor). This means that developers can interact with multiple ledgers but represent their data consistently across them.
Another common use case is to pull data from a ledger. If you wanted to pull a list of all the customers and their attributes from QuickBooks, you would use a Searcher.
searcher = LedgerSync::Adaptors::QuickBooksOnline::Customer::Searcher.new(
# Using the same adaptor as the prior example
result = searcher.search
result.resources.map do |resource|
# resource is a LedgerSync::Customer
We are using LedgerSync for our application so that our system is future-proof. Undoubtedly, there will be differences between accounting systems and we will have to build around them. For example, QuickBooks has a concept to represent bank-to-bank transfers that we use. But in NetSuite, a bank-to-bank transfer is represented as a journal entry debiting one account and crediting the other.
Our Commitment to Open Source
We decided to build LedgerSync as an open source library.
We did this because we know accounting integrations can be frustrating to build, and developers deserve to focus on their core product or project. We also hope that others will contribute to the work we’ve started as they add additional functionality or accounting systems beyond where we started.
The library is written in Ruby, as that is the programming language we primarily use at Modern Treasury. The gem is available as open source under the terms of the MIT License.
We would like to thank Ryan Jackson and Jozef Vaclavik, who built the LedgerSync library. To learn more about LedgerSync, visit https://ledgersync.dev. If you are interested in contributing or have any questions, please reach out to us via a GitHub issue.
If you’d like to learn more about Continuous Accounting with Modern Treasury, please send us a note.